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
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 {
101 WAVEOUTCAPSW out_caps;
103 DWORD in_caps_support;
107 unsigned audio_fragment;
109 BOOL bTriggerSupport;
112 DSDRIVERDESC ds_desc;
113 DSDRIVERCAPS ds_caps;
114 DSCDRIVERCAPS dsc_caps;
118 AudioDeviceID outputDeviceID;
119 AudioDeviceID inputDeviceID;
120 AudioStreamBasicDescription streamDescription;
123 /* for now use the default device */
124 static CoreAudio_Device CoreAudio_DefaultDevice;
127 volatile int state; /* one of the WINE_WS_ manifest constants */
128 CoreAudio_Device *cadev;
129 WAVEOPENDESC waveDesc;
131 PCMWAVEFORMAT format;
134 AudioStreamBasicDescription streamDescription;
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 */
155 CoreAudio_Device *cadev;
156 WAVEOPENDESC waveDesc;
158 PCMWAVEFORMAT format;
159 LPWAVEHDR lpQueuePtr;
160 DWORD dwTotalRecorded;
164 AudioStreamBasicDescription streamDescription;
166 /* BOOL bTriggerSupport;
168 char interface_name[32];*/
170 /* synchronization stuff */
171 pthread_mutex_t lock;
174 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
176 static CFStringRef MessageThreadPortName;
178 static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
179 static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
181 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
182 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
183 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
185 extern OSStatus AudioOutputUnitStart(AudioUnit au);
186 extern OSStatus AudioOutputUnitStop(AudioUnit au);
187 extern OSStatus AudioUnitUninitialize(AudioUnit au);
189 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
190 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
192 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
193 AudioUnitRenderActionFlags *ioActionFlags,
194 const AudioTimeStamp *inTimeStamp,
196 UInt32 inNumberFrames,
197 AudioBufferList *ioData);
199 /* These strings used only for tracing */
201 static const char * getMessage(UINT msg)
203 static char unknown[32];
204 #define MSG_TO_STR(x) case x: return #x
206 MSG_TO_STR(DRVM_INIT);
207 MSG_TO_STR(DRVM_EXIT);
208 MSG_TO_STR(DRVM_ENABLE);
209 MSG_TO_STR(DRVM_DISABLE);
210 MSG_TO_STR(WIDM_OPEN);
211 MSG_TO_STR(WIDM_CLOSE);
212 MSG_TO_STR(WIDM_ADDBUFFER);
213 MSG_TO_STR(WIDM_PREPARE);
214 MSG_TO_STR(WIDM_UNPREPARE);
215 MSG_TO_STR(WIDM_GETDEVCAPS);
216 MSG_TO_STR(WIDM_GETNUMDEVS);
217 MSG_TO_STR(WIDM_GETPOS);
218 MSG_TO_STR(WIDM_RESET);
219 MSG_TO_STR(WIDM_START);
220 MSG_TO_STR(WIDM_STOP);
221 MSG_TO_STR(WODM_OPEN);
222 MSG_TO_STR(WODM_CLOSE);
223 MSG_TO_STR(WODM_WRITE);
224 MSG_TO_STR(WODM_PAUSE);
225 MSG_TO_STR(WODM_GETPOS);
226 MSG_TO_STR(WODM_BREAKLOOP);
227 MSG_TO_STR(WODM_PREPARE);
228 MSG_TO_STR(WODM_UNPREPARE);
229 MSG_TO_STR(WODM_GETDEVCAPS);
230 MSG_TO_STR(WODM_GETNUMDEVS);
231 MSG_TO_STR(WODM_GETPITCH);
232 MSG_TO_STR(WODM_SETPITCH);
233 MSG_TO_STR(WODM_GETPLAYBACKRATE);
234 MSG_TO_STR(WODM_SETPLAYBACKRATE);
235 MSG_TO_STR(WODM_GETVOLUME);
236 MSG_TO_STR(WODM_SETVOLUME);
237 MSG_TO_STR(WODM_RESTART);
238 MSG_TO_STR(WODM_RESET);
239 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
240 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
241 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
242 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
245 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
249 #define kStopLoopMessage 0
250 #define kWaveOutCallbackMessage 1
251 #define kWaveInCallbackMessage 2
253 /* Mach Message Handling */
254 static CFDataRef wodMessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
256 UInt32 *buffer = NULL;
257 WINE_WAVEOUT* wwo = NULL;
261 case kWaveOutCallbackMessage:
262 buffer = (UInt32 *) CFDataGetBytePtr(data);
263 wwo = &WOutDev[buffer[0]];
265 pthread_mutex_lock(&wwo->lock);
266 DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
267 (HDRVR)wwo->waveDesc.hWave, (WORD)buffer[1], wwo->waveDesc.dwInstance,
268 (DWORD)buffer[2], (DWORD)buffer[3]);
269 pthread_mutex_unlock(&wwo->lock);
271 case kWaveInCallbackMessage:
273 CFRunLoopStop(CFRunLoopGetCurrent());
280 static DWORD WINAPI messageThread(LPVOID p)
282 CFMessagePortRef local;
283 CFRunLoopSourceRef source;
286 local = CFMessagePortCreateLocal(kCFAllocatorDefault, MessageThreadPortName,
287 &wodMessageHandler, NULL, &info);
289 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0);
290 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
294 CFRunLoopSourceInvalidate(source);
297 CFRelease(MessageThreadPortName);
298 MessageThreadPortName = NULL;
303 static DWORD wodSendDriverCallbackMessage(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
309 CFMessagePortRef messagePort;
310 messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MessageThreadPortName);
312 buffer[0] = (UInt32) wwo->woID;
313 buffer[1] = (UInt32) wMsg;
314 buffer[2] = (UInt32) dwParam1;
315 buffer[3] = (UInt32) dwParam2;
317 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)buffer, sizeof(buffer));
321 ret = CFMessagePortSendRequest(messagePort, kWaveOutCallbackMessage, data, 0.0, 0.0, NULL, NULL);
323 CFRelease(messagePort);
325 return (ret == kCFMessagePortSuccess)?1:0;
328 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
329 PCMWAVEFORMAT* format)
331 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
332 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
333 format->wf.nChannels, format->wf.nAvgBytesPerSec);
334 TRACE("Position in bytes=%u\n", position);
336 switch (lpTime->wType) {
338 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
339 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
342 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
343 TRACE("TIME_MS=%u\n", lpTime->u.ms);
346 lpTime->u.smpte.fps = 30;
347 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
348 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
349 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
350 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
351 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
352 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
353 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
354 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
355 lpTime->u.smpte.fps = 30;
356 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
357 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
358 lpTime->u.smpte.hour, lpTime->u.smpte.min,
359 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
362 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
363 lpTime->wType = TIME_BYTES;
366 lpTime->u.cb = position;
367 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
370 return MMSYSERR_NOERROR;
373 /**************************************************************************
374 * CoreAudio_GetDevCaps [internal]
376 BOOL CoreAudio_GetDevCaps (void)
380 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
382 char name[MAXPNAMELEN];
384 propertySize = MAXPNAMELEN;
385 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
387 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
388 (char) (status >> 16),
389 (char) (status >> 8),
394 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
395 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
396 MultiByteToWideChar(CP_ACP, 0, name, sizeof(name),
397 CoreAudio_DefaultDevice.out_caps.szPname,
398 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
399 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
401 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
402 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
403 if (status != noErr) {
404 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
405 (char) (status >> 16),
406 (char) (status >> 8),
411 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
412 "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
413 "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
414 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
415 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
416 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
417 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
418 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
419 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
420 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
421 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
422 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
423 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
424 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
426 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
427 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
429 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
430 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
431 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
432 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
433 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
435 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
436 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
441 /******************************************************************
444 * Initialize CoreAudio_DefaultDevice
446 LONG CoreAudio_WaveInit(void)
450 CHAR szPname[MAXPNAMELEN];
451 pthread_mutexattr_t mutexattr;
457 /* number of sound cards */
458 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
459 propertySize /= sizeof(AudioDeviceID);
460 TRACE("sound cards : %lu\n", propertySize);
462 /* Get the output device */
463 propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
464 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
466 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
467 (char) (status >> 16),
468 (char) (status >> 8),
472 if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
473 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
477 if ( ! CoreAudio_GetDevCaps() )
480 CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
481 sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name);
483 pthread_mutexattr_init(&mutexattr);
484 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
486 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
488 WOutDev[i].state = WINE_WS_CLOSED;
489 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
492 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
494 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
495 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
496 snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
497 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
498 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
500 WOutDev[i].caps.vDriverVersion = 0x0001;
501 WOutDev[i].caps.dwFormats = 0x00000000;
502 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
504 WOutDev[i].caps.wChannels = 2;
505 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
507 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
508 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
509 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
510 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
511 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
512 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
513 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
514 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
515 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
516 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
517 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
518 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
520 pthread_mutex_init(&WOutDev[i].lock, &mutexattr); /* initialize the mutex */
523 pthread_mutexattr_destroy(&mutexattr);
525 /* create mach messages handler */
527 MessageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
528 CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
529 if (!MessageThreadPortName)
531 ERR("Can't create message thread port name\n");
535 hThread = CreateThread(NULL, 0, messageThread, NULL, 0, NULL);
538 ERR("Can't create message thread\n");
545 void CoreAudio_WaveRelease(void)
547 /* Stop CFRunLoop in messageThread */
548 CFMessagePortRef messagePort;
553 messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MessageThreadPortName);
554 CFMessagePortSendRequest(messagePort, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
555 CFRelease(messagePort);
557 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
559 pthread_mutex_destroy(&WOutDev[i].lock);
563 /*======================================================================*
564 * Low level WAVE OUT implementation *
565 *======================================================================*/
567 /**************************************************************************
568 * wodNotifyClient [internal]
569 * Call from AudioUnit IO thread can't use Wine debug channels.
571 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
576 if (wwo->wFlags != DCB_NULL &&
577 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
578 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
581 return MMSYSERR_ERROR;
585 if (wwo->wFlags != DCB_NULL &&
586 ! wodSendDriverCallbackMessage(wwo, wMsg, dwParam1, dwParam2))
588 return MMSYSERR_ERROR;
592 return MMSYSERR_INVALPARAM;
594 return MMSYSERR_NOERROR;
598 /**************************************************************************
599 * wodGetDevCaps [internal]
601 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
603 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
605 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
607 if (wDevID >= MAX_WAVEOUTDRV)
609 TRACE("MAX_WAVOUTDRV reached !\n");
610 return MMSYSERR_BADDEVICEID;
613 TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
614 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
615 return MMSYSERR_NOERROR;
618 /**************************************************************************
621 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
626 AudioStreamBasicDescription streamFormat;
628 TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
631 WARN("Invalid Parameter !\n");
632 return MMSYSERR_INVALPARAM;
634 if (wDevID >= MAX_WAVEOUTDRV) {
635 TRACE("MAX_WAVOUTDRV reached !\n");
636 return MMSYSERR_BADDEVICEID;
639 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
640 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
641 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
643 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
644 lpDesc->lpFormat->nChannels == 0 ||
645 lpDesc->lpFormat->nSamplesPerSec == 0
648 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
649 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
650 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
651 return WAVERR_BADFORMAT;
654 if (dwFlags & WAVE_FORMAT_QUERY)
656 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
657 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
658 lpDesc->lpFormat->nSamplesPerSec);
659 return MMSYSERR_NOERROR;
662 wwo = &WOutDev[wDevID];
663 pthread_mutex_lock(&wwo->lock);
665 if (wwo->state != WINE_WS_CLOSED)
667 pthread_mutex_unlock(&wwo->lock);
668 return MMSYSERR_ALLOCATED;
671 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
673 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
674 pthread_mutex_unlock(&wwo->lock);
675 return MMSYSERR_ERROR;
678 if ((dwFlags & WAVE_DIRECTSOUND) &&
679 !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
680 /* not supported, ignore it */
681 dwFlags &= ~WAVE_DIRECTSOUND;
683 streamFormat.mFormatID = kAudioFormatLinearPCM;
684 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
685 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
686 if (lpDesc->lpFormat->wBitsPerSample != 8)
687 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
688 # ifdef WORDS_BIGENDIAN
689 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
692 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
693 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
694 streamFormat.mFramesPerPacket = 1;
695 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
696 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
697 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
699 ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, &streamFormat);
702 AudioUnit_CloseAudioUnit(wwo->audioUnit);
703 pthread_mutex_unlock(&wwo->lock);
704 return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
706 wwo->streamDescription = streamFormat;
707 wwo->state = WINE_WS_STOPPED;
709 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
711 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
712 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
714 if (wwo->format.wBitsPerSample == 0) {
715 WARN("Resetting zeroed wBitsPerSample\n");
716 wwo->format.wBitsPerSample = 8 *
717 (wwo->format.wf.nAvgBytesPerSec /
718 wwo->format.wf.nSamplesPerSec) /
719 wwo->format.wf.nChannels;
722 wwo->dwPlayedTotal = 0;
723 wwo->dwWrittenTotal = 0;
725 pthread_mutex_unlock(&wwo->lock);
727 retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
732 /**************************************************************************
733 * wodClose [internal]
735 static DWORD wodClose(WORD wDevID)
737 DWORD ret = MMSYSERR_NOERROR;
740 TRACE("(%u);\n", wDevID);
742 if (wDevID >= MAX_WAVEOUTDRV)
744 WARN("bad device ID !\n");
745 return MMSYSERR_BADDEVICEID;
748 wwo = &WOutDev[wDevID];
749 pthread_mutex_lock(&wwo->lock);
752 WARN("buffers still playing !\n");
753 pthread_mutex_unlock(&wwo->lock);
754 ret = WAVERR_STILLPLAYING;
758 /* sanity check: this should not happen since the device must have been reset before */
759 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
761 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
763 err = AudioUnitUninitialize(wwo->audioUnit);
765 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
769 pthread_mutex_unlock(&wwo->lock);
770 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
773 if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
775 ERR("Can't close AudioUnit\n");
776 pthread_mutex_unlock(&wwo->lock);
777 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
779 pthread_mutex_unlock(&wwo->lock);
781 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
787 /**************************************************************************
788 * wodPrepare [internal]
790 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
792 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
794 if (wDevID >= MAX_WAVEOUTDRV) {
795 WARN("bad device ID !\n");
796 return MMSYSERR_BADDEVICEID;
799 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
800 return WAVERR_STILLPLAYING;
802 lpWaveHdr->dwFlags |= WHDR_PREPARED;
803 lpWaveHdr->dwFlags &= ~WHDR_DONE;
805 return MMSYSERR_NOERROR;
808 /**************************************************************************
809 * wodUnprepare [internal]
811 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
813 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
815 if (wDevID >= MAX_WAVEOUTDRV) {
816 WARN("bad device ID !\n");
817 return MMSYSERR_BADDEVICEID;
820 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
821 return WAVERR_STILLPLAYING;
823 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
824 lpWaveHdr->dwFlags |= WHDR_DONE;
826 return MMSYSERR_NOERROR;
829 /**************************************************************************
830 * wodHelper_BeginWaveHdr [internal]
832 * Makes the specified lpWaveHdr the currently playing wave header.
833 * If the specified wave header is a begin loop and we're not already in
834 * a loop, setup the loop.
835 * Call from AudioUnit IO thread can't use Wine debug channels.
837 static void wodHelper_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
841 wwo->lpPlayPtr = lpWaveHdr;
845 if (wwo->state == WINE_WS_PLAYING)
847 wwo->state = WINE_WS_STOPPED;
848 status = AudioOutputUnitStop(wwo->audioUnit);
850 fprintf(stderr, "err:winecoreaudio:wodHelper_BeginWaveHdr AudioOutputUnitStop return %c%c%c%c\n",
851 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
856 if (wwo->state == WINE_WS_STOPPED)
858 status = AudioOutputUnitStart(wwo->audioUnit);
860 fprintf(stderr, "err:winecoreaudio:AudioOutputUnitStart return %c%c%c%c\n",
861 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
863 else wwo->state = WINE_WS_PLAYING;
866 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
870 fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
873 fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
875 wwo->lpLoopPtr = lpWaveHdr;
876 /* Windows does not touch WAVEHDR.dwLoops,
877 * so we need to make an internal copy */
878 wwo->dwLoops = lpWaveHdr->dwLoops;
881 wwo->dwPartialOffset = 0;
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 LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
895 pthread_mutex_lock(&wwo->lock);
897 lpWaveHdr = wwo->lpPlayPtr;
900 pthread_mutex_unlock(&wwo->lock);
904 wwo->dwPartialOffset = 0;
905 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
907 /* We're at the end of a loop, loop if required */
908 if (wwo->dwLoops > 1)
911 wwo->lpPlayPtr = wwo->lpLoopPtr;
914 /* Handle overlapping loops correctly */
915 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
916 /* shall we consider the END flag for the closing loop or for
917 * the opening one or for both ???
918 * code assumes for closing loop only
922 lpWaveHdr = lpWaveHdr->lpNext;
924 wwo->lpLoopPtr = NULL;
925 wodHelper_BeginWaveHdr(wwo, lpWaveHdr);
929 /* We're not in a loop. Advance to the next wave header */
930 wodHelper_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
933 pthread_mutex_unlock(&wwo->lock);
938 /* if force is TRUE then notify the client that all the headers were completed
939 * Call from AudioUnit IO thread can't use Wine debug channels.
941 static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
946 pthread_mutex_lock(&wwo->lock);
948 /* Start from lpQueuePtr and keep notifying until:
949 * - we hit an unwritten wavehdr
950 * - we hit the beginning of a running loop
951 * - we hit a wavehdr which hasn't finished playing
953 while ((lpWaveHdr = wwo->lpQueuePtr) &&
955 (lpWaveHdr != wwo->lpPlayPtr &&
956 lpWaveHdr != wwo->lpLoopPtr)))
958 wwo->lpQueuePtr = lpWaveHdr->lpNext;
960 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
961 lpWaveHdr->dwFlags |= WHDR_DONE;
963 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
966 retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr !=
967 wwo->lpLoopPtr) ? 0 : INFINITE;
969 pthread_mutex_unlock(&wwo->lock);
974 /**************************************************************************
975 * wodHelper_Reset [internal]
977 * Resets current output stream.
979 static DWORD wodHelper_Reset(WINE_WAVEOUT* wwo, BOOL reset)
985 /* updates current notify list */
986 /* if resetting, remove all wave headers and notify client that all headers were completed */
987 wodHelper_NotifyCompletions(wwo, reset);
989 pthread_mutex_lock(&wwo->lock);
993 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
994 wwo->state = WINE_WS_STOPPED;
995 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
997 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
1003 /* complicated case, not handled yet (could imply modifying the loop counter) */
1004 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
1005 wwo->lpPlayPtr = wwo->lpLoopPtr;
1006 wwo->dwPartialOffset = 0;
1007 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
1011 DWORD sz = wwo->dwPartialOffset;
1013 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
1014 /* compute the max size playable from lpQueuePtr */
1015 for (ptr = wwo->lpQueuePtr; ptr && ptr != wwo->lpPlayPtr; ptr = ptr->lpNext)
1017 sz += ptr->dwBufferLength;
1020 /* because the reset lpPlayPtr will be lpQueuePtr */
1021 if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("doh\n");
1022 wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
1023 wwo->dwWrittenTotal = wwo->dwPlayedTotal;
1024 wwo->lpPlayPtr = wwo->lpQueuePtr;
1027 wwo->state = WINE_WS_PAUSED;
1030 status = AudioOutputUnitStop(wwo->audioUnit);
1032 pthread_mutex_unlock(&wwo->lock);
1035 ERR( "AudioOutputUnitStop return %c%c%c%c\n",
1036 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1037 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1040 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * wodWrite [internal]
1048 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1053 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1055 /* first, do the sanity checks... */
1056 if (wDevID >= MAX_WAVEOUTDRV)
1058 WARN("bad dev ID !\n");
1059 return MMSYSERR_BADDEVICEID;
1062 wwo = &WOutDev[wDevID];
1064 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1066 TRACE("unprepared\n");
1067 return WAVERR_UNPREPARED;
1070 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1072 TRACE("still playing\n");
1073 return WAVERR_STILLPLAYING;
1076 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1077 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1078 lpWaveHdr->lpNext = 0;
1080 pthread_mutex_lock(&wwo->lock);
1081 /* insert buffer at the end of queue */
1082 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1086 if (!wwo->lpPlayPtr)
1087 wodHelper_BeginWaveHdr(wwo,lpWaveHdr);
1088 pthread_mutex_unlock(&wwo->lock);
1090 return MMSYSERR_NOERROR;
1093 /**************************************************************************
1094 * wodPause [internal]
1096 static DWORD wodPause(WORD wDevID)
1100 TRACE("(%u);!\n", wDevID);
1102 if (wDevID >= MAX_WAVEOUTDRV)
1104 WARN("bad device ID !\n");
1105 return MMSYSERR_BADDEVICEID;
1108 pthread_mutex_lock(&WOutDev[wDevID].lock);
1109 if (WOutDev[wDevID].state == WINE_WS_PLAYING || WOutDev[wDevID].state == WINE_WS_STOPPED)
1111 WOutDev[wDevID].state = WINE_WS_PAUSED;
1112 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1114 ERR( "AudioOutputUnitStop return %c%c%c%c\n",
1115 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1116 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1117 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1120 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1122 return MMSYSERR_NOERROR;
1125 /**************************************************************************
1126 * wodRestart [internal]
1128 static DWORD wodRestart(WORD wDevID)
1130 TRACE("(%u);\n", wDevID);
1132 if (wDevID >= MAX_WAVEOUTDRV )
1134 WARN("bad device ID !\n");
1135 return MMSYSERR_BADDEVICEID;
1138 pthread_mutex_lock(&WOutDev[wDevID].lock);
1139 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1141 if (WOutDev[wDevID].lpPlayPtr)
1143 OSStatus status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1145 ERR("AudioOutputUnitStart return %c%c%c%c\n",
1146 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1147 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1148 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1150 WOutDev[wDevID].state = WINE_WS_PLAYING;
1153 WOutDev[wDevID].state = WINE_WS_STOPPED;
1155 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1157 return MMSYSERR_NOERROR;
1160 /**************************************************************************
1161 * wodReset [internal]
1163 static DWORD wodReset(WORD wDevID)
1165 TRACE("(%u);\n", wDevID);
1167 if (wDevID >= MAX_WAVEOUTDRV)
1169 WARN("bad device ID !\n");
1170 return MMSYSERR_BADDEVICEID;
1173 return wodHelper_Reset(&WOutDev[wDevID], TRUE);
1176 /**************************************************************************
1177 * wodGetPosition [internal]
1179 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1184 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1186 if (wDevID >= MAX_WAVEOUTDRV)
1188 WARN("bad device ID !\n");
1189 return MMSYSERR_BADDEVICEID;
1192 /* if null pointer to time structure return error */
1193 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1195 wwo = &WOutDev[wDevID];
1197 pthread_mutex_lock(&WOutDev[wDevID].lock);
1198 val = wwo->dwPlayedTotal;
1199 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1201 return bytes_to_mmtime(lpTime, val, &wwo->format);
1204 /**************************************************************************
1205 * wodGetVolume [internal]
1207 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1212 if (wDevID >= MAX_WAVEOUTDRV)
1214 WARN("bad device ID !\n");
1215 return MMSYSERR_BADDEVICEID;
1218 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1220 pthread_mutex_lock(&WOutDev[wDevID].lock);
1222 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1224 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1226 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1228 return MMSYSERR_NOERROR;
1231 /**************************************************************************
1232 * wodSetVolume [internal]
1234 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1239 if (wDevID >= MAX_WAVEOUTDRV)
1241 WARN("bad device ID !\n");
1242 return MMSYSERR_BADDEVICEID;
1245 left = LOWORD(dwParam) / 65535.0f;
1246 right = HIWORD(dwParam) / 65535.0f;
1248 TRACE("(%u, %08x);\n", wDevID, dwParam);
1250 pthread_mutex_lock(&WOutDev[wDevID].lock);
1252 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1254 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1256 return MMSYSERR_NOERROR;
1259 /**************************************************************************
1260 * wodGetNumDevs [internal]
1262 static DWORD wodGetNumDevs(void)
1265 return MAX_WAVEOUTDRV;
1268 /**************************************************************************
1269 * wodDevInterfaceSize [internal]
1271 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1273 TRACE("(%u, %p)\n", wDevID, dwParam1);
1275 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1276 NULL, 0 ) * sizeof(WCHAR);
1277 return MMSYSERR_NOERROR;
1280 /**************************************************************************
1281 * wodDevInterface [internal]
1283 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1286 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1287 NULL, 0 ) * sizeof(WCHAR))
1289 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1290 dwParam1, dwParam2 / sizeof(WCHAR));
1291 return MMSYSERR_NOERROR;
1293 return MMSYSERR_INVALPARAM;
1296 /**************************************************************************
1297 * wodMessage (WINEJACK.7)
1299 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1300 DWORD dwParam1, DWORD dwParam2)
1302 TRACE("(%u, %s, %08x, %08x, %08x);\n",
1303 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1311 /* FIXME: Pretend this is supported */
1313 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1314 case WODM_CLOSE: return wodClose(wDevID);
1315 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1316 case WODM_PAUSE: return wodPause(wDevID);
1317 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1318 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1319 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1320 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1322 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1323 case WODM_GETNUMDEVS: return wodGetNumDevs();
1327 case WODM_GETPLAYBACKRATE:
1328 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1329 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1330 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1331 case WODM_RESTART: return wodRestart(wDevID);
1332 case WODM_RESET: return wodReset(wDevID);
1334 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1335 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1336 case DRV_QUERYDSOUNDIFACE:
1337 case DRV_QUERYDSOUNDDESC:
1338 return MMSYSERR_NOTSUPPORTED;
1341 FIXME("unknown message %d!\n", wMsg);
1344 return MMSYSERR_NOTSUPPORTED;
1347 /*======================================================================*
1348 * Low level DSOUND implementation *
1349 *======================================================================*/
1351 typedef struct IDsDriverImpl IDsDriverImpl;
1352 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1354 struct IDsDriverImpl
1356 /* IUnknown fields */
1357 const IDsDriverVtbl *lpVtbl;
1359 /* IDsDriverImpl fields */
1361 IDsDriverBufferImpl*primary;
1364 struct IDsDriverBufferImpl
1366 /* IUnknown fields */
1367 const IDsDriverBufferVtbl *lpVtbl;
1369 /* IDsDriverBufferImpl fields */
1376 CoreAudio IO threaded callback,
1377 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1379 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1380 AudioUnitRenderActionFlags *ioActionFlags,
1381 const AudioTimeStamp *inTimeStamp,
1383 UInt32 inNumberFrames,
1384 AudioBufferList *ioData)
1387 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1391 unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1392 unsigned int dataProvided = 0;
1394 while (dataNeeded > 0)
1396 pthread_mutex_lock(&wwo->lock);
1398 if (wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1400 unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1401 unsigned int toCopy;
1403 if (available >= dataNeeded)
1404 toCopy = dataNeeded;
1410 memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1411 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1412 wwo->dwPartialOffset += toCopy;
1413 wwo->dwPlayedTotal += toCopy;
1414 dataProvided += toCopy;
1415 dataNeeded -= toCopy;
1416 available -= toCopy;
1426 memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1427 dataProvided += dataNeeded;
1431 pthread_mutex_unlock(&wwo->lock);
1435 wodHelper_PlayPtrNext(wwo);
1440 /* We only fill buffer 0. Set any others that might be requested to 0. */
1441 for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1443 memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1446 if (needNotify) wodHelper_NotifyCompletions(wwo, FALSE);
1451 /**************************************************************************
1452 * wodMessage (WINECOREAUDIO.7)
1454 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1455 DWORD dwParam1, DWORD dwParam2)
1457 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1458 return MMSYSERR_NOTENABLED;