winecoreaudio: Eliminate unnecessary variable, unreachable code, unused retval.
[wine.git] / dlls / winmm / winecoreaudio / audio.c
blob265b9af2fe9500510f4b52b1c75f6f9eb5940b27
1 /*
2 * Wine Driver for CoreAudio based on Jack Driver
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1999 Eric Pouech (async playing in waveOut/waveIn)
6 * Copyright 2000 Eric Pouech (loops in waveOut)
7 * Copyright 2002 Chris Morgan (jack version of this file)
8 * Copyright 2005, 2006 Emmanuel Maillard
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <fcntl.h>
35 #ifdef HAVE_PTHREAD_H
36 # include <pthread.h>
37 #endif
39 #include "windef.h"
40 #include "winbase.h"
41 #include "winnls.h"
42 #include "wingdi.h"
43 #include "winerror.h"
44 #include "mmddk.h"
45 #include "dsound.h"
46 #include "dsdriver.h"
47 #include "coreaudio.h"
48 #include "wine/unicode.h"
49 #include "wine/library.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(wave);
55 #if defined(HAVE_COREAUDIO_COREAUDIO_H) && defined(HAVE_AUDIOUNIT_AUDIOUNIT_H)
56 #include <CoreAudio/CoreAudio.h>
57 #include <CoreFoundation/CoreFoundation.h>
60 Due to AudioUnit headers conflict define some needed types.
63 typedef void *AudioUnit;
65 /* From AudioUnit/AUComponents.h */
66 typedef UInt32 AudioUnitRenderActionFlags;
68 /* only allow 10 output devices through this driver, this ought to be adequate */
69 #define MAX_WAVEOUTDRV (1)
70 #define MAX_WAVEINDRV (1)
72 /* state diagram for waveOut writing:
74 * +---------+-------------+---------------+---------------------------------+
75 * | state | function | event | new state |
76 * +---------+-------------+---------------+---------------------------------+
77 * | | open() | | STOPPED |
78 * | PAUSED | write() | | PAUSED |
79 * | STOPPED | write() | <thrd create> | PLAYING |
80 * | PLAYING | write() | HEADER | PLAYING |
81 * | (other) | write() | <error> | |
82 * | (any) | pause() | PAUSING | PAUSED |
83 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
84 * | (any) | reset() | RESETTING | STOPPED |
85 * | (any) | close() | CLOSING | CLOSED |
86 * +---------+-------------+---------------+---------------------------------+
89 /* states of the playing device */
90 #define WINE_WS_PLAYING 0
91 #define WINE_WS_PAUSED 1
92 #define WINE_WS_STOPPED 2
93 #define WINE_WS_CLOSED 3
95 typedef struct tagCoreAudio_Device {
96 char dev_name[32];
97 char mixer_name[32];
98 unsigned open_count;
99 char* interface_name;
101 WAVEOUTCAPSW out_caps;
102 WAVEINCAPSW in_caps;
103 DWORD in_caps_support;
104 int sample_rate;
105 int stereo;
106 int format;
107 unsigned audio_fragment;
108 BOOL full_duplex;
109 BOOL bTriggerSupport;
110 BOOL bOutputEnabled;
111 BOOL bInputEnabled;
112 DSDRIVERDESC ds_desc;
113 DSDRIVERCAPS ds_caps;
114 DSCDRIVERCAPS dsc_caps;
115 GUID ds_guid;
116 GUID dsc_guid;
118 AudioDeviceID outputDeviceID;
119 AudioDeviceID inputDeviceID;
120 AudioStreamBasicDescription streamDescription;
121 } CoreAudio_Device;
123 /* for now use the default device */
124 static CoreAudio_Device CoreAudio_DefaultDevice;
126 typedef struct {
127 volatile int state; /* one of the WINE_WS_ manifest constants */
128 CoreAudio_Device *cadev;
129 WAVEOPENDESC waveDesc;
130 WORD wFlags;
131 PCMWAVEFORMAT format;
132 DWORD woID;
133 AudioUnit audioUnit;
134 AudioStreamBasicDescription streamDescription;
136 WAVEOUTCAPSW caps;
137 char interface_name[32];
138 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
139 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
140 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
142 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
143 DWORD dwLoops; /* private copy of loop counter */
145 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
146 DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */
148 DWORD tickCountMS; /* time in MS of last AudioUnit callback */
150 pthread_mutex_t lock; /* synchronization stuff */
152 BOOL trace_on;
153 BOOL warn_on;
154 BOOL err_on;
155 } WINE_WAVEOUT;
157 typedef struct {
158 volatile int state;
159 CoreAudio_Device *cadev;
160 WAVEOPENDESC waveDesc;
161 WORD wFlags;
162 PCMWAVEFORMAT format;
163 LPWAVEHDR lpQueuePtr;
164 DWORD dwTotalRecorded;
165 WAVEINCAPSW caps;
167 AudioUnit audioUnit;
168 AudioStreamBasicDescription streamDescription;
170 /* BOOL bTriggerSupport;
171 WORD wDevID;
172 char interface_name[32];*/
174 /* synchronization stuff */
175 pthread_mutex_t lock;
176 } WINE_WAVEIN;
178 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
180 static CFMessagePortRef Port_SendToMessageThread;
182 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
183 static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
185 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
186 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
187 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
189 extern OSStatus AudioOutputUnitStart(AudioUnit au);
190 extern OSStatus AudioOutputUnitStop(AudioUnit au);
191 extern OSStatus AudioUnitUninitialize(AudioUnit au);
193 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
194 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
196 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
197 AudioUnitRenderActionFlags *ioActionFlags,
198 const AudioTimeStamp *inTimeStamp,
199 UInt32 inBusNumber,
200 UInt32 inNumberFrames,
201 AudioBufferList *ioData);
203 /* These strings used only for tracing */
205 static const char * getMessage(UINT msg)
207 static char unknown[32];
208 #define MSG_TO_STR(x) case x: return #x
209 switch(msg) {
210 MSG_TO_STR(DRVM_INIT);
211 MSG_TO_STR(DRVM_EXIT);
212 MSG_TO_STR(DRVM_ENABLE);
213 MSG_TO_STR(DRVM_DISABLE);
214 MSG_TO_STR(WIDM_OPEN);
215 MSG_TO_STR(WIDM_CLOSE);
216 MSG_TO_STR(WIDM_ADDBUFFER);
217 MSG_TO_STR(WIDM_PREPARE);
218 MSG_TO_STR(WIDM_UNPREPARE);
219 MSG_TO_STR(WIDM_GETDEVCAPS);
220 MSG_TO_STR(WIDM_GETNUMDEVS);
221 MSG_TO_STR(WIDM_GETPOS);
222 MSG_TO_STR(WIDM_RESET);
223 MSG_TO_STR(WIDM_START);
224 MSG_TO_STR(WIDM_STOP);
225 MSG_TO_STR(WODM_OPEN);
226 MSG_TO_STR(WODM_CLOSE);
227 MSG_TO_STR(WODM_WRITE);
228 MSG_TO_STR(WODM_PAUSE);
229 MSG_TO_STR(WODM_GETPOS);
230 MSG_TO_STR(WODM_BREAKLOOP);
231 MSG_TO_STR(WODM_PREPARE);
232 MSG_TO_STR(WODM_UNPREPARE);
233 MSG_TO_STR(WODM_GETDEVCAPS);
234 MSG_TO_STR(WODM_GETNUMDEVS);
235 MSG_TO_STR(WODM_GETPITCH);
236 MSG_TO_STR(WODM_SETPITCH);
237 MSG_TO_STR(WODM_GETPLAYBACKRATE);
238 MSG_TO_STR(WODM_SETPLAYBACKRATE);
239 MSG_TO_STR(WODM_GETVOLUME);
240 MSG_TO_STR(WODM_SETVOLUME);
241 MSG_TO_STR(WODM_RESTART);
242 MSG_TO_STR(WODM_RESET);
243 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
244 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
245 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
246 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
248 #undef MSG_TO_STR
249 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
250 return unknown;
253 #define kStopLoopMessage 0
254 #define kWaveOutCallbackMessage 1
255 #define kWaveInCallbackMessage 2
257 /* Mach Message Handling */
258 static CFDataRef wodMessageHandler(CFMessagePortRef port_ReceiveInMessageThread, SInt32 msgid, CFDataRef data, void *info)
260 UInt32 *buffer = NULL;
261 WINE_WAVEOUT* wwo = NULL;
263 switch (msgid)
265 case kWaveOutCallbackMessage:
266 buffer = (UInt32 *) CFDataGetBytePtr(data);
267 wwo = &WOutDev[buffer[0]];
269 pthread_mutex_lock(&wwo->lock);
270 DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
271 (HDRVR)wwo->waveDesc.hWave, (WORD)buffer[1], wwo->waveDesc.dwInstance,
272 (DWORD)buffer[2], (DWORD)buffer[3]);
273 pthread_mutex_unlock(&wwo->lock);
274 break;
275 case kWaveInCallbackMessage:
276 default:
277 CFRunLoopStop(CFRunLoopGetCurrent());
278 break;
281 return NULL;
284 static DWORD WINAPI messageThread(LPVOID p)
286 CFMessagePortRef port_ReceiveInMessageThread = (CFMessagePortRef) p;
287 CFRunLoopSourceRef source;
289 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port_ReceiveInMessageThread, (CFIndex)0);
290 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
292 CFRunLoopRun();
294 CFRunLoopSourceInvalidate(source);
295 CFRelease(source);
296 CFRelease(port_ReceiveInMessageThread);
298 return 0;
301 static DWORD wodSendDriverCallbackMessage(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
303 CFDataRef data;
304 UInt32 buffer[4];
305 SInt32 ret;
307 buffer[0] = (UInt32) wwo->woID;
308 buffer[1] = (UInt32) wMsg;
309 buffer[2] = (UInt32) dwParam1;
310 buffer[3] = (UInt32) dwParam2;
312 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)buffer, sizeof(buffer));
313 if (!data)
314 return 0;
316 ret = CFMessagePortSendRequest(Port_SendToMessageThread, kWaveOutCallbackMessage, data, 0.0, 0.0, NULL, NULL);
317 CFRelease(data);
319 return (ret == kCFMessagePortSuccess)?1:0;
322 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
323 PCMWAVEFORMAT* format)
325 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
326 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
327 format->wf.nChannels, format->wf.nAvgBytesPerSec);
328 TRACE("Position in bytes=%u\n", position);
330 switch (lpTime->wType) {
331 case TIME_SAMPLES:
332 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
333 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
334 break;
335 case TIME_MS:
336 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
337 TRACE("TIME_MS=%u\n", lpTime->u.ms);
338 break;
339 case TIME_SMPTE:
340 lpTime->u.smpte.fps = 30;
341 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
342 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
343 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
344 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
345 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
346 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
347 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
348 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
349 lpTime->u.smpte.fps = 30;
350 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
351 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
352 lpTime->u.smpte.hour, lpTime->u.smpte.min,
353 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
354 break;
355 default:
356 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
357 lpTime->wType = TIME_BYTES;
358 /* fall through */
359 case TIME_BYTES:
360 lpTime->u.cb = position;
361 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
362 break;
364 return MMSYSERR_NOERROR;
367 /**************************************************************************
368 * CoreAudio_GetDevCaps [internal]
370 BOOL CoreAudio_GetDevCaps (void)
372 OSStatus status;
373 UInt32 propertySize;
374 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
376 char name[MAXPNAMELEN];
378 propertySize = MAXPNAMELEN;
379 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
380 if (status) {
381 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
382 (char) (status >> 16),
383 (char) (status >> 8),
384 (char) status);
385 return FALSE;
388 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
389 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
390 MultiByteToWideChar(CP_ACP, 0, name, sizeof(name),
391 CoreAudio_DefaultDevice.out_caps.szPname,
392 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
393 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
395 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
396 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
397 if (status != noErr) {
398 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
399 (char) (status >> 16),
400 (char) (status >> 8),
401 (char) status);
402 return FALSE;
405 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
406 "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
407 "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
408 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
409 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
410 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
411 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
412 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
413 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
414 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
415 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
416 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
417 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
418 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
420 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
421 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
423 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
424 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
425 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
426 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
427 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
429 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
430 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
432 return TRUE;
435 /******************************************************************
436 * CoreAudio_WaveInit
438 * Initialize CoreAudio_DefaultDevice
440 LONG CoreAudio_WaveInit(void)
442 OSStatus status;
443 UInt32 propertySize;
444 CHAR szPname[MAXPNAMELEN];
445 pthread_mutexattr_t mutexattr;
446 int i;
447 HANDLE hThread;
448 CFStringRef messageThreadPortName;
449 CFMessagePortRef port_ReceiveInMessageThread;
451 TRACE("()\n");
453 /* number of sound cards */
454 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
455 propertySize /= sizeof(AudioDeviceID);
456 TRACE("sound cards : %lu\n", propertySize);
458 /* Get the output device */
459 propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
460 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
461 if (status) {
462 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
463 (char) (status >> 16),
464 (char) (status >> 8),
465 (char) status);
466 return 1;
468 if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
469 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
470 return 1;
473 if ( ! CoreAudio_GetDevCaps() )
474 return 1;
476 CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
477 sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name);
479 pthread_mutexattr_init(&mutexattr);
480 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
482 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
484 WOutDev[i].state = WINE_WS_CLOSED;
485 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
486 WOutDev[i].woID = i;
488 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
490 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
491 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
492 snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
493 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
494 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
496 WOutDev[i].caps.vDriverVersion = 0x0001;
497 WOutDev[i].caps.dwFormats = 0x00000000;
498 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
500 WOutDev[i].caps.wChannels = 2;
501 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
503 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
504 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
505 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
506 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
507 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
508 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
509 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
510 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
511 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
512 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
513 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
514 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
516 pthread_mutex_init(&WOutDev[i].lock, &mutexattr); /* initialize the mutex */
519 pthread_mutexattr_destroy(&mutexattr);
521 /* create mach messages handler */
522 srandomdev();
523 messageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
524 CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
525 if (!messageThreadPortName)
527 ERR("Can't create message thread port name\n");
528 return 1;
531 port_ReceiveInMessageThread = CFMessagePortCreateLocal(kCFAllocatorDefault, messageThreadPortName,
532 &wodMessageHandler, NULL, NULL);
533 if (!port_ReceiveInMessageThread)
535 ERR("Can't create message thread local port\n");
536 CFRelease(messageThreadPortName);
537 return 1;
540 Port_SendToMessageThread = CFMessagePortCreateRemote(kCFAllocatorDefault, messageThreadPortName);
541 CFRelease(messageThreadPortName);
542 if (!Port_SendToMessageThread)
544 ERR("Can't create port for sending to message thread\n");
545 CFRelease(port_ReceiveInMessageThread);
546 return 1;
549 hThread = CreateThread(NULL, 0, messageThread, (LPVOID)port_ReceiveInMessageThread, 0, NULL);
550 if ( !hThread )
552 ERR("Can't create message thread\n");
553 CFRelease(port_ReceiveInMessageThread);
554 CFRelease(Port_SendToMessageThread);
555 Port_SendToMessageThread = NULL;
556 return 1;
559 /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
561 return 0;
564 void CoreAudio_WaveRelease(void)
566 /* Stop CFRunLoop in messageThread */
567 int i;
569 TRACE("()\n");
571 CFMessagePortSendRequest(Port_SendToMessageThread, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
572 CFRelease(Port_SendToMessageThread);
573 Port_SendToMessageThread = NULL;
575 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
577 pthread_mutex_destroy(&WOutDev[i].lock);
581 /*======================================================================*
582 * Low level WAVE OUT implementation *
583 *======================================================================*/
585 /**************************************************************************
586 * wodNotifyClient [internal]
587 * Call from AudioUnit IO thread can't use Wine debug channels.
589 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
591 switch (wMsg) {
592 case WOM_OPEN:
593 case WOM_CLOSE:
594 if (wwo->wFlags != DCB_NULL &&
595 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
596 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
597 dwParam1, dwParam2))
599 return MMSYSERR_ERROR;
601 break;
602 case WOM_DONE:
603 if (wwo->wFlags != DCB_NULL &&
604 ! wodSendDriverCallbackMessage(wwo, wMsg, dwParam1, dwParam2))
606 return MMSYSERR_ERROR;
608 break;
609 default:
610 return MMSYSERR_INVALPARAM;
612 return MMSYSERR_NOERROR;
616 /**************************************************************************
617 * wodGetDevCaps [internal]
619 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
621 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
623 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
625 if (wDevID >= MAX_WAVEOUTDRV)
627 TRACE("MAX_WAVOUTDRV reached !\n");
628 return MMSYSERR_BADDEVICEID;
631 TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
632 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
633 return MMSYSERR_NOERROR;
636 /**************************************************************************
637 * wodOpen [internal]
639 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
641 WINE_WAVEOUT* wwo;
642 DWORD retval;
643 DWORD ret;
644 AudioStreamBasicDescription streamFormat;
646 TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
647 if (lpDesc == NULL)
649 WARN("Invalid Parameter !\n");
650 return MMSYSERR_INVALPARAM;
652 if (wDevID >= MAX_WAVEOUTDRV) {
653 TRACE("MAX_WAVOUTDRV reached !\n");
654 return MMSYSERR_BADDEVICEID;
657 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
658 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
659 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
661 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
662 lpDesc->lpFormat->nChannels == 0 ||
663 lpDesc->lpFormat->nSamplesPerSec == 0
666 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
667 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
668 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
669 return WAVERR_BADFORMAT;
672 if (dwFlags & WAVE_FORMAT_QUERY)
674 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
675 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
676 lpDesc->lpFormat->nSamplesPerSec);
677 return MMSYSERR_NOERROR;
680 wwo = &WOutDev[wDevID];
681 pthread_mutex_lock(&wwo->lock);
683 if (wwo->state != WINE_WS_CLOSED)
685 pthread_mutex_unlock(&wwo->lock);
686 return MMSYSERR_ALLOCATED;
689 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
691 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
692 pthread_mutex_unlock(&wwo->lock);
693 return MMSYSERR_ERROR;
696 if ((dwFlags & WAVE_DIRECTSOUND) &&
697 !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
698 /* not supported, ignore it */
699 dwFlags &= ~WAVE_DIRECTSOUND;
701 streamFormat.mFormatID = kAudioFormatLinearPCM;
702 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
703 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
704 if (lpDesc->lpFormat->wBitsPerSample != 8)
705 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
706 # ifdef WORDS_BIGENDIAN
707 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
708 # endif
710 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
711 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
712 streamFormat.mFramesPerPacket = 1;
713 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
714 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
715 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
717 ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, &streamFormat);
718 if (!ret)
720 AudioUnit_CloseAudioUnit(wwo->audioUnit);
721 pthread_mutex_unlock(&wwo->lock);
722 return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
724 wwo->streamDescription = streamFormat;
725 wwo->state = WINE_WS_STOPPED;
727 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
729 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
730 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
732 if (wwo->format.wBitsPerSample == 0) {
733 WARN("Resetting zeroed wBitsPerSample\n");
734 wwo->format.wBitsPerSample = 8 *
735 (wwo->format.wf.nAvgBytesPerSec /
736 wwo->format.wf.nSamplesPerSec) /
737 wwo->format.wf.nChannels;
740 wwo->dwPlayedTotal = 0;
741 wwo->dwWrittenTotal = 0;
743 wwo->trace_on = TRACE_ON(wave);
744 wwo->warn_on = WARN_ON(wave);
745 wwo->err_on = ERR_ON(wave);
747 pthread_mutex_unlock(&wwo->lock);
749 retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
751 return retval;
754 /**************************************************************************
755 * wodClose [internal]
757 static DWORD wodClose(WORD wDevID)
759 DWORD ret = MMSYSERR_NOERROR;
760 WINE_WAVEOUT* wwo;
762 TRACE("(%u);\n", wDevID);
764 if (wDevID >= MAX_WAVEOUTDRV)
766 WARN("bad device ID !\n");
767 return MMSYSERR_BADDEVICEID;
770 wwo = &WOutDev[wDevID];
771 pthread_mutex_lock(&wwo->lock);
772 if (wwo->lpQueuePtr)
774 WARN("buffers still playing !\n");
775 pthread_mutex_unlock(&wwo->lock);
776 ret = WAVERR_STILLPLAYING;
777 } else
779 OSStatus err;
780 /* sanity check: this should not happen since the device must have been reset before */
781 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
783 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
785 err = AudioUnitUninitialize(wwo->audioUnit);
786 if (err) {
787 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
788 (char) (err >> 16),
789 (char) (err >> 8),
790 (char) err);
791 pthread_mutex_unlock(&wwo->lock);
792 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
795 if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
797 ERR("Can't close AudioUnit\n");
798 pthread_mutex_unlock(&wwo->lock);
799 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
801 pthread_mutex_unlock(&wwo->lock);
803 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
806 return ret;
809 /**************************************************************************
810 * wodPrepare [internal]
812 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
814 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
816 if (wDevID >= MAX_WAVEOUTDRV) {
817 WARN("bad device ID !\n");
818 return MMSYSERR_BADDEVICEID;
821 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
822 return WAVERR_STILLPLAYING;
824 lpWaveHdr->dwFlags |= WHDR_PREPARED;
825 lpWaveHdr->dwFlags &= ~WHDR_DONE;
827 return MMSYSERR_NOERROR;
830 /**************************************************************************
831 * wodUnprepare [internal]
833 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
835 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
837 if (wDevID >= MAX_WAVEOUTDRV) {
838 WARN("bad device ID !\n");
839 return MMSYSERR_BADDEVICEID;
842 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
843 return WAVERR_STILLPLAYING;
845 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
846 lpWaveHdr->dwFlags |= WHDR_DONE;
848 return MMSYSERR_NOERROR;
852 /**************************************************************************
853 * wodHelper_CheckForLoopBegin [internal]
855 * Check if the new waveheader is the beginning of a loop, and set up
856 * state if so.
857 * This is called with the WAVEOUT lock held.
858 * Call from AudioUnit IO thread can't use Wine debug channels.
860 static void wodHelper_CheckForLoopBegin(WINE_WAVEOUT* wwo)
862 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
864 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
866 if (wwo->lpLoopPtr)
868 if (wwo->warn_on)
869 fprintf(stderr, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
871 else
873 if (wwo->trace_on)
874 fprintf(stderr, "trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
876 wwo->lpLoopPtr = lpWaveHdr;
877 /* Windows does not touch WAVEHDR.dwLoops,
878 * so we need to make an internal copy */
879 wwo->dwLoops = lpWaveHdr->dwLoops;
885 /**************************************************************************
886 * wodHelper_PlayPtrNext [internal]
888 * Advance the play pointer to the next waveheader, looping if required.
889 * Call from AudioUnit IO thread can't use Wine debug channels.
891 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
893 BOOL didLoopBack = FALSE;
895 pthread_mutex_lock(&wwo->lock);
897 wwo->dwPartialOffset = 0;
898 if ((wwo->lpPlayPtr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
900 /* We're at the end of a loop, loop if required */
901 if (wwo->dwLoops > 1)
903 wwo->dwLoops--;
904 wwo->lpPlayPtr = wwo->lpLoopPtr;
905 didLoopBack = TRUE;
907 else
909 wwo->lpLoopPtr = NULL;
912 if (!didLoopBack)
914 /* We didn't loop back. Advance to the next wave header */
915 wwo->lpPlayPtr = wwo->lpPlayPtr->lpNext;
917 if (!wwo->lpPlayPtr)
919 OSStatus status;
920 wwo->state = WINE_WS_STOPPED;
921 status = AudioOutputUnitStop(wwo->audioUnit);
922 if (status && wwo->err_on)
923 fprintf(stderr, "err:winecoreaudio:wodHelper_PlayPtrNext AudioOutputUnitStop return %c%c%c%c\n",
924 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
926 else
927 wodHelper_CheckForLoopBegin(wwo);
930 pthread_mutex_unlock(&wwo->lock);
933 /* if force is TRUE then notify the client that all the headers were completed
934 * Call from AudioUnit IO thread can't use Wine debug channels.
936 static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
938 LPWAVEHDR lpWaveHdr;
939 DWORD retval;
941 pthread_mutex_lock(&wwo->lock);
943 /* Start from lpQueuePtr and keep notifying until:
944 * - we hit an unwritten wavehdr
945 * - we hit the beginning of a running loop
946 * - we hit a wavehdr which hasn't finished playing
948 while ((lpWaveHdr = wwo->lpQueuePtr) &&
949 (force ||
950 (lpWaveHdr != wwo->lpPlayPtr &&
951 lpWaveHdr != wwo->lpLoopPtr)))
953 wwo->lpQueuePtr = lpWaveHdr->lpNext;
955 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
956 lpWaveHdr->dwFlags |= WHDR_DONE;
958 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
961 retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr !=
962 wwo->lpLoopPtr) ? 0 : INFINITE;
964 pthread_mutex_unlock(&wwo->lock);
966 return retval;
969 /**************************************************************************
970 * wodHelper_Reset [internal]
972 * Resets current output stream.
974 static DWORD wodHelper_Reset(WINE_WAVEOUT* wwo, BOOL reset)
976 OSStatus status;
978 FIXME("\n");
980 /* updates current notify list */
981 /* if resetting, remove all wave headers and notify client that all headers were completed */
982 wodHelper_NotifyCompletions(wwo, reset);
984 pthread_mutex_lock(&wwo->lock);
986 if (reset)
988 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
989 wwo->state = WINE_WS_STOPPED;
990 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
992 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
994 else
996 if (wwo->lpLoopPtr)
998 /* complicated case, not handled yet (could imply modifying the loop counter) */
999 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
1000 wwo->lpPlayPtr = wwo->lpLoopPtr;
1001 wwo->dwPartialOffset = 0;
1002 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
1003 } else
1005 LPWAVEHDR ptr;
1006 DWORD sz = wwo->dwPartialOffset;
1008 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
1009 /* compute the max size playable from lpQueuePtr */
1010 for (ptr = wwo->lpQueuePtr; ptr && ptr != wwo->lpPlayPtr; ptr = ptr->lpNext)
1012 sz += ptr->dwBufferLength;
1015 /* because the reset lpPlayPtr will be lpQueuePtr */
1016 if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("doh\n");
1017 wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
1018 wwo->dwWrittenTotal = wwo->dwPlayedTotal;
1019 wwo->lpPlayPtr = wwo->lpQueuePtr;
1022 wwo->state = WINE_WS_PAUSED;
1025 status = AudioOutputUnitStop(wwo->audioUnit);
1027 pthread_mutex_unlock(&wwo->lock);
1029 if (status) {
1030 ERR( "AudioOutputUnitStop return %c%c%c%c\n",
1031 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1032 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1035 return MMSYSERR_NOERROR;
1039 /**************************************************************************
1040 * wodWrite [internal]
1043 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1045 LPWAVEHDR*wh;
1046 WINE_WAVEOUT *wwo;
1048 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1050 /* first, do the sanity checks... */
1051 if (wDevID >= MAX_WAVEOUTDRV)
1053 WARN("bad dev ID !\n");
1054 return MMSYSERR_BADDEVICEID;
1057 wwo = &WOutDev[wDevID];
1059 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1061 TRACE("unprepared\n");
1062 return WAVERR_UNPREPARED;
1065 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1067 TRACE("still playing\n");
1068 return WAVERR_STILLPLAYING;
1071 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1072 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1073 lpWaveHdr->lpNext = 0;
1075 pthread_mutex_lock(&wwo->lock);
1076 /* insert buffer at the end of queue */
1077 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1078 /* Do nothing */;
1079 *wh = lpWaveHdr;
1081 if (!wwo->lpPlayPtr)
1083 wwo->lpPlayPtr = lpWaveHdr;
1085 if (wwo->state == WINE_WS_STOPPED)
1087 OSStatus status = AudioOutputUnitStart(wwo->audioUnit);
1088 if (status) {
1089 ERR("AudioOutputUnitStart return %c%c%c%c\n", (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1091 else wwo->state = WINE_WS_PLAYING;
1094 wodHelper_CheckForLoopBegin(wwo);
1096 wwo->dwPartialOffset = 0;
1098 pthread_mutex_unlock(&wwo->lock);
1100 return MMSYSERR_NOERROR;
1103 /**************************************************************************
1104 * wodPause [internal]
1106 static DWORD wodPause(WORD wDevID)
1108 OSStatus status;
1110 TRACE("(%u);!\n", wDevID);
1112 if (wDevID >= MAX_WAVEOUTDRV)
1114 WARN("bad device ID !\n");
1115 return MMSYSERR_BADDEVICEID;
1118 pthread_mutex_lock(&WOutDev[wDevID].lock);
1119 if (WOutDev[wDevID].state == WINE_WS_PLAYING || WOutDev[wDevID].state == WINE_WS_STOPPED)
1121 WOutDev[wDevID].state = WINE_WS_PAUSED;
1122 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1123 if (status) {
1124 ERR( "AudioOutputUnitStop return %c%c%c%c\n",
1125 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1126 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1127 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1130 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1132 return MMSYSERR_NOERROR;
1135 /**************************************************************************
1136 * wodRestart [internal]
1138 static DWORD wodRestart(WORD wDevID)
1140 TRACE("(%u);\n", wDevID);
1142 if (wDevID >= MAX_WAVEOUTDRV )
1144 WARN("bad device ID !\n");
1145 return MMSYSERR_BADDEVICEID;
1148 pthread_mutex_lock(&WOutDev[wDevID].lock);
1149 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1151 if (WOutDev[wDevID].lpPlayPtr)
1153 OSStatus status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1154 if (status) {
1155 ERR("AudioOutputUnitStart return %c%c%c%c\n",
1156 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1157 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1158 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1160 WOutDev[wDevID].state = WINE_WS_PLAYING;
1162 else
1163 WOutDev[wDevID].state = WINE_WS_STOPPED;
1165 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1167 return MMSYSERR_NOERROR;
1170 /**************************************************************************
1171 * wodReset [internal]
1173 static DWORD wodReset(WORD wDevID)
1175 TRACE("(%u);\n", wDevID);
1177 if (wDevID >= MAX_WAVEOUTDRV)
1179 WARN("bad device ID !\n");
1180 return MMSYSERR_BADDEVICEID;
1183 return wodHelper_Reset(&WOutDev[wDevID], TRUE);
1186 /**************************************************************************
1187 * wodGetPosition [internal]
1189 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1191 DWORD val;
1192 WINE_WAVEOUT* wwo;
1194 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1196 if (wDevID >= MAX_WAVEOUTDRV)
1198 WARN("bad device ID !\n");
1199 return MMSYSERR_BADDEVICEID;
1202 /* if null pointer to time structure return error */
1203 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1205 wwo = &WOutDev[wDevID];
1207 pthread_mutex_lock(&WOutDev[wDevID].lock);
1208 val = wwo->dwPlayedTotal;
1209 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1211 return bytes_to_mmtime(lpTime, val, &wwo->format);
1214 /**************************************************************************
1215 * wodGetVolume [internal]
1217 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1219 float left;
1220 float right;
1222 if (wDevID >= MAX_WAVEOUTDRV)
1224 WARN("bad device ID !\n");
1225 return MMSYSERR_BADDEVICEID;
1228 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1230 pthread_mutex_lock(&WOutDev[wDevID].lock);
1232 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1234 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1236 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1238 return MMSYSERR_NOERROR;
1241 /**************************************************************************
1242 * wodSetVolume [internal]
1244 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1246 float left;
1247 float right;
1249 if (wDevID >= MAX_WAVEOUTDRV)
1251 WARN("bad device ID !\n");
1252 return MMSYSERR_BADDEVICEID;
1255 left = LOWORD(dwParam) / 65535.0f;
1256 right = HIWORD(dwParam) / 65535.0f;
1258 TRACE("(%u, %08x);\n", wDevID, dwParam);
1260 pthread_mutex_lock(&WOutDev[wDevID].lock);
1262 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1264 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1266 return MMSYSERR_NOERROR;
1269 /**************************************************************************
1270 * wodGetNumDevs [internal]
1272 static DWORD wodGetNumDevs(void)
1274 TRACE("\n");
1275 return MAX_WAVEOUTDRV;
1278 /**************************************************************************
1279 * wodDevInterfaceSize [internal]
1281 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1283 TRACE("(%u, %p)\n", wDevID, dwParam1);
1285 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1286 NULL, 0 ) * sizeof(WCHAR);
1287 return MMSYSERR_NOERROR;
1290 /**************************************************************************
1291 * wodDevInterface [internal]
1293 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1295 TRACE("\n");
1296 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1297 NULL, 0 ) * sizeof(WCHAR))
1299 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1300 dwParam1, dwParam2 / sizeof(WCHAR));
1301 return MMSYSERR_NOERROR;
1303 return MMSYSERR_INVALPARAM;
1306 /**************************************************************************
1307 * wodMessage (WINEJACK.7)
1309 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1310 DWORD dwParam1, DWORD dwParam2)
1312 TRACE("(%u, %s, %08x, %08x, %08x);\n",
1313 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1315 switch (wMsg) {
1316 case DRVM_INIT:
1317 case DRVM_EXIT:
1318 case DRVM_ENABLE:
1319 case DRVM_DISABLE:
1321 /* FIXME: Pretend this is supported */
1322 return 0;
1323 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1324 case WODM_CLOSE: return wodClose(wDevID);
1325 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1326 case WODM_PAUSE: return wodPause(wDevID);
1327 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1328 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1329 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1330 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1332 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1333 case WODM_GETNUMDEVS: return wodGetNumDevs();
1335 case WODM_GETPITCH:
1336 case WODM_SETPITCH:
1337 case WODM_GETPLAYBACKRATE:
1338 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1339 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1340 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1341 case WODM_RESTART: return wodRestart(wDevID);
1342 case WODM_RESET: return wodReset(wDevID);
1344 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1345 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1346 case DRV_QUERYDSOUNDIFACE:
1347 case DRV_QUERYDSOUNDDESC:
1348 return MMSYSERR_NOTSUPPORTED;
1350 default:
1351 FIXME("unknown message %d!\n", wMsg);
1354 return MMSYSERR_NOTSUPPORTED;
1357 /*======================================================================*
1358 * Low level DSOUND implementation *
1359 *======================================================================*/
1361 typedef struct IDsDriverImpl IDsDriverImpl;
1362 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1364 struct IDsDriverImpl
1366 /* IUnknown fields */
1367 const IDsDriverVtbl *lpVtbl;
1368 DWORD ref;
1369 /* IDsDriverImpl fields */
1370 UINT wDevID;
1371 IDsDriverBufferImpl*primary;
1374 struct IDsDriverBufferImpl
1376 /* IUnknown fields */
1377 const IDsDriverBufferVtbl *lpVtbl;
1378 DWORD ref;
1379 /* IDsDriverBufferImpl fields */
1380 IDsDriverImpl* drv;
1381 DWORD buflen;
1386 CoreAudio IO threaded callback,
1387 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1389 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1390 AudioUnitRenderActionFlags *ioActionFlags,
1391 const AudioTimeStamp *inTimeStamp,
1392 UInt32 inBusNumber,
1393 UInt32 inNumberFrames,
1394 AudioBufferList *ioData)
1396 UInt32 buffer;
1397 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1398 int nextPtr = 0;
1399 int needNotify = 0;
1401 unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1402 unsigned int dataProvided = 0;
1404 while (dataNeeded > 0)
1406 pthread_mutex_lock(&wwo->lock);
1408 if (wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1410 unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1411 unsigned int toCopy;
1413 if (available >= dataNeeded)
1414 toCopy = dataNeeded;
1415 else
1416 toCopy = available;
1418 if (toCopy > 0)
1420 memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1421 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1422 wwo->dwPartialOffset += toCopy;
1423 wwo->dwPlayedTotal += toCopy;
1424 dataProvided += toCopy;
1425 dataNeeded -= toCopy;
1426 available -= toCopy;
1429 if (available == 0)
1430 nextPtr = 1;
1432 needNotify = 1;
1434 else
1436 memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1437 dataProvided += dataNeeded;
1438 dataNeeded = 0;
1441 pthread_mutex_unlock(&wwo->lock);
1443 if (nextPtr)
1445 wodHelper_PlayPtrNext(wwo);
1446 nextPtr = 0;
1450 /* We only fill buffer 0. Set any others that might be requested to 0. */
1451 for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1453 memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1456 if (needNotify) wodHelper_NotifyCompletions(wwo, FALSE);
1457 return noErr;
1459 #else
1461 /**************************************************************************
1462 * wodMessage (WINECOREAUDIO.7)
1464 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1465 DWORD dwParam1, DWORD dwParam2)
1467 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1468 return MMSYSERR_NOTENABLED;
1471 #endif