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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
45 #include "coreaudio.h"
46 #include "wine/unicode.h"
47 #include "wine/library.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(wave);
53 #if defined(HAVE_COREAUDIO_COREAUDIO_H) && defined(HAVE_AUDIOUNIT_AUDIOUNIT_H)
54 #include <CoreAudio/CoreAudio.h>
55 #include <CoreFoundation/CoreFoundation.h>
58 Due to AudioUnit headers conflict define some needed types.
61 typedef void *AudioUnit;
63 /* From AudioUnit/AUComponents.h */
64 typedef UInt32 AudioUnitRenderActionFlags;
66 /* only allow 10 output devices through this driver, this ought to be adequate */
67 #define MAX_WAVEOUTDRV (1)
68 #define MAX_WAVEINDRV (1)
70 /* state diagram for waveOut writing:
72 * +---------+-------------+---------------+---------------------------------+
73 * | state | function | event | new state |
74 * +---------+-------------+---------------+---------------------------------+
75 * | | open() | | STOPPED |
76 * | PAUSED | write() | | PAUSED |
77 * | STOPPED | write() | <thrd create> | PLAYING |
78 * | PLAYING | write() | HEADER | PLAYING |
79 * | (other) | write() | <error> | |
80 * | (any) | pause() | PAUSING | PAUSED |
81 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
82 * | (any) | reset() | RESETTING | STOPPED |
83 * | (any) | close() | CLOSING | CLOSED |
84 * +---------+-------------+---------------+---------------------------------+
87 /* states of the playing device */
88 #define WINE_WS_PLAYING 0
89 #define WINE_WS_PAUSED 1
90 #define WINE_WS_STOPPED 2
91 #define WINE_WS_CLOSED 3
93 typedef struct tagCoreAudio_Device {
99 WAVEOUTCAPSA out_caps;
101 DWORD in_caps_support;
105 unsigned audio_fragment;
107 BOOL bTriggerSupport;
110 DSDRIVERDESC ds_desc;
111 DSDRIVERCAPS ds_caps;
112 DSCDRIVERCAPS dsc_caps;
116 AudioDeviceID outputDeviceID;
117 AudioDeviceID inputDeviceID;
118 AudioStreamBasicDescription streamDescription;
121 /* for now use the default device */
122 static CoreAudio_Device CoreAudio_DefaultDevice;
125 volatile int state; /* one of the WINE_WS_ manifest constants */
126 CoreAudio_Device *cadev;
127 WAVEOPENDESC waveDesc;
129 PCMWAVEFORMAT format;
132 AudioStreamBasicDescription streamDescription;
135 char interface_name[32];
136 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
137 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
138 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
140 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
141 DWORD dwLoops; /* private copy of loop counter */
143 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
144 DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */
146 DWORD tickCountMS; /* time in MS of last AudioUnit callback */
148 pthread_mutex_t lock; /* synchronization stuff */
153 CoreAudio_Device *cadev;
154 WAVEOPENDESC waveDesc;
156 PCMWAVEFORMAT format;
157 LPWAVEHDR lpQueuePtr;
158 DWORD dwTotalRecorded;
162 AudioStreamBasicDescription streamDescription;
164 /* BOOL bTriggerSupport;
166 char interface_name[32];*/
168 /* synchronization stuff */
169 pthread_mutex_t lock;
172 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
173 static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];
175 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
176 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
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 messageThread(LPVOID p)
282 CFMessagePortRef local;
283 CFRunLoopSourceRef source;
286 local = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("WaveMessagePort"),
287 &wodMessageHandler, NULL, &info);
289 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0);
290 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
297 static DWORD wodSendDriverCallbackMessage(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
303 CFMessagePortRef messagePort;
304 messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR("WaveMessagePort"));
306 buffer = CFAllocatorAllocate(NULL, sizeof(UInt32) * 4, 0);
310 buffer[0] = (UInt32) wwo->woID;
311 buffer[1] = (UInt32) wMsg;
312 buffer[2] = (UInt32) dwParam1;
313 buffer[3] = (UInt32) dwParam2;
315 data = CFDataCreate(kCFAllocatorDefault, buffer, sizeof(UInt32) * 4);
318 CFAllocatorDeallocate(NULL, buffer);
322 ret = CFMessagePortSendRequest(messagePort, kWaveOutCallbackMessage, data, 0.0, 0.0, NULL, NULL);
324 CFAllocatorDeallocate(NULL, buffer);
326 return (ret == kCFMessagePortSuccess)?1:0;
329 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
330 PCMWAVEFORMAT* format)
332 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
333 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
334 format->wf.nChannels, format->wf.nAvgBytesPerSec);
335 TRACE("Position in bytes=%lu\n", position);
337 switch (lpTime->wType) {
339 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
340 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
343 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
344 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
347 lpTime->u.smpte.fps = 30;
348 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
349 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
350 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
351 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
352 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
353 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
354 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
355 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
356 lpTime->u.smpte.fps = 30;
357 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
358 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
359 lpTime->u.smpte.hour, lpTime->u.smpte.min,
360 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
363 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
364 lpTime->wType = TIME_BYTES;
367 lpTime->u.cb = position;
368 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
371 return MMSYSERR_NOERROR;
374 /**************************************************************************
375 * CoreAudio_GetDevCaps [internal]
377 BOOL CoreAudio_GetDevCaps (void)
381 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
383 char name[MAXPNAMELEN];
385 propertySize = MAXPNAMELEN;
386 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
388 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
389 (char) (status >> 16),
390 (char) (status >> 8),
395 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
396 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
397 MultiByteToWideChar(CP_ACP, 0, name, sizeof(name),
398 CoreAudio_DefaultDevice.out_caps.szPname,
399 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
400 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
402 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
403 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
404 if (status != noErr) {
405 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
406 (char) (status >> 16),
407 (char) (status >> 8),
412 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
413 "mFormatFlags : %lX\n mBytesPerPacket : %u\n mFramesPerPacket : %u\n"
414 "mBytesPerFrame : %u\n mChannelsPerFrame : %u\n mBitsPerChannel : %u\n",
415 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
416 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
417 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
418 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
419 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
420 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
421 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
422 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
423 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
424 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
425 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
427 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
428 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
430 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
431 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
432 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
433 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
434 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
436 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
437 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
442 /******************************************************************
445 * Initialize CoreAudio_DefaultDevice
447 LONG CoreAudio_WaveInit(void)
451 CHAR szPname[MAXPNAMELEN];
457 /* number of sound cards */
458 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
459 propertySize /= sizeof(AudioDeviceID);
460 TRACE("sound cards : %u\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 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
485 WOutDev[i].state = WINE_WS_CLOSED;
486 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
489 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
491 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
492 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
493 snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
494 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
495 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
497 WOutDev[i].caps.vDriverVersion = 0x0001;
498 WOutDev[i].caps.dwFormats = 0x00000000;
499 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
501 WOutDev[i].caps.wChannels = 2;
502 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
504 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
505 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
506 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
507 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
508 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
509 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
510 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
511 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
512 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
513 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
514 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
515 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
518 /* create mach messages handler */
519 hThread = CreateThread(NULL, 0, messageThread, NULL, 0, NULL);
522 ERR("Can't create message thread\n");
529 void CoreAudio_WaveRelease(void)
533 /* Stop CFRunLoop in messageThread */
534 CFMessagePortRef messagePort;
536 messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR("WaveMessagePort"));
537 CFMessagePortSendRequest(messagePort, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
538 CFRelease(messagePort);
541 /*======================================================================*
542 * Low level WAVE OUT implementation *
543 *======================================================================*/
545 /**************************************************************************
546 * wodNotifyClient [internal]
547 * Call from AudioUnit IO thread can't use Wine debug channels.
549 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
554 if (wwo->wFlags != DCB_NULL &&
555 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
556 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
559 return MMSYSERR_ERROR;
563 if (wwo->wFlags != DCB_NULL &&
564 ! wodSendDriverCallbackMessage(wwo, wMsg, dwParam1, dwParam2))
566 return MMSYSERR_ERROR;
570 return MMSYSERR_INVALPARAM;
572 return MMSYSERR_NOERROR;
576 /**************************************************************************
577 * wodGetDevCaps [internal]
579 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
581 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
583 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
585 if (wDevID >= MAX_WAVEOUTDRV)
587 TRACE("MAX_WAVOUTDRV reached !\n");
588 return MMSYSERR_BADDEVICEID;
591 TRACE("dwSupport=(0x%lx), dwFormats=(0x%lx)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
592 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
593 return MMSYSERR_NOERROR;
596 /**************************************************************************
599 * NOTE: doesn't it seem like there is a race condition if you try to open
600 * the same device twice?
602 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
609 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
612 WARN("Invalid Parameter !\n");
613 return MMSYSERR_INVALPARAM;
615 if (wDevID >= MAX_WAVEOUTDRV) {
616 TRACE("MAX_WAVOUTDRV reached !\n");
617 return MMSYSERR_BADDEVICEID;
620 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%ld wBitsPerSample=%d !\n",
621 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
622 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
624 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
625 lpDesc->lpFormat->nChannels == 0 ||
626 lpDesc->lpFormat->nSamplesPerSec == 0
629 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld wBitsPerSample=%d !\n",
630 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
631 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
632 return WAVERR_BADFORMAT;
635 if (dwFlags & WAVE_FORMAT_QUERY)
637 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
638 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
639 lpDesc->lpFormat->nSamplesPerSec);
640 return MMSYSERR_NOERROR;
643 wwo = &WOutDev[wDevID];
644 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
646 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
647 return MMSYSERR_ERROR;
650 if ((dwFlags & WAVE_DIRECTSOUND) &&
651 !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
652 /* not supported, ignore it */
653 dwFlags &= ~WAVE_DIRECTSOUND;
655 if (wwo->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
657 pthread_mutex_init(&wwo->lock, NULL); /* initialize the mutex */
658 pthread_mutex_lock(&wwo->lock);
660 AudioStreamBasicDescription streamFormat;
662 streamFormat.mFormatID = kAudioFormatLinearPCM;
664 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
665 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
667 | kLinearPCMFormatFlagIsBigEndian /* FIXME Wave format is little endian */
669 | kLinearPCMFormatFlagIsPacked;
671 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
672 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
673 streamFormat.mFramesPerPacket = 1;
674 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
675 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
676 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
678 ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, streamFormat);
681 pthread_mutex_unlock(&wwo->lock);
682 return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
684 wwo->streamDescription = streamFormat;
685 wwo->state = WINE_WS_STOPPED;
687 pthread_mutex_unlock(&wwo->lock);
689 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
691 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
692 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
694 if (wwo->format.wBitsPerSample == 0) {
695 WARN("Resetting zeroed wBitsPerSample\n");
696 wwo->format.wBitsPerSample = 8 *
697 (wwo->format.wf.nAvgBytesPerSec /
698 wwo->format.wf.nSamplesPerSec) /
699 wwo->format.wf.nChannels;
702 wwo->dwPlayedTotal = 0;
703 wwo->dwWrittenTotal = 0;
705 retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
710 /**************************************************************************
711 * wodClose [internal]
713 static DWORD wodClose(WORD wDevID)
715 DWORD ret = MMSYSERR_NOERROR;
718 TRACE("(%u);\n", wDevID);
720 if (wDevID >= MAX_WAVEOUTDRV)
722 WARN("bad device ID !\n");
723 return MMSYSERR_BADDEVICEID;
726 wwo = &WOutDev[wDevID];
729 WARN("buffers still playing !\n");
730 ret = WAVERR_STILLPLAYING;
734 /* sanity check: this should not happen since the device must have been reset before */
735 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
737 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
739 err = AudioUnitUninitialize(wwo->audioUnit);
741 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
745 pthread_mutex_destroy(&wwo->lock);
746 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
749 if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
751 ERR("Can't close AudioUnit\n");
752 pthread_mutex_destroy(&wwo->lock);
753 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
755 pthread_mutex_destroy(&wwo->lock);
757 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
763 /**************************************************************************
764 * wodPrepare [internal]
766 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
770 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
772 if (wDevID >= MAX_WAVEOUTDRV) {
773 WARN("bad device ID !\n");
774 return MMSYSERR_BADDEVICEID;
777 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
778 return WAVERR_STILLPLAYING;
780 lpWaveHdr->dwFlags |= WHDR_PREPARED;
781 lpWaveHdr->dwFlags &= ~WHDR_DONE;
783 WOutDev[wDevID].state = WINE_WS_STOPPED;
785 status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
787 ERR("AudioOutputUnitStart return %c%c%c%c\n", (char) (status >> 24),
788 (char) (status >> 16),
789 (char) (status >> 8),
791 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
793 return MMSYSERR_NOERROR;
796 /**************************************************************************
797 * wodUnprepare [internal]
799 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
803 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
805 if (wDevID >= MAX_WAVEOUTDRV) {
806 WARN("bad device ID !\n");
807 return MMSYSERR_BADDEVICEID;
810 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
811 return WAVERR_STILLPLAYING;
813 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
814 lpWaveHdr->dwFlags |= WHDR_DONE;
816 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
818 ERR("AudioOutputUnitStop return %c%c%c%c\n", (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
819 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
821 return MMSYSERR_NOERROR;
824 /**************************************************************************
825 * wodHelper_BeginWaveHdr [internal]
827 * Makes the specified lpWaveHdr the currently playing wave header.
828 * If the specified wave header is a begin loop and we're not already in
829 * a loop, setup the loop.
830 * Call from AudioUnit IO thread can't use Wine debug channels.
832 static void wodHelper_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
834 wwo->lpPlayPtr = lpWaveHdr;
841 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
845 fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
848 fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
850 wwo->lpLoopPtr = lpWaveHdr;
851 /* Windows does not touch WAVEHDR.dwLoops,
852 * so we need to make an internal copy */
853 wwo->dwLoops = lpWaveHdr->dwLoops;
856 wwo->dwPartialOffset = 0;
860 /**************************************************************************
861 * wodHelper_PlayPtrNext [internal]
863 * Advance the play pointer to the next waveheader, looping if required.
864 * Call from AudioUnit IO thread can't use Wine debug channels.
866 static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
870 pthread_mutex_lock(&wwo->lock);
872 lpWaveHdr = wwo->lpPlayPtr;
874 wwo->dwPartialOffset = 0;
875 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
877 /* We're at the end of a loop, loop if required */
878 if (--wwo->dwLoops > 0)
880 wwo->lpPlayPtr = wwo->lpLoopPtr;
883 /* Handle overlapping loops correctly */
884 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
885 fprintf(stderr, "trace:winecoreaudio:wodHelper_PlayPtrNext Correctly handled case ? (ending loop buffer also starts a new loop)\n");
886 /* shall we consider the END flag for the closing loop or for
887 * the opening one or for both ???
888 * code assumes for closing loop only
892 lpWaveHdr = lpWaveHdr->lpNext;
894 wwo->lpLoopPtr = NULL;
895 wodHelper_BeginWaveHdr(wwo, lpWaveHdr);
899 /* We're not in a loop. Advance to the next wave header */
900 wodHelper_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
903 pthread_mutex_unlock(&wwo->lock);
908 /* if force is TRUE then notify the client that all the headers were completed
909 * Call from AudioUnit IO thread can't use Wine debug channels.
911 static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
916 pthread_mutex_lock(&wwo->lock);
918 /* Start from lpQueuePtr and keep notifying until:
919 * - we hit an unwritten wavehdr
920 * - we hit the beginning of a running loop
921 * - we hit a wavehdr which hasn't finished playing
923 while ((lpWaveHdr = wwo->lpQueuePtr) &&
925 (lpWaveHdr != wwo->lpPlayPtr &&
926 lpWaveHdr != wwo->lpLoopPtr)))
928 wwo->lpQueuePtr = lpWaveHdr->lpNext;
930 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
931 lpWaveHdr->dwFlags |= WHDR_DONE;
933 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
936 retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr !=
937 wwo->lpLoopPtr) ? 0 : INFINITE;
939 pthread_mutex_unlock(&wwo->lock);
944 /**************************************************************************
945 * wodHelper_Reset [internal]
947 * Resets current output stream.
949 static void wodHelper_Reset(WINE_WAVEOUT* wwo, BOOL reset)
953 /* updates current notify list */
954 wodHelper_NotifyCompletions(wwo, FALSE);
958 /* remove all wave headers and notify client that all headers were completed */
959 wodHelper_NotifyCompletions(wwo, TRUE);
961 pthread_mutex_lock(&wwo->lock);
963 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
964 wwo->state = WINE_WS_STOPPED;
965 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
967 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
969 pthread_mutex_unlock(&wwo->lock);
973 pthread_mutex_lock(&wwo->lock);
976 /* complicated case, not handled yet (could imply modifying the loop counter) */
977 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
978 wwo->lpPlayPtr = wwo->lpLoopPtr;
979 wwo->dwPartialOffset = 0;
980 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
984 DWORD sz = wwo->dwPartialOffset;
986 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
987 /* compute the max size playable from lpQueuePtr */
988 for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext)
990 sz += ptr->dwBufferLength;
993 /* because the reset lpPlayPtr will be lpQueuePtr */
994 if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("doh\n");
995 wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
996 wwo->dwWrittenTotal = wwo->dwPlayedTotal;
997 wwo->lpPlayPtr = wwo->lpQueuePtr;
1000 wwo->state = WINE_WS_PAUSED;
1001 pthread_mutex_unlock(&wwo->lock);
1006 /**************************************************************************
1007 * wodWrite [internal]
1010 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1015 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1017 /* first, do the sanity checks... */
1018 if (wDevID >= MAX_WAVEOUTDRV)
1020 WARN("bad dev ID !\n");
1021 return MMSYSERR_BADDEVICEID;
1024 wwo = &WOutDev[wDevID];
1026 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1028 TRACE("unprepared\n");
1029 return WAVERR_UNPREPARED;
1032 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1034 TRACE("still playing\n");
1035 return WAVERR_STILLPLAYING;
1038 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1039 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1040 lpWaveHdr->lpNext = 0;
1042 pthread_mutex_lock(&wwo->lock);
1043 /* insert buffer at the end of queue */
1044 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1047 if (!wwo->lpPlayPtr)
1048 wodHelper_BeginWaveHdr(wwo,lpWaveHdr);
1049 if (wwo->state == WINE_WS_STOPPED)
1050 wwo->state = WINE_WS_PLAYING;
1051 pthread_mutex_unlock(&wwo->lock);
1053 return MMSYSERR_NOERROR;
1056 /**************************************************************************
1057 * wodPause [internal]
1059 static DWORD wodPause(WORD wDevID)
1061 TRACE("(%u);!\n", wDevID);
1063 if (wDevID >= MAX_WAVEOUTDRV)
1065 WARN("bad device ID !\n");
1066 return MMSYSERR_BADDEVICEID;
1069 if (WOutDev[wDevID].state == WINE_WS_PLAYING)
1071 pthread_mutex_lock(&WOutDev[wDevID].lock);
1072 WOutDev[wDevID].state = WINE_WS_PAUSED;
1073 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1076 return MMSYSERR_NOERROR;
1079 /**************************************************************************
1080 * wodRestart [internal]
1082 static DWORD wodRestart(WORD wDevID)
1084 TRACE("(%u);\n", wDevID);
1086 if (wDevID >= MAX_WAVEOUTDRV )
1088 WARN("bad device ID !\n");
1089 return MMSYSERR_BADDEVICEID;
1092 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1094 pthread_mutex_lock(&WOutDev[wDevID].lock);
1095 WOutDev[wDevID].state = WINE_WS_PLAYING;
1096 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1099 return MMSYSERR_NOERROR;
1102 /**************************************************************************
1103 * wodReset [internal]
1105 static DWORD wodReset(WORD wDevID)
1107 TRACE("(%u);\n", wDevID);
1109 if (wDevID >= MAX_WAVEOUTDRV)
1111 WARN("bad device ID !\n");
1112 return MMSYSERR_BADDEVICEID;
1115 wodHelper_Reset(&WOutDev[wDevID], TRUE);
1117 return MMSYSERR_NOERROR;
1120 /**************************************************************************
1121 * wodGetPosition [internal]
1123 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1129 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1131 if (wDevID >= MAX_WAVEOUTDRV)
1133 WARN("bad device ID !\n");
1134 return MMSYSERR_BADDEVICEID;
1137 /* if null pointer to time structure return error */
1138 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1140 wwo = &WOutDev[wDevID];
1142 pthread_mutex_lock(&WOutDev[wDevID].lock);
1143 val = wwo->dwPlayedTotal;
1144 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1146 return bytes_to_mmtime(lpTime, val, &wwo->format);
1149 /**************************************************************************
1150 * wodBreakLoop [internal]
1152 static DWORD wodBreakLoop(WORD wDevID)
1154 FIXME("(%u);\n", wDevID);
1156 if (wDevID >= MAX_WAVEOUTDRV)
1158 WARN("bad device ID !\n");
1159 return MMSYSERR_BADDEVICEID;
1161 pthread_mutex_lock(&WOutDev[wDevID].lock);
1163 if (WOutDev[wDevID].state == WINE_WS_PLAYING && WOutDev[wDevID].lpLoopPtr != NULL)
1165 /* ensure exit at end of current loop */
1166 WOutDev[wDevID].dwLoops = 1;
1169 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1171 return MMSYSERR_NOERROR;
1174 /**************************************************************************
1175 * wodGetVolume [internal]
1177 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1182 if (wDevID >= MAX_WAVEOUTDRV)
1184 WARN("bad device ID !\n");
1185 return MMSYSERR_BADDEVICEID;
1188 TRACE("(%u, %08lX);\n", wDevID, lpdwVol);
1190 pthread_mutex_lock(&WOutDev[wDevID].lock);
1192 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1194 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1196 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1198 return MMSYSERR_NOERROR;
1201 /**************************************************************************
1202 * wodSetVolume [internal]
1204 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1209 if (wDevID >= MAX_WAVEOUTDRV)
1211 WARN("bad device ID !\n");
1212 return MMSYSERR_BADDEVICEID;
1215 left = LOWORD(dwParam) / 65535.0f;
1216 right = HIWORD(dwParam) / 65535.0f;
1218 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1220 pthread_mutex_lock(&WOutDev[wDevID].lock);
1222 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1224 pthread_mutex_unlock(&WOutDev[wDevID].lock);
1226 return MMSYSERR_NOERROR;
1229 /**************************************************************************
1230 * wodGetNumDevs [internal]
1232 static DWORD wodGetNumDevs(void)
1235 return MAX_WAVEOUTDRV;
1238 /**************************************************************************
1239 * wodDevInterfaceSize [internal]
1241 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1243 TRACE("(%u, %p)\n", wDevID, dwParam1);
1245 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1246 NULL, 0 ) * sizeof(WCHAR);
1247 return MMSYSERR_NOERROR;
1250 /**************************************************************************
1251 * wodDevInterface [internal]
1253 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1256 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1257 NULL, 0 ) * sizeof(WCHAR))
1259 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1260 dwParam1, dwParam2 / sizeof(WCHAR));
1261 return MMSYSERR_NOERROR;
1263 return MMSYSERR_INVALPARAM;
1266 /**************************************************************************
1267 * wodMessage (WINEJACK.7)
1269 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1270 DWORD dwParam1, DWORD dwParam2)
1272 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
1273 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1281 // FIXME: Pretend this is supported //
1283 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1284 case WODM_CLOSE: return wodClose(wDevID);
1285 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1286 case WODM_PAUSE: return wodPause(wDevID);
1287 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1288 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1289 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1290 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1292 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1293 case WODM_GETNUMDEVS: return wodGetNumDevs();
1297 case WODM_GETPLAYBACKRATE:
1298 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1299 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1300 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1301 case WODM_RESTART: return wodRestart(wDevID);
1302 case WODM_RESET: return wodReset(wDevID);
1304 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1305 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1306 case DRV_QUERYDSOUNDIFACE:
1307 case DRV_QUERYDSOUNDDESC:
1308 return MMSYSERR_NOTSUPPORTED;
1311 FIXME("unknown message %d!\n", wMsg);
1314 return MMSYSERR_NOTSUPPORTED;
1317 /*======================================================================*
1318 * Low level DSOUND implementation *
1319 *======================================================================*/
1321 typedef struct IDsDriverImpl IDsDriverImpl;
1322 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1324 struct IDsDriverImpl
1326 /* IUnknown fields */
1327 const IDsDriverVtbl *lpVtbl;
1329 /* IDsDriverImpl fields */
1331 IDsDriverBufferImpl*primary;
1334 struct IDsDriverBufferImpl
1336 /* IUnknown fields */
1337 const IDsDriverBufferVtbl *lpVtbl;
1339 /* IDsDriverBufferImpl fields */
1344 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1346 /* we can't perform memory mapping as we don't have a file stream
1347 interface with jack like we do with oss */
1348 MESSAGE("This sound card's driver does not support direct access\n");
1349 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1350 return MMSYSERR_NOTSUPPORTED;
1353 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
1355 memset(desc, 0, sizeof(*desc));
1356 strcpy(desc->szDesc, "Wine CoreAudio DirectSound Driver");
1357 strcpy(desc->szDrvname, "winecoreaudio.drv");
1358 return MMSYSERR_NOERROR;
1363 CoreAudio IO threaded callback,
1364 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1366 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1367 AudioUnitRenderActionFlags *ioActionFlags,
1368 const AudioTimeStamp *inTimeStamp,
1370 UInt32 inNumberFrames,
1371 AudioBufferList *ioData)
1374 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1378 pthread_mutex_lock(&wwo->lock);
1379 if(wwo->state == WINE_WS_PLAYING)
1381 unsigned int available;
1382 unsigned int count = ioData->mBuffers[0].mDataByteSize;
1384 available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1386 for (channel = 0; channel < ioData->mNumberBuffers; channel++)
1388 if ( available >= count)
1390 memcpy(ioData->mBuffers[channel].mData, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, count);
1391 wwo->dwPartialOffset += count;
1392 wwo->dwPlayedTotal += count;
1397 memcpy(ioData->mBuffers[channel].mData, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, available);
1398 wwo->dwPartialOffset += available;
1399 wwo->dwPlayedTotal += available;
1401 /* Fill with silence */
1402 for (s = available; s < count; s++)
1403 ((int *)ioData->mBuffers[channel].mData)[s] = 0;
1412 for (channel = 0; channel < ioData->mNumberBuffers; channel++)
1413 memset(ioData->mBuffers[channel].mData, 0, ioData->mBuffers[channel].mDataByteSize);
1415 pthread_mutex_unlock(&wwo->lock);
1417 if (nextPtr) wodHelper_PlayPtrNext(wwo);
1418 if (needNotify) wodHelper_NotifyCompletions(wwo, FALSE);
1423 /**************************************************************************
1424 * wodMessage (WINECOREAUDIO.7)
1426 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1427 DWORD dwParam1, DWORD dwParam2)
1429 FIXME("(%u, %04X, %08lX, %08lX, %08lX): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1430 return MMSYSERR_NOTENABLED;