dpwsockx: Implementation of SPInit
[wine/gsoc_dplay.git] / dlls / winecoreaudio.drv / audio.c
blob10b66d00e18efde6c3360d5b257646e4dba20113
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 #include <assert.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "wingdi.h"
41 #include "winerror.h"
42 #include "mmddk.h"
43 #include "dsound.h"
44 #include "dsdriver.h"
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>
56 #include <libkern/OSAtomic.h>
59 Due to AudioUnit headers conflict define some needed types.
62 typedef void *AudioUnit;
64 /* From AudioUnit/AUComponents.h */
65 enum
67 kAudioUnitRenderAction_OutputIsSilence = (1 << 4),
68 /* provides hint on return from Render(): if set the buffer contains all zeroes */
70 typedef UInt32 AudioUnitRenderActionFlags;
72 typedef long ComponentResult;
73 extern ComponentResult
74 AudioUnitRender( AudioUnit ci,
75 AudioUnitRenderActionFlags * ioActionFlags,
76 const AudioTimeStamp * inTimeStamp,
77 UInt32 inOutputBusNumber,
78 UInt32 inNumberFrames,
79 AudioBufferList * ioData) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER;
81 /* only allow 10 output devices through this driver, this ought to be adequate */
82 #define MAX_WAVEOUTDRV (1)
83 #define MAX_WAVEINDRV (1)
85 /* state diagram for waveOut writing:
87 * +---------+-------------+---------------+---------------------------------+
88 * | state | function | event | new state |
89 * +---------+-------------+---------------+---------------------------------+
90 * | | open() | | PLAYING |
91 * | PAUSED | write() | | PAUSED |
92 * | PLAYING | write() | HEADER | PLAYING |
93 * | (other) | write() | <error> | |
94 * | (any) | pause() | PAUSING | PAUSED |
95 * | PAUSED | restart() | RESTARTING | PLAYING |
96 * | (any) | reset() | RESETTING | PLAYING |
97 * | (any) | close() | CLOSING | CLOSED |
98 * +---------+-------------+---------------+---------------------------------+
101 /* states of the playing device */
102 #define WINE_WS_PLAYING 0 /* for waveOut: lpPlayPtr == NULL -> stopped */
103 #define WINE_WS_PAUSED 1
104 #define WINE_WS_STOPPED 2 /* Not used for waveOut */
105 #define WINE_WS_CLOSED 3
106 #define WINE_WS_OPENING 4
107 #define WINE_WS_CLOSING 5
109 typedef struct tagCoreAudio_Device {
110 char dev_name[32];
111 char mixer_name[32];
112 unsigned open_count;
113 char* interface_name;
115 WAVEOUTCAPSW out_caps;
116 WAVEINCAPSW in_caps;
117 DWORD in_caps_support;
118 int sample_rate;
119 int stereo;
120 int format;
121 unsigned audio_fragment;
122 BOOL full_duplex;
123 BOOL bTriggerSupport;
124 BOOL bOutputEnabled;
125 BOOL bInputEnabled;
126 DSDRIVERDESC ds_desc;
127 DSDRIVERCAPS ds_caps;
128 DSCDRIVERCAPS dsc_caps;
129 GUID ds_guid;
130 GUID dsc_guid;
132 AudioDeviceID outputDeviceID;
133 AudioDeviceID inputDeviceID;
134 AudioStreamBasicDescription streamDescription;
135 } CoreAudio_Device;
137 /* for now use the default device */
138 static CoreAudio_Device CoreAudio_DefaultDevice;
140 typedef struct {
141 volatile int state; /* one of the WINE_WS_ manifest constants */
142 CoreAudio_Device *cadev;
143 WAVEOPENDESC waveDesc;
144 WORD wFlags;
145 PCMWAVEFORMAT format;
146 DWORD woID;
147 AudioUnit audioUnit;
148 AudioStreamBasicDescription streamDescription;
150 WAVEOUTCAPSW caps;
151 char interface_name[32];
152 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
153 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
154 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
156 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
157 DWORD dwLoops; /* private copy of loop counter */
159 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
160 DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */
162 DWORD tickCountMS; /* time in MS of last AudioUnit callback */
164 OSSpinLock lock; /* synchronization stuff */
166 BOOL trace_on;
167 BOOL warn_on;
168 BOOL err_on;
169 } WINE_WAVEOUT;
171 typedef struct {
172 /* This device's device number */
173 DWORD wiID;
175 /* Access to the following fields is synchronized across threads. */
176 volatile int state;
177 LPWAVEHDR lpQueuePtr;
178 DWORD dwTotalRecorded;
180 /* Synchronization mechanism to protect above fields */
181 OSSpinLock lock;
183 /* Capabilities description */
184 WAVEINCAPSW caps;
185 char interface_name[32];
187 /* Record the arguments used when opening the device. */
188 WAVEOPENDESC waveDesc;
189 WORD wFlags;
190 PCMWAVEFORMAT format;
192 AudioUnit audioUnit;
193 AudioBufferList*bufferList;
194 AudioBufferList*bufferListCopy;
196 /* Record state of debug channels at open. Used to control fprintf's since
197 * we can't use Wine debug channel calls in non-Wine AudioUnit threads. */
198 BOOL trace_on;
199 BOOL warn_on;
200 BOOL err_on;
202 /* These fields aren't used. */
203 #if 0
204 CoreAudio_Device *cadev;
206 AudioStreamBasicDescription streamDescription;
207 #endif
208 } WINE_WAVEIN;
210 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
211 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
213 static HANDLE hThread = NULL; /* Track the thread we create so we can clean it up later */
214 static CFMessagePortRef Port_SendToMessageThread;
216 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
217 static void wodHelper_NotifyDoneForList(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr);
218 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
219 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi);
221 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
222 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
223 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
225 extern OSStatus AudioOutputUnitStart(AudioUnit au);
226 extern OSStatus AudioOutputUnitStop(AudioUnit au);
227 extern OSStatus AudioUnitUninitialize(AudioUnit au);
229 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
230 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
232 extern int AudioUnit_GetInputDeviceSampleRate(void);
234 extern int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
235 WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
236 UInt32* outFrameCount);
238 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
239 AudioUnitRenderActionFlags *ioActionFlags,
240 const AudioTimeStamp *inTimeStamp,
241 UInt32 inBusNumber,
242 UInt32 inNumberFrames,
243 AudioBufferList *ioData);
244 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
245 AudioUnitRenderActionFlags *ioActionFlags,
246 const AudioTimeStamp *inTimeStamp,
247 UInt32 inBusNumber,
248 UInt32 inNumberFrames,
249 AudioBufferList *ioData);
251 /* These strings used only for tracing */
253 static const char * getMessage(UINT msg)
255 static char unknown[32];
256 #define MSG_TO_STR(x) case x: return #x
257 switch(msg) {
258 MSG_TO_STR(DRVM_INIT);
259 MSG_TO_STR(DRVM_EXIT);
260 MSG_TO_STR(DRVM_ENABLE);
261 MSG_TO_STR(DRVM_DISABLE);
262 MSG_TO_STR(WIDM_OPEN);
263 MSG_TO_STR(WIDM_CLOSE);
264 MSG_TO_STR(WIDM_ADDBUFFER);
265 MSG_TO_STR(WIDM_PREPARE);
266 MSG_TO_STR(WIDM_UNPREPARE);
267 MSG_TO_STR(WIDM_GETDEVCAPS);
268 MSG_TO_STR(WIDM_GETNUMDEVS);
269 MSG_TO_STR(WIDM_GETPOS);
270 MSG_TO_STR(WIDM_RESET);
271 MSG_TO_STR(WIDM_START);
272 MSG_TO_STR(WIDM_STOP);
273 MSG_TO_STR(WODM_OPEN);
274 MSG_TO_STR(WODM_CLOSE);
275 MSG_TO_STR(WODM_WRITE);
276 MSG_TO_STR(WODM_PAUSE);
277 MSG_TO_STR(WODM_GETPOS);
278 MSG_TO_STR(WODM_BREAKLOOP);
279 MSG_TO_STR(WODM_PREPARE);
280 MSG_TO_STR(WODM_UNPREPARE);
281 MSG_TO_STR(WODM_GETDEVCAPS);
282 MSG_TO_STR(WODM_GETNUMDEVS);
283 MSG_TO_STR(WODM_GETPITCH);
284 MSG_TO_STR(WODM_SETPITCH);
285 MSG_TO_STR(WODM_GETPLAYBACKRATE);
286 MSG_TO_STR(WODM_SETPLAYBACKRATE);
287 MSG_TO_STR(WODM_GETVOLUME);
288 MSG_TO_STR(WODM_SETVOLUME);
289 MSG_TO_STR(WODM_RESTART);
290 MSG_TO_STR(WODM_RESET);
291 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
292 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
293 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
294 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
296 #undef MSG_TO_STR
297 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
298 return unknown;
301 #define kStopLoopMessage 0
302 #define kWaveOutNotifyCompletionsMessage 1
303 #define kWaveInNotifyCompletionsMessage 2
305 /* Mach Message Handling */
306 static CFDataRef wodMessageHandler(CFMessagePortRef port_ReceiveInMessageThread, SInt32 msgid, CFDataRef data, void *info)
308 UInt32 *buffer = NULL;
310 switch (msgid)
312 case kWaveOutNotifyCompletionsMessage:
313 buffer = (UInt32 *) CFDataGetBytePtr(data);
314 wodHelper_NotifyCompletions(&WOutDev[buffer[0]], FALSE);
315 break;
316 case kWaveInNotifyCompletionsMessage:
317 buffer = (UInt32 *) CFDataGetBytePtr(data);
318 widHelper_NotifyCompletions(&WInDev[buffer[0]]);
319 break;
320 default:
321 CFRunLoopStop(CFRunLoopGetCurrent());
322 break;
325 return NULL;
328 static DWORD WINAPI messageThread(LPVOID p)
330 CFMessagePortRef port_ReceiveInMessageThread = (CFMessagePortRef) p;
331 CFRunLoopSourceRef source;
333 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port_ReceiveInMessageThread, (CFIndex)0);
334 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
336 CFRunLoopRun();
338 CFRunLoopSourceInvalidate(source);
339 CFRelease(source);
340 CFRelease(port_ReceiveInMessageThread);
342 return 0;
345 /**************************************************************************
346 * wodSendNotifyCompletionsMessage [internal]
347 * Call from AudioUnit IO thread can't use Wine debug channels.
349 static void wodSendNotifyCompletionsMessage(WINE_WAVEOUT* wwo)
351 CFDataRef data;
352 UInt32 buffer;
354 if (!Port_SendToMessageThread)
355 return;
357 buffer = (UInt32) wwo->woID;
359 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
360 if (!data)
361 return;
363 CFMessagePortSendRequest(Port_SendToMessageThread, kWaveOutNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
364 CFRelease(data);
367 /**************************************************************************
368 * wodSendNotifyInputCompletionsMessage [internal]
369 * Call from AudioUnit IO thread can't use Wine debug channels.
371 static void wodSendNotifyInputCompletionsMessage(WINE_WAVEIN* wwi)
373 CFDataRef data;
374 UInt32 buffer;
376 if (!Port_SendToMessageThread)
377 return;
379 buffer = (UInt32) wwi->wiID;
381 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
382 if (!data)
383 return;
385 CFMessagePortSendRequest(Port_SendToMessageThread, kWaveInNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
386 CFRelease(data);
389 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
390 PCMWAVEFORMAT* format)
392 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
393 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
394 format->wf.nChannels, format->wf.nAvgBytesPerSec);
395 TRACE("Position in bytes=%u\n", position);
397 switch (lpTime->wType) {
398 case TIME_SAMPLES:
399 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
400 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
401 break;
402 case TIME_MS:
403 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
404 TRACE("TIME_MS=%u\n", lpTime->u.ms);
405 break;
406 case TIME_SMPTE:
407 lpTime->u.smpte.fps = 30;
408 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
409 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
410 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
411 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
412 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
413 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
414 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
415 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
416 lpTime->u.smpte.fps = 30;
417 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
418 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
419 lpTime->u.smpte.hour, lpTime->u.smpte.min,
420 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
421 break;
422 default:
423 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
424 lpTime->wType = TIME_BYTES;
425 /* fall through */
426 case TIME_BYTES:
427 lpTime->u.cb = position;
428 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
429 break;
431 return MMSYSERR_NOERROR;
434 /**************************************************************************
435 * CoreAudio_GetDevCaps [internal]
437 BOOL CoreAudio_GetDevCaps (void)
439 OSStatus status;
440 UInt32 propertySize;
441 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
443 char name[MAXPNAMELEN];
445 propertySize = MAXPNAMELEN;
446 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
447 if (status) {
448 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
449 (char) (status >> 16),
450 (char) (status >> 8),
451 (char) status);
452 return FALSE;
455 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
456 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
457 MultiByteToWideChar(CP_UNIXCP, 0, name, sizeof(name),
458 CoreAudio_DefaultDevice.out_caps.szPname,
459 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
460 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
462 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
463 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
464 if (status != noErr) {
465 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
466 (char) (status >> 16),
467 (char) (status >> 8),
468 (char) status);
469 return FALSE;
472 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
473 "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
474 "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
475 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
476 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
477 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
478 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
479 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
480 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
481 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
482 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
483 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
484 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
485 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
487 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
488 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
490 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
491 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
492 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
493 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
494 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
496 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
497 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
499 return TRUE;
502 /******************************************************************
503 * CoreAudio_WaveInit
505 * Initialize CoreAudio_DefaultDevice
507 LONG CoreAudio_WaveInit(void)
509 OSStatus status;
510 UInt32 propertySize;
511 int i;
512 CFStringRef messageThreadPortName;
513 CFMessagePortRef port_ReceiveInMessageThread;
514 int inputSampleRate;
516 TRACE("()\n");
518 /* number of sound cards */
519 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
520 propertySize /= sizeof(AudioDeviceID);
521 TRACE("sound cards : %lu\n", propertySize);
523 /* Get the output device */
524 propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
525 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
526 if (status) {
527 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
528 (char) (status >> 16),
529 (char) (status >> 8),
530 (char) status);
531 return DRV_FAILURE;
533 if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
534 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
535 return DRV_FAILURE;
538 if ( ! CoreAudio_GetDevCaps() )
539 return DRV_FAILURE;
541 CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
542 strcpy(CoreAudio_DefaultDevice.interface_name, CoreAudio_DefaultDevice.dev_name);
544 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
546 static const WCHAR wszWaveOutFormat[] =
547 {'C','o','r','e','A','u','d','i','o',' ','W','a','v','e','O','u','t',' ','%','d',0};
549 WOutDev[i].state = WINE_WS_CLOSED;
550 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
551 WOutDev[i].woID = i;
553 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
555 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
556 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
557 snprintfW(WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR), wszWaveOutFormat, i);
558 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
560 WOutDev[i].caps.vDriverVersion = 0x0001;
561 WOutDev[i].caps.dwFormats = 0x00000000;
562 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
564 WOutDev[i].caps.wChannels = 2;
565 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
567 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
568 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
569 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
570 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
571 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
572 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
573 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
574 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
575 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
576 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
577 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
578 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
579 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
580 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
581 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
582 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
583 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
584 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
585 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
586 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
588 WOutDev[i].lock = 0; /* initialize the mutex */
591 /* FIXME: implement sample rate conversion on input */
592 inputSampleRate = AudioUnit_GetInputDeviceSampleRate();
594 for (i = 0; i < MAX_WAVEINDRV; ++i)
596 static const WCHAR wszWaveInFormat[] =
597 {'C','o','r','e','A','u','d','i','o',' ','W','a','v','e','I','n',' ','%','d',0};
599 memset(&WInDev[i], 0, sizeof(WInDev[i]));
600 WInDev[i].wiID = i;
602 /* Establish preconditions for widOpen */
603 WInDev[i].state = WINE_WS_CLOSED;
604 WInDev[i].lock = 0; /* initialize the mutex */
606 /* Fill in capabilities. widGetDevCaps can be called at any time. */
607 WInDev[i].caps.wMid = 0xcafe; /* Manufac ID */
608 WInDev[i].caps.wPid = 0x0001; /* Product ID */
609 WInDev[i].caps.vDriverVersion = 0x0001;
611 snprintfW(WInDev[i].caps.szPname, sizeof(WInDev[i].caps.szPname)/sizeof(WCHAR), wszWaveInFormat, i);
612 snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "winecoreaudio in: %d", i);
614 if (inputSampleRate == 96000)
616 WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
617 WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
618 WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
619 WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
621 if (inputSampleRate == 48000)
623 WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
624 WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
625 WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
626 WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
628 if (inputSampleRate == 44100)
630 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
631 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
632 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
633 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
635 if (inputSampleRate == 22050)
637 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
638 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
639 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
640 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
642 if (inputSampleRate == 11025)
644 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
645 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
646 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
647 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
650 WInDev[i].caps.wChannels = 2;
653 /* create mach messages handler */
654 srandomdev();
655 messageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
656 CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
657 if (!messageThreadPortName)
659 ERR("Can't create message thread port name\n");
660 return DRV_FAILURE;
663 port_ReceiveInMessageThread = CFMessagePortCreateLocal(kCFAllocatorDefault, messageThreadPortName,
664 &wodMessageHandler, NULL, NULL);
665 if (!port_ReceiveInMessageThread)
667 ERR("Can't create message thread local port\n");
668 CFRelease(messageThreadPortName);
669 return DRV_FAILURE;
672 Port_SendToMessageThread = CFMessagePortCreateRemote(kCFAllocatorDefault, messageThreadPortName);
673 CFRelease(messageThreadPortName);
674 if (!Port_SendToMessageThread)
676 ERR("Can't create port for sending to message thread\n");
677 CFRelease(port_ReceiveInMessageThread);
678 return DRV_FAILURE;
681 /* Cannot WAIT for any events because we are called from the loader (which has a lock on loading stuff) */
682 /* We might want to wait for this thread to be created -- but we cannot -- not here at least */
683 /* Instead track the thread so we can clean it up later */
684 if ( hThread )
686 ERR("Message thread already started -- expect problems\n");
688 hThread = CreateThread(NULL, 0, messageThread, (LPVOID)port_ReceiveInMessageThread, 0, NULL);
689 if ( !hThread )
691 ERR("Can't create message thread\n");
692 CFRelease(port_ReceiveInMessageThread);
693 CFRelease(Port_SendToMessageThread);
694 Port_SendToMessageThread = NULL;
695 return DRV_FAILURE;
698 /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
700 return DRV_SUCCESS;
703 void CoreAudio_WaveRelease(void)
705 /* Stop CFRunLoop in messageThread */
706 TRACE("()\n");
708 if (!Port_SendToMessageThread)
709 return;
711 CFMessagePortSendRequest(Port_SendToMessageThread, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
712 CFRelease(Port_SendToMessageThread);
713 Port_SendToMessageThread = NULL;
715 /* Wait for the thread to finish and clean it up */
716 /* This rids us of any quick start/shutdown driver crashes */
717 WaitForSingleObject(hThread, INFINITE);
718 CloseHandle(hThread);
719 hThread = NULL;
722 /*======================================================================*
723 * Low level WAVE OUT implementation *
724 *======================================================================*/
726 /**************************************************************************
727 * wodNotifyClient [internal]
729 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
731 switch (wMsg) {
732 case WOM_OPEN:
733 case WOM_CLOSE:
734 case WOM_DONE:
735 if (wwo->wFlags != DCB_NULL &&
736 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
737 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
738 dwParam1, dwParam2))
740 return MMSYSERR_ERROR;
742 break;
743 default:
744 return MMSYSERR_INVALPARAM;
746 return MMSYSERR_NOERROR;
750 /**************************************************************************
751 * wodGetDevCaps [internal]
753 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
755 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
757 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
759 if (wDevID >= MAX_WAVEOUTDRV)
761 TRACE("MAX_WAVOUTDRV reached !\n");
762 return MMSYSERR_BADDEVICEID;
765 TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
766 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
767 return MMSYSERR_NOERROR;
770 /**************************************************************************
771 * wodOpen [internal]
773 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
775 WINE_WAVEOUT* wwo;
776 DWORD ret;
777 AudioStreamBasicDescription streamFormat;
778 AudioUnit audioUnit = NULL;
779 BOOL auInited = FALSE;
781 TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
782 if (lpDesc == NULL)
784 WARN("Invalid Parameter !\n");
785 return MMSYSERR_INVALPARAM;
787 if (wDevID >= MAX_WAVEOUTDRV) {
788 TRACE("MAX_WAVOUTDRV reached !\n");
789 return MMSYSERR_BADDEVICEID;
792 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
793 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
794 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
796 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
797 lpDesc->lpFormat->nChannels == 0 ||
798 lpDesc->lpFormat->nSamplesPerSec == 0
801 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
802 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
803 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
804 return WAVERR_BADFORMAT;
807 if (dwFlags & WAVE_FORMAT_QUERY)
809 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
810 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
811 lpDesc->lpFormat->nSamplesPerSec);
812 return MMSYSERR_NOERROR;
815 /* We proceed in three phases:
816 * o Reserve the device for us, marking it as unavailable (not closed)
817 * o Create, configure, and start the Audio Unit. To avoid deadlock,
818 * this has to be done without holding wwo->lock.
819 * o If that was successful, finish setting up our device info and
820 * mark the device as ready.
821 * Otherwise, clean up and mark the device as available.
823 wwo = &WOutDev[wDevID];
824 if (!OSSpinLockTry(&wwo->lock))
825 return MMSYSERR_ALLOCATED;
827 if (wwo->state != WINE_WS_CLOSED)
829 OSSpinLockUnlock(&wwo->lock);
830 return MMSYSERR_ALLOCATED;
833 wwo->state = WINE_WS_OPENING;
834 wwo->audioUnit = NULL;
835 OSSpinLockUnlock(&wwo->lock);
838 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &audioUnit))
840 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
841 ret = MMSYSERR_ERROR;
842 goto error;
845 streamFormat.mFormatID = kAudioFormatLinearPCM;
846 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
847 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
848 if (lpDesc->lpFormat->wBitsPerSample != 8)
849 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
850 # ifdef WORDS_BIGENDIAN
851 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
852 # endif
854 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
855 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
856 streamFormat.mFramesPerPacket = 1;
857 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
858 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
859 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
861 ret = AudioUnit_InitializeWithStreamDescription(audioUnit, &streamFormat);
862 if (!ret)
864 ret = WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
865 goto error;
867 auInited = TRUE;
869 /* Our render callback CoreAudio_woAudioUnitIOProc may be called before
870 * AudioOutputUnitStart returns. Core Audio will grab its own internal
871 * lock before calling it and the callback grabs wwo->lock. This would
872 * deadlock if we were holding wwo->lock.
873 * Also, the callback has to safely do nothing in that case, because
874 * wwo hasn't been completely filled out, yet. */
875 ret = AudioOutputUnitStart(audioUnit);
876 if (ret)
878 ERR("AudioOutputUnitStart failed: %08x\n", ret);
879 ret = MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
880 goto error;
884 OSSpinLockLock(&wwo->lock);
885 assert(wwo->state == WINE_WS_OPENING);
887 wwo->audioUnit = audioUnit;
888 wwo->streamDescription = streamFormat;
890 wwo->state = WINE_WS_PLAYING;
892 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
894 wwo->waveDesc = *lpDesc;
895 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
897 if (wwo->format.wBitsPerSample == 0) {
898 WARN("Resetting zeroed wBitsPerSample\n");
899 wwo->format.wBitsPerSample = 8 *
900 (wwo->format.wf.nAvgBytesPerSec /
901 wwo->format.wf.nSamplesPerSec) /
902 wwo->format.wf.nChannels;
905 wwo->dwPlayedTotal = 0;
906 wwo->dwWrittenTotal = 0;
908 wwo->trace_on = TRACE_ON(wave);
909 wwo->warn_on = WARN_ON(wave);
910 wwo->err_on = ERR_ON(wave);
912 OSSpinLockUnlock(&wwo->lock);
914 ret = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
916 return ret;
918 error:
919 if (audioUnit)
921 if (auInited)
922 AudioUnitUninitialize(audioUnit);
923 AudioUnit_CloseAudioUnit(audioUnit);
926 OSSpinLockLock(&wwo->lock);
927 assert(wwo->state == WINE_WS_OPENING);
928 wwo->state = WINE_WS_CLOSED;
929 OSSpinLockUnlock(&wwo->lock);
931 return ret;
934 /**************************************************************************
935 * wodClose [internal]
937 static DWORD wodClose(WORD wDevID)
939 DWORD ret = MMSYSERR_NOERROR;
940 WINE_WAVEOUT* wwo;
942 TRACE("(%u);\n", wDevID);
944 if (wDevID >= MAX_WAVEOUTDRV)
946 WARN("bad device ID !\n");
947 return MMSYSERR_BADDEVICEID;
950 wwo = &WOutDev[wDevID];
951 OSSpinLockLock(&wwo->lock);
952 if (wwo->lpQueuePtr)
954 WARN("buffers still playing !\n");
955 OSSpinLockUnlock(&wwo->lock);
956 ret = WAVERR_STILLPLAYING;
957 } else
959 OSStatus err;
960 AudioUnit audioUnit = wwo->audioUnit;
962 /* sanity check: this should not happen since the device must have been reset before */
963 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
965 wwo->state = WINE_WS_CLOSING; /* mark the device as closing */
966 wwo->audioUnit = NULL;
968 OSSpinLockUnlock(&wwo->lock);
970 err = AudioUnitUninitialize(audioUnit);
971 if (err) {
972 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
973 (char) (err >> 16),
974 (char) (err >> 8),
975 (char) err);
976 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
979 if ( !AudioUnit_CloseAudioUnit(audioUnit) )
981 ERR("Can't close AudioUnit\n");
982 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
985 OSSpinLockLock(&wwo->lock);
986 assert(wwo->state == WINE_WS_CLOSING);
987 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
988 OSSpinLockUnlock(&wwo->lock);
990 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
993 return ret;
996 /**************************************************************************
997 * wodPrepare [internal]
999 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1001 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
1003 if (wDevID >= MAX_WAVEOUTDRV) {
1004 WARN("bad device ID !\n");
1005 return MMSYSERR_BADDEVICEID;
1008 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1009 return WAVERR_STILLPLAYING;
1011 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1012 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1014 return MMSYSERR_NOERROR;
1017 /**************************************************************************
1018 * wodUnprepare [internal]
1020 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1022 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
1024 if (wDevID >= MAX_WAVEOUTDRV) {
1025 WARN("bad device ID !\n");
1026 return MMSYSERR_BADDEVICEID;
1029 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1030 return WAVERR_STILLPLAYING;
1032 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1033 lpWaveHdr->dwFlags |= WHDR_DONE;
1035 return MMSYSERR_NOERROR;
1039 /**************************************************************************
1040 * wodHelper_CheckForLoopBegin [internal]
1042 * Check if the new waveheader is the beginning of a loop, and set up
1043 * state if so.
1044 * This is called with the WAVEOUT lock held.
1045 * Call from AudioUnit IO thread can't use Wine debug channels.
1047 static void wodHelper_CheckForLoopBegin(WINE_WAVEOUT* wwo)
1049 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1051 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
1053 if (wwo->lpLoopPtr)
1055 if (wwo->warn_on)
1056 fprintf(stderr, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1058 else
1060 if (wwo->trace_on)
1061 fprintf(stderr, "trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1063 wwo->lpLoopPtr = lpWaveHdr;
1064 /* Windows does not touch WAVEHDR.dwLoops,
1065 * so we need to make an internal copy */
1066 wwo->dwLoops = lpWaveHdr->dwLoops;
1072 /**************************************************************************
1073 * wodHelper_PlayPtrNext [internal]
1075 * Advance the play pointer to the next waveheader, looping if required.
1076 * This is called with the WAVEOUT lock held.
1077 * Call from AudioUnit IO thread can't use Wine debug channels.
1079 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
1081 BOOL didLoopBack = FALSE;
1083 wwo->dwPartialOffset = 0;
1084 if ((wwo->lpPlayPtr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
1086 /* We're at the end of a loop, loop if required */
1087 if (wwo->dwLoops > 1)
1089 wwo->dwLoops--;
1090 wwo->lpPlayPtr = wwo->lpLoopPtr;
1091 didLoopBack = TRUE;
1093 else
1095 wwo->lpLoopPtr = NULL;
1098 if (!didLoopBack)
1100 /* We didn't loop back. Advance to the next wave header */
1101 wwo->lpPlayPtr = wwo->lpPlayPtr->lpNext;
1103 if (wwo->lpPlayPtr)
1104 wodHelper_CheckForLoopBegin(wwo);
1108 /* Send the "done" notification for each WAVEHDR in a list. The list must be
1109 * free-standing. It should not be part of a device's queue.
1110 * This function must be called with the WAVEOUT lock *not* held. Furthermore,
1111 * it does not lock it, itself. That's because the callback to the application
1112 * may prompt the application to operate on the device, and we don't want to
1113 * deadlock.
1115 static void wodHelper_NotifyDoneForList(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1117 while (lpWaveHdr)
1119 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1121 lpWaveHdr->lpNext = NULL;
1122 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1123 lpWaveHdr->dwFlags |= WHDR_DONE;
1124 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1126 lpWaveHdr = lpNext;
1130 /* if force is TRUE then notify the client that all the headers were completed
1132 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1134 LPWAVEHDR lpFirstDoneWaveHdr = NULL;
1136 OSSpinLockLock(&wwo->lock);
1138 /* First, excise all of the done headers from the queue into
1139 * a free-standing list. */
1140 if (force)
1142 lpFirstDoneWaveHdr = wwo->lpQueuePtr;
1143 wwo->lpQueuePtr = NULL;
1145 else
1147 LPWAVEHDR lpWaveHdr;
1148 LPWAVEHDR lpLastDoneWaveHdr = NULL;
1150 /* Start from lpQueuePtr and keep notifying until:
1151 * - we hit an unwritten wavehdr
1152 * - we hit the beginning of a running loop
1153 * - we hit a wavehdr which hasn't finished playing
1155 for (
1156 lpWaveHdr = wwo->lpQueuePtr;
1157 lpWaveHdr &&
1158 lpWaveHdr != wwo->lpPlayPtr &&
1159 lpWaveHdr != wwo->lpLoopPtr;
1160 lpWaveHdr = lpWaveHdr->lpNext
1163 if (!lpFirstDoneWaveHdr)
1164 lpFirstDoneWaveHdr = lpWaveHdr;
1165 lpLastDoneWaveHdr = lpWaveHdr;
1168 if (lpLastDoneWaveHdr)
1170 wwo->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1171 lpLastDoneWaveHdr->lpNext = NULL;
1175 OSSpinLockUnlock(&wwo->lock);
1177 /* Now, send the "done" notification for each header in our list. */
1178 wodHelper_NotifyDoneForList(wwo, lpFirstDoneWaveHdr);
1182 /**************************************************************************
1183 * wodWrite [internal]
1186 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1188 LPWAVEHDR*wh;
1189 WINE_WAVEOUT *wwo;
1191 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1193 /* first, do the sanity checks... */
1194 if (wDevID >= MAX_WAVEOUTDRV)
1196 WARN("bad dev ID !\n");
1197 return MMSYSERR_BADDEVICEID;
1200 wwo = &WOutDev[wDevID];
1202 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1204 TRACE("unprepared\n");
1205 return WAVERR_UNPREPARED;
1208 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1210 TRACE("still playing\n");
1211 return WAVERR_STILLPLAYING;
1214 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1215 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1216 lpWaveHdr->lpNext = 0;
1218 OSSpinLockLock(&wwo->lock);
1219 /* insert buffer at the end of queue */
1220 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1221 /* Do nothing */;
1222 *wh = lpWaveHdr;
1224 if (!wwo->lpPlayPtr)
1226 wwo->lpPlayPtr = lpWaveHdr;
1228 wodHelper_CheckForLoopBegin(wwo);
1230 wwo->dwPartialOffset = 0;
1232 OSSpinLockUnlock(&wwo->lock);
1234 return MMSYSERR_NOERROR;
1237 /**************************************************************************
1238 * wodPause [internal]
1240 static DWORD wodPause(WORD wDevID)
1242 OSStatus status;
1244 TRACE("(%u);!\n", wDevID);
1246 if (wDevID >= MAX_WAVEOUTDRV)
1248 WARN("bad device ID !\n");
1249 return MMSYSERR_BADDEVICEID;
1252 /* The order of the following operations is important since we can't hold
1253 * the mutex while we make an Audio Unit call. Stop the Audio Unit before
1254 * setting the PAUSED state. In wodRestart, the order is reversed. This
1255 * guarantees that we can't get into a situation where the state is
1256 * PLAYING but the Audio Unit isn't running. Although we can be in PAUSED
1257 * state with the Audio Unit still running, that's harmless because the
1258 * render callback will just produce silence.
1260 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1261 if (status) {
1262 WARN("AudioOutputUnitStop return %c%c%c%c\n",
1263 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1266 OSSpinLockLock(&WOutDev[wDevID].lock);
1267 if (WOutDev[wDevID].state == WINE_WS_PLAYING)
1268 WOutDev[wDevID].state = WINE_WS_PAUSED;
1269 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1271 return MMSYSERR_NOERROR;
1274 /**************************************************************************
1275 * wodRestart [internal]
1277 static DWORD wodRestart(WORD wDevID)
1279 OSStatus status;
1281 TRACE("(%u);\n", wDevID);
1283 if (wDevID >= MAX_WAVEOUTDRV )
1285 WARN("bad device ID !\n");
1286 return MMSYSERR_BADDEVICEID;
1289 /* The order of the following operations is important since we can't hold
1290 * the mutex while we make an Audio Unit call. Set the PLAYING
1291 * state before starting the Audio Unit. In wodPause, the order is
1292 * reversed. This guarantees that we can't get into a situation where
1293 * the state is PLAYING but the Audio Unit isn't running.
1294 * Although we can be in PAUSED state with the Audio Unit still running,
1295 * that's harmless because the render callback will just produce silence.
1297 OSSpinLockLock(&WOutDev[wDevID].lock);
1298 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1299 WOutDev[wDevID].state = WINE_WS_PLAYING;
1300 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1302 status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1303 if (status) {
1304 ERR("AudioOutputUnitStart return %c%c%c%c\n",
1305 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1306 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1309 return MMSYSERR_NOERROR;
1312 /**************************************************************************
1313 * wodReset [internal]
1315 static DWORD wodReset(WORD wDevID)
1317 WINE_WAVEOUT* wwo;
1318 OSStatus status;
1319 LPWAVEHDR lpSavedQueuePtr;
1321 TRACE("(%u);\n", wDevID);
1323 if (wDevID >= MAX_WAVEOUTDRV)
1325 WARN("bad device ID !\n");
1326 return MMSYSERR_BADDEVICEID;
1329 wwo = &WOutDev[wDevID];
1331 OSSpinLockLock(&wwo->lock);
1333 if (wwo->state == WINE_WS_CLOSED || wwo->state == WINE_WS_CLOSING ||
1334 wwo->state == WINE_WS_OPENING)
1336 OSSpinLockUnlock(&wwo->lock);
1337 WARN("resetting a closed device\n");
1338 return MMSYSERR_INVALHANDLE;
1341 lpSavedQueuePtr = wwo->lpQueuePtr;
1342 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1343 wwo->state = WINE_WS_PLAYING;
1344 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1346 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
1348 OSSpinLockUnlock(&wwo->lock);
1350 status = AudioOutputUnitStart(wwo->audioUnit);
1352 if (status) {
1353 ERR( "AudioOutputUnitStart return %c%c%c%c\n",
1354 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1355 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1358 /* Now, send the "done" notification for each header in our list. */
1359 /* Do this last so the reset operation is effectively complete before the
1360 * app does whatever it's going to do in response to these notifications. */
1361 wodHelper_NotifyDoneForList(wwo, lpSavedQueuePtr);
1363 return MMSYSERR_NOERROR;
1366 /**************************************************************************
1367 * wodBreakLoop [internal]
1369 static DWORD wodBreakLoop(WORD wDevID)
1371 WINE_WAVEOUT* wwo;
1373 TRACE("(%u);\n", wDevID);
1375 if (wDevID >= MAX_WAVEOUTDRV)
1377 WARN("bad device ID !\n");
1378 return MMSYSERR_BADDEVICEID;
1381 wwo = &WOutDev[wDevID];
1383 OSSpinLockLock(&wwo->lock);
1385 if (wwo->lpLoopPtr != NULL)
1387 /* ensure exit at end of current loop */
1388 wwo->dwLoops = 1;
1391 OSSpinLockUnlock(&wwo->lock);
1393 return MMSYSERR_NOERROR;
1396 /**************************************************************************
1397 * wodGetPosition [internal]
1399 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1401 DWORD val;
1402 WINE_WAVEOUT* wwo;
1404 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1406 if (wDevID >= MAX_WAVEOUTDRV)
1408 WARN("bad device ID !\n");
1409 return MMSYSERR_BADDEVICEID;
1412 /* if null pointer to time structure return error */
1413 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1415 wwo = &WOutDev[wDevID];
1417 OSSpinLockLock(&WOutDev[wDevID].lock);
1418 val = wwo->dwPlayedTotal;
1419 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1421 return bytes_to_mmtime(lpTime, val, &wwo->format);
1424 /**************************************************************************
1425 * wodGetVolume [internal]
1427 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1429 float left;
1430 float right;
1432 if (wDevID >= MAX_WAVEOUTDRV)
1434 WARN("bad device ID !\n");
1435 return MMSYSERR_BADDEVICEID;
1438 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1440 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1442 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1444 return MMSYSERR_NOERROR;
1447 /**************************************************************************
1448 * wodSetVolume [internal]
1450 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1452 float left;
1453 float right;
1455 if (wDevID >= MAX_WAVEOUTDRV)
1457 WARN("bad device ID !\n");
1458 return MMSYSERR_BADDEVICEID;
1461 left = LOWORD(dwParam) / 65535.0f;
1462 right = HIWORD(dwParam) / 65535.0f;
1464 TRACE("(%u, %08x);\n", wDevID, dwParam);
1466 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1468 return MMSYSERR_NOERROR;
1471 /**************************************************************************
1472 * wodGetNumDevs [internal]
1474 static DWORD wodGetNumDevs(void)
1476 TRACE("\n");
1477 return MAX_WAVEOUTDRV;
1480 /**************************************************************************
1481 * wodDevInterfaceSize [internal]
1483 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1485 TRACE("(%u, %p)\n", wDevID, dwParam1);
1487 *dwParam1 = MultiByteToWideChar(CP_UNIXCP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1488 NULL, 0 ) * sizeof(WCHAR);
1489 return MMSYSERR_NOERROR;
1492 /**************************************************************************
1493 * wodDevInterface [internal]
1495 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1497 TRACE("\n");
1498 if (dwParam2 >= MultiByteToWideChar(CP_UNIXCP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1499 NULL, 0 ) * sizeof(WCHAR))
1501 MultiByteToWideChar(CP_UNIXCP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1502 dwParam1, dwParam2 / sizeof(WCHAR));
1503 return MMSYSERR_NOERROR;
1505 return MMSYSERR_INVALPARAM;
1508 /**************************************************************************
1509 * widDsCreate [internal]
1511 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1513 TRACE("(%d,%p)\n",wDevID,drv);
1515 FIXME("DirectSound not implemented\n");
1516 FIXME("The (slower) DirectSound HEL mode will be used instead.\n");
1517 return MMSYSERR_NOTSUPPORTED;
1520 /**************************************************************************
1521 * wodDsDesc [internal]
1523 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
1525 /* The DirectSound HEL will automatically wrap a non-DirectSound-capable
1526 * driver in a DirectSound adaptor, thus allowing the driver to be used by
1527 * DirectSound clients. However, it only does this if we respond
1528 * successfully to the DRV_QUERYDSOUNDDESC message. It's enough to fill in
1529 * the driver and device names of the description output parameter. */
1530 *desc = WOutDev[wDevID].cadev->ds_desc;
1531 return MMSYSERR_NOERROR;
1534 /**************************************************************************
1535 * wodMessage (WINECOREAUDIO.7)
1537 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1538 DWORD dwParam1, DWORD dwParam2)
1540 TRACE("(%u, %s, %08x, %08x, %08x);\n",
1541 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1543 switch (wMsg) {
1544 case DRVM_INIT:
1545 case DRVM_EXIT:
1546 case DRVM_ENABLE:
1547 case DRVM_DISABLE:
1549 /* FIXME: Pretend this is supported */
1550 return 0;
1551 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1552 case WODM_CLOSE: return wodClose(wDevID);
1553 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1554 case WODM_PAUSE: return wodPause(wDevID);
1555 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1556 case WODM_BREAKLOOP: return wodBreakLoop(wDevID);
1557 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1558 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1560 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1561 case WODM_GETNUMDEVS: return wodGetNumDevs();
1563 case WODM_GETPITCH:
1564 case WODM_SETPITCH:
1565 case WODM_GETPLAYBACKRATE:
1566 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1567 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1568 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1569 case WODM_RESTART: return wodRestart(wDevID);
1570 case WODM_RESET: return wodReset(wDevID);
1572 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1573 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1574 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
1575 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
1577 default:
1578 FIXME("unknown message %d!\n", wMsg);
1581 return MMSYSERR_NOTSUPPORTED;
1584 /*======================================================================*
1585 * Low level DSOUND implementation *
1586 *======================================================================*/
1588 typedef struct IDsDriverImpl IDsDriverImpl;
1589 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1591 struct IDsDriverImpl
1593 /* IUnknown fields */
1594 const IDsDriverVtbl *lpVtbl;
1595 DWORD ref;
1596 /* IDsDriverImpl fields */
1597 UINT wDevID;
1598 IDsDriverBufferImpl*primary;
1601 struct IDsDriverBufferImpl
1603 /* IUnknown fields */
1604 const IDsDriverBufferVtbl *lpVtbl;
1605 DWORD ref;
1606 /* IDsDriverBufferImpl fields */
1607 IDsDriverImpl* drv;
1608 DWORD buflen;
1613 CoreAudio IO threaded callback,
1614 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1616 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1617 AudioUnitRenderActionFlags *ioActionFlags,
1618 const AudioTimeStamp *inTimeStamp,
1619 UInt32 inBusNumber,
1620 UInt32 inNumberFrames,
1621 AudioBufferList *ioData)
1623 UInt32 buffer;
1624 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1625 int needNotify = 0;
1627 unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1628 unsigned int dataProvided = 0;
1630 OSSpinLockLock(&wwo->lock);
1632 /* We might have been called before wwo has been completely filled out by
1633 * wodOpen, or while it's being closed in wodClose. We have to do nothing
1634 * in that case. The check of wwo->state below ensures that. */
1635 while (dataNeeded > 0 && wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1637 unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1638 unsigned int toCopy;
1640 if (available >= dataNeeded)
1641 toCopy = dataNeeded;
1642 else
1643 toCopy = available;
1645 if (toCopy > 0)
1647 memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1648 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1649 wwo->dwPartialOffset += toCopy;
1650 wwo->dwPlayedTotal += toCopy;
1651 dataProvided += toCopy;
1652 dataNeeded -= toCopy;
1653 available -= toCopy;
1656 if (available == 0)
1658 wodHelper_PlayPtrNext(wwo);
1659 needNotify = 1;
1662 ioData->mBuffers[0].mDataByteSize = dataProvided;
1664 OSSpinLockUnlock(&wwo->lock);
1666 /* We can't provide any more wave data. Fill the rest with silence. */
1667 if (dataNeeded > 0)
1669 if (!dataProvided)
1670 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
1671 memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1672 dataProvided += dataNeeded;
1673 dataNeeded = 0;
1676 /* We only fill buffer 0. Set any others that might be requested to 0. */
1677 for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1679 memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1682 if (needNotify) wodSendNotifyCompletionsMessage(wwo);
1683 return noErr;
1687 /*======================================================================*
1688 * Low level WAVE IN implementation *
1689 *======================================================================*/
1691 /**************************************************************************
1692 * widNotifyClient [internal]
1694 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1696 TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
1698 switch (wMsg)
1700 case WIM_OPEN:
1701 case WIM_CLOSE:
1702 case WIM_DATA:
1703 if (wwi->wFlags != DCB_NULL &&
1704 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1705 (HDRVR)wwi->waveDesc.hWave, wMsg, wwi->waveDesc.dwInstance,
1706 dwParam1, dwParam2))
1708 WARN("can't notify client !\n");
1709 return MMSYSERR_ERROR;
1711 break;
1712 default:
1713 FIXME("Unknown callback message %u\n", wMsg);
1714 return MMSYSERR_INVALPARAM;
1716 return MMSYSERR_NOERROR;
1720 /**************************************************************************
1721 * widHelper_NotifyCompletions [internal]
1723 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi)
1725 LPWAVEHDR lpWaveHdr;
1726 LPWAVEHDR lpFirstDoneWaveHdr = NULL;
1727 LPWAVEHDR lpLastDoneWaveHdr = NULL;
1729 OSSpinLockLock(&wwi->lock);
1731 /* First, excise all of the done headers from the queue into
1732 * a free-standing list. */
1734 /* Start from lpQueuePtr and keep notifying until:
1735 * - we hit an unfilled wavehdr
1736 * - we hit the end of the list
1738 for (
1739 lpWaveHdr = wwi->lpQueuePtr;
1740 lpWaveHdr &&
1741 lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength;
1742 lpWaveHdr = lpWaveHdr->lpNext
1745 if (!lpFirstDoneWaveHdr)
1746 lpFirstDoneWaveHdr = lpWaveHdr;
1747 lpLastDoneWaveHdr = lpWaveHdr;
1750 if (lpLastDoneWaveHdr)
1752 wwi->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1753 lpLastDoneWaveHdr->lpNext = NULL;
1756 OSSpinLockUnlock(&wwi->lock);
1758 /* Now, send the "done" notification for each header in our list. */
1759 lpWaveHdr = lpFirstDoneWaveHdr;
1760 while (lpWaveHdr)
1762 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1764 lpWaveHdr->lpNext = NULL;
1765 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1766 lpWaveHdr->dwFlags |= WHDR_DONE;
1767 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1769 lpWaveHdr = lpNext;
1774 /**************************************************************************
1775 * widGetDevCaps [internal]
1777 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1779 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1781 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1783 if (wDevID >= MAX_WAVEINDRV)
1785 TRACE("MAX_WAVEINDRV reached !\n");
1786 return MMSYSERR_BADDEVICEID;
1789 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1790 return MMSYSERR_NOERROR;
1794 /**************************************************************************
1795 * widHelper_DestroyAudioBufferList [internal]
1796 * Convenience function to dispose of our audio buffers
1798 static void widHelper_DestroyAudioBufferList(AudioBufferList* list)
1800 if (list)
1802 UInt32 i;
1803 for (i = 0; i < list->mNumberBuffers; i++)
1805 if (list->mBuffers[i].mData)
1806 HeapFree(GetProcessHeap(), 0, list->mBuffers[i].mData);
1808 HeapFree(GetProcessHeap(), 0, list);
1813 #define AUDIOBUFFERLISTSIZE(numBuffers) (offsetof(AudioBufferList, mBuffers) + (numBuffers) * sizeof(AudioBuffer))
1815 /**************************************************************************
1816 * widHelper_AllocateAudioBufferList [internal]
1817 * Convenience function to allocate our audio buffers
1819 static AudioBufferList* widHelper_AllocateAudioBufferList(UInt32 numChannels, UInt32 bitsPerChannel, UInt32 bufferFrames, BOOL interleaved)
1821 UInt32 numBuffers;
1822 UInt32 channelsPerFrame;
1823 UInt32 bytesPerFrame;
1824 UInt32 bytesPerBuffer;
1825 AudioBufferList* list;
1826 UInt32 i;
1828 if (interleaved)
1830 /* For interleaved audio, we allocate one buffer for all channels. */
1831 numBuffers = 1;
1832 channelsPerFrame = numChannels;
1834 else
1836 numBuffers = numChannels;
1837 channelsPerFrame = 1;
1840 bytesPerFrame = bitsPerChannel * channelsPerFrame / 8;
1841 bytesPerBuffer = bytesPerFrame * bufferFrames;
1843 list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AUDIOBUFFERLISTSIZE(numBuffers));
1844 if (list == NULL)
1845 return NULL;
1847 list->mNumberBuffers = numBuffers;
1848 for (i = 0; i < numBuffers; ++i)
1850 list->mBuffers[i].mNumberChannels = channelsPerFrame;
1851 list->mBuffers[i].mDataByteSize = bytesPerBuffer;
1852 list->mBuffers[i].mData = HeapAlloc(GetProcessHeap(), 0, bytesPerBuffer);
1853 if (list->mBuffers[i].mData == NULL)
1855 widHelper_DestroyAudioBufferList(list);
1856 return NULL;
1859 return list;
1863 /**************************************************************************
1864 * widOpen [internal]
1866 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1868 WINE_WAVEIN* wwi;
1869 UInt32 frameCount;
1871 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1872 if (lpDesc == NULL)
1874 WARN("Invalid Parameter !\n");
1875 return MMSYSERR_INVALPARAM;
1877 if (wDevID >= MAX_WAVEINDRV)
1879 TRACE ("MAX_WAVEINDRV reached !\n");
1880 return MMSYSERR_BADDEVICEID;
1883 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1884 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1885 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1887 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1888 lpDesc->lpFormat->nChannels == 0 ||
1889 lpDesc->lpFormat->nSamplesPerSec == 0 ||
1890 lpDesc->lpFormat->nSamplesPerSec != AudioUnit_GetInputDeviceSampleRate()
1893 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1894 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1895 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1896 return WAVERR_BADFORMAT;
1899 if (dwFlags & WAVE_FORMAT_QUERY)
1901 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1902 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1903 lpDesc->lpFormat->nSamplesPerSec);
1904 return MMSYSERR_NOERROR;
1907 wwi = &WInDev[wDevID];
1908 if (!OSSpinLockTry(&wwi->lock))
1909 return MMSYSERR_ALLOCATED;
1911 if (wwi->state != WINE_WS_CLOSED)
1913 OSSpinLockUnlock(&wwi->lock);
1914 return MMSYSERR_ALLOCATED;
1917 wwi->state = WINE_WS_STOPPED;
1918 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1920 wwi->waveDesc = *lpDesc;
1921 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1923 if (wwi->format.wBitsPerSample == 0)
1925 WARN("Resetting zeroed wBitsPerSample\n");
1926 wwi->format.wBitsPerSample = 8 *
1927 (wwi->format.wf.nAvgBytesPerSec /
1928 wwi->format.wf.nSamplesPerSec) /
1929 wwi->format.wf.nChannels;
1932 wwi->dwTotalRecorded = 0;
1934 wwi->trace_on = TRACE_ON(wave);
1935 wwi->warn_on = WARN_ON(wave);
1936 wwi->err_on = ERR_ON(wave);
1938 if (!AudioUnit_CreateInputUnit(wwi, &wwi->audioUnit,
1939 wwi->format.wf.nChannels, wwi->format.wf.nSamplesPerSec,
1940 wwi->format.wBitsPerSample, &frameCount))
1942 ERR("AudioUnit_CreateInputUnit failed\n");
1943 OSSpinLockUnlock(&wwi->lock);
1944 return MMSYSERR_ERROR;
1947 /* Allocate our audio buffers */
1948 wwi->bufferList = widHelper_AllocateAudioBufferList(wwi->format.wf.nChannels,
1949 wwi->format.wBitsPerSample, frameCount, TRUE);
1950 if (wwi->bufferList == NULL)
1952 ERR("Failed to allocate buffer list\n");
1953 AudioUnitUninitialize(wwi->audioUnit);
1954 AudioUnit_CloseAudioUnit(wwi->audioUnit);
1955 OSSpinLockUnlock(&wwi->lock);
1956 return MMSYSERR_NOMEM;
1959 /* Keep a copy of the buffer list structure (but not the buffers themselves)
1960 * in case AudioUnitRender clobbers the original, as it won't to do. */
1961 wwi->bufferListCopy = HeapAlloc(GetProcessHeap(), 0, AUDIOBUFFERLISTSIZE(wwi->bufferList->mNumberBuffers));
1962 if (wwi->bufferListCopy == NULL)
1964 ERR("Failed to allocate buffer list copy\n");
1965 widHelper_DestroyAudioBufferList(wwi->bufferList);
1966 AudioUnitUninitialize(wwi->audioUnit);
1967 AudioUnit_CloseAudioUnit(wwi->audioUnit);
1968 OSSpinLockUnlock(&wwi->lock);
1969 return MMSYSERR_NOMEM;
1971 memcpy(wwi->bufferListCopy, wwi->bufferList, AUDIOBUFFERLISTSIZE(wwi->bufferList->mNumberBuffers));
1973 OSSpinLockUnlock(&wwi->lock);
1975 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1979 /**************************************************************************
1980 * widClose [internal]
1982 static DWORD widClose(WORD wDevID)
1984 DWORD ret = MMSYSERR_NOERROR;
1985 WINE_WAVEIN* wwi;
1986 OSStatus err;
1988 TRACE("(%u);\n", wDevID);
1990 if (wDevID >= MAX_WAVEINDRV)
1992 WARN("bad device ID !\n");
1993 return MMSYSERR_BADDEVICEID;
1996 wwi = &WInDev[wDevID];
1997 OSSpinLockLock(&wwi->lock);
1998 if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
2000 WARN("Device already closed.\n");
2001 ret = MMSYSERR_INVALHANDLE;
2003 else if (wwi->lpQueuePtr)
2005 WARN("Buffers in queue.\n");
2006 ret = WAVERR_STILLPLAYING;
2008 else
2010 wwi->state = WINE_WS_CLOSING;
2013 OSSpinLockUnlock(&wwi->lock);
2015 if (ret != MMSYSERR_NOERROR)
2016 return ret;
2019 /* Clean up and close the audio unit. This has to be done without
2020 * wwi->lock being held to avoid deadlock. AudioUnitUninitialize will
2021 * grab an internal Core Audio lock while waiting for the device work
2022 * thread to exit. Meanwhile the device work thread may be holding
2023 * that lock and trying to grab the wwi->lock in the callback. */
2024 err = AudioUnitUninitialize(wwi->audioUnit);
2025 if (err)
2027 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
2028 (char) (err >> 16),
2029 (char) (err >> 8),
2030 (char) err);
2033 if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
2035 ERR("Can't close AudioUnit\n");
2039 OSSpinLockLock(&wwi->lock);
2040 assert(wwi->state == WINE_WS_CLOSING);
2042 /* Dellocate our audio buffers */
2043 widHelper_DestroyAudioBufferList(wwi->bufferList);
2044 wwi->bufferList = NULL;
2045 HeapFree(GetProcessHeap(), 0, wwi->bufferListCopy);
2046 wwi->bufferListCopy = NULL;
2048 wwi->audioUnit = NULL;
2049 wwi->state = WINE_WS_CLOSED;
2050 OSSpinLockUnlock(&wwi->lock);
2052 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
2054 return ret;
2058 /**************************************************************************
2059 * widAddBuffer [internal]
2061 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2063 DWORD ret = MMSYSERR_NOERROR;
2064 WINE_WAVEIN* wwi;
2066 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
2068 if (wDevID >= MAX_WAVEINDRV)
2070 WARN("invalid device ID\n");
2071 return MMSYSERR_INVALHANDLE;
2073 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
2075 TRACE("never been prepared !\n");
2076 return WAVERR_UNPREPARED;
2078 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2080 TRACE("header already in use !\n");
2081 return WAVERR_STILLPLAYING;
2084 wwi = &WInDev[wDevID];
2085 OSSpinLockLock(&wwi->lock);
2087 if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
2089 WARN("Trying to add buffer to closed device.\n");
2090 ret = MMSYSERR_INVALHANDLE;
2092 else
2094 LPWAVEHDR* wh;
2096 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2097 lpWaveHdr->dwFlags &= ~WHDR_DONE;
2098 lpWaveHdr->dwBytesRecorded = 0;
2099 lpWaveHdr->lpNext = NULL;
2101 /* insert buffer at end of queue */
2102 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
2103 /* Do nothing */;
2104 *wh = lpWaveHdr;
2107 OSSpinLockUnlock(&wwi->lock);
2109 return ret;
2113 /**************************************************************************
2114 * widStart [internal]
2116 static DWORD widStart(WORD wDevID)
2118 DWORD ret = MMSYSERR_NOERROR;
2119 WINE_WAVEIN* wwi;
2121 TRACE("(%u);\n", wDevID);
2122 if (wDevID >= MAX_WAVEINDRV)
2124 WARN("invalid device ID\n");
2125 return MMSYSERR_INVALHANDLE;
2128 /* The order of the following operations is important since we can't hold
2129 * the mutex while we make an Audio Unit call. Set the PLAYING state
2130 * before starting the Audio Unit. In widStop, the order is reversed.
2131 * This guarantees that we can't get into a situation where the state is
2132 * PLAYING but the Audio Unit isn't running. Although we can be in STOPPED
2133 * state with the Audio Unit still running, that's harmless because the
2134 * input callback will just throw away the sound data.
2136 wwi = &WInDev[wDevID];
2137 OSSpinLockLock(&wwi->lock);
2139 if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
2141 WARN("Trying to start closed device.\n");
2142 ret = MMSYSERR_INVALHANDLE;
2144 else
2145 wwi->state = WINE_WS_PLAYING;
2147 OSSpinLockUnlock(&wwi->lock);
2149 if (ret == MMSYSERR_NOERROR)
2151 /* Start pulling for audio data */
2152 OSStatus err = AudioOutputUnitStart(wwi->audioUnit);
2153 if (err != noErr)
2154 ERR("Failed to start AU: %08lx\n", err);
2156 TRACE("Recording started...\n");
2159 return ret;
2163 /**************************************************************************
2164 * widStop [internal]
2166 static DWORD widStop(WORD wDevID)
2168 DWORD ret = MMSYSERR_NOERROR;
2169 WINE_WAVEIN* wwi;
2170 WAVEHDR* lpWaveHdr = NULL;
2171 OSStatus err;
2173 TRACE("(%u);\n", wDevID);
2174 if (wDevID >= MAX_WAVEINDRV)
2176 WARN("invalid device ID\n");
2177 return MMSYSERR_INVALHANDLE;
2180 wwi = &WInDev[wDevID];
2182 /* The order of the following operations is important since we can't hold
2183 * the mutex while we make an Audio Unit call. Stop the Audio Unit before
2184 * setting the STOPPED state. In widStart, the order is reversed. This
2185 * guarantees that we can't get into a situation where the state is
2186 * PLAYING but the Audio Unit isn't running. Although we can be in STOPPED
2187 * state with the Audio Unit still running, that's harmless because the
2188 * input callback will just throw away the sound data.
2190 err = AudioOutputUnitStop(wwi->audioUnit);
2191 if (err != noErr)
2192 WARN("Failed to stop AU: %08lx\n", err);
2194 TRACE("Recording stopped.\n");
2196 OSSpinLockLock(&wwi->lock);
2198 if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
2200 WARN("Trying to stop closed device.\n");
2201 ret = MMSYSERR_INVALHANDLE;
2203 else if (wwi->state != WINE_WS_STOPPED)
2205 wwi->state = WINE_WS_STOPPED;
2206 /* If there's a buffer in progress, it's done. Remove it from the
2207 * queue so that we can return it to the app, below. */
2208 if (wwi->lpQueuePtr)
2210 lpWaveHdr = wwi->lpQueuePtr;
2211 wwi->lpQueuePtr = lpWaveHdr->lpNext;
2215 OSSpinLockUnlock(&wwi->lock);
2217 if (lpWaveHdr)
2219 lpWaveHdr->lpNext = NULL;
2220 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2221 lpWaveHdr->dwFlags |= WHDR_DONE;
2222 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2225 return ret;
2228 /**************************************************************************
2229 * widGetPos [internal]
2231 static DWORD widGetPos(WORD wDevID, LPMMTIME lpTime, UINT size)
2233 DWORD val;
2234 WINE_WAVEIN* wwi;
2236 TRACE("(%u);\n", wDevID);
2237 if (wDevID >= MAX_WAVEINDRV)
2239 WARN("invalid device ID\n");
2240 return MMSYSERR_INVALHANDLE;
2243 wwi = &WInDev[wDevID];
2245 OSSpinLockLock(&WInDev[wDevID].lock);
2246 val = wwi->dwTotalRecorded;
2247 OSSpinLockUnlock(&WInDev[wDevID].lock);
2249 return bytes_to_mmtime(lpTime, val, &wwi->format);
2252 /**************************************************************************
2253 * widReset [internal]
2255 static DWORD widReset(WORD wDevID)
2257 DWORD ret = MMSYSERR_NOERROR;
2258 WINE_WAVEIN* wwi;
2259 WAVEHDR* lpWaveHdr = NULL;
2261 TRACE("(%u);\n", wDevID);
2262 if (wDevID >= MAX_WAVEINDRV)
2264 WARN("invalid device ID\n");
2265 return MMSYSERR_INVALHANDLE;
2268 wwi = &WInDev[wDevID];
2269 OSSpinLockLock(&wwi->lock);
2271 if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
2273 WARN("Trying to reset a closed device.\n");
2274 ret = MMSYSERR_INVALHANDLE;
2276 else
2278 lpWaveHdr = wwi->lpQueuePtr;
2279 wwi->lpQueuePtr = NULL;
2280 wwi->state = WINE_WS_STOPPED;
2281 wwi->dwTotalRecorded = 0;
2284 OSSpinLockUnlock(&wwi->lock);
2286 if (ret == MMSYSERR_NOERROR)
2288 OSStatus err = AudioOutputUnitStop(wwi->audioUnit);
2289 if (err != noErr)
2290 WARN("Failed to stop AU: %08lx\n", err);
2292 TRACE("Recording stopped.\n");
2295 while (lpWaveHdr)
2297 WAVEHDR* lpNext = lpWaveHdr->lpNext;
2299 lpWaveHdr->lpNext = NULL;
2300 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2301 lpWaveHdr->dwFlags |= WHDR_DONE;
2302 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2304 lpWaveHdr = lpNext;
2307 return ret;
2311 /**************************************************************************
2312 * widGetNumDevs [internal]
2314 static DWORD widGetNumDevs(void)
2316 return MAX_WAVEINDRV;
2320 /**************************************************************************
2321 * widDevInterfaceSize [internal]
2323 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2325 TRACE("(%u, %p)\n", wDevID, dwParam1);
2327 *dwParam1 = MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
2328 NULL, 0 ) * sizeof(WCHAR);
2329 return MMSYSERR_NOERROR;
2333 /**************************************************************************
2334 * widDevInterface [internal]
2336 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2338 if (dwParam2 >= MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
2339 NULL, 0 ) * sizeof(WCHAR))
2341 MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
2342 dwParam1, dwParam2 / sizeof(WCHAR));
2343 return MMSYSERR_NOERROR;
2345 return MMSYSERR_INVALPARAM;
2349 /**************************************************************************
2350 * widDsCreate [internal]
2352 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
2354 TRACE("(%d,%p)\n",wDevID,drv);
2356 FIXME("DirectSoundCapture not implemented\n");
2357 FIXME("The (slower) DirectSound HEL mode will be used instead.\n");
2358 return MMSYSERR_NOTSUPPORTED;
2361 /**************************************************************************
2362 * widDsDesc [internal]
2364 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2366 /* The DirectSound HEL will automatically wrap a non-DirectSound-capable
2367 * driver in a DirectSound adaptor, thus allowing the driver to be used by
2368 * DirectSound clients. However, it only does this if we respond
2369 * successfully to the DRV_QUERYDSOUNDDESC message. It's enough to fill in
2370 * the driver and device names of the description output parameter. */
2371 memset(desc, 0, sizeof(*desc));
2372 lstrcpynA(desc->szDrvname, "winecoreaudio.drv", sizeof(desc->szDrvname) - 1);
2373 lstrcpynA(desc->szDesc, WInDev[wDevID].interface_name, sizeof(desc->szDesc) - 1);
2374 return MMSYSERR_NOERROR;
2378 /**************************************************************************
2379 * widMessage (WINECOREAUDIO.6)
2381 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2382 DWORD dwParam1, DWORD dwParam2)
2384 TRACE("(%u, %04X, %08X, %08X, %08X);\n",
2385 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2387 switch (wMsg)
2389 case DRVM_INIT:
2390 case DRVM_EXIT:
2391 case DRVM_ENABLE:
2392 case DRVM_DISABLE:
2393 /* FIXME: Pretend this is supported */
2394 return 0;
2395 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2396 case WIDM_CLOSE: return widClose (wDevID);
2397 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2398 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
2399 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
2400 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
2401 case WIDM_GETNUMDEVS: return widGetNumDevs ();
2402 case WIDM_RESET: return widReset (wDevID);
2403 case WIDM_START: return widStart (wDevID);
2404 case WIDM_STOP: return widStop (wDevID);
2405 case WIDM_GETPOS: return widGetPos (wDevID, (LPMMTIME)dwParam1, (UINT)dwParam2 );
2406 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2407 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2408 case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
2409 case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
2410 default:
2411 FIXME("unknown message %d!\n", wMsg);
2414 return MMSYSERR_NOTSUPPORTED;
2418 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
2419 AudioUnitRenderActionFlags *ioActionFlags,
2420 const AudioTimeStamp *inTimeStamp,
2421 UInt32 inBusNumber,
2422 UInt32 inNumberFrames,
2423 AudioBufferList *ioData)
2425 WINE_WAVEIN* wwi = (WINE_WAVEIN*)inRefCon;
2426 OSStatus err = noErr;
2427 BOOL needNotify = FALSE;
2428 WAVEHDR* lpStorePtr;
2429 unsigned int dataToStore;
2430 unsigned int dataStored = 0;
2433 if (wwi->trace_on)
2434 fprintf(stderr, "trace:wave:CoreAudio_wiAudioUnitIOProc (ioActionFlags = %08lx, "
2435 "inTimeStamp = { %f, %x%08x, %f, %x%08x, %08lx }, inBusNumber = %lu, inNumberFrames = %lu)\n",
2436 *ioActionFlags, inTimeStamp->mSampleTime, (DWORD)(inTimeStamp->mHostTime >>32),
2437 (DWORD)inTimeStamp->mHostTime, inTimeStamp->mRateScalar, (DWORD)(inTimeStamp->mWordClockTime >> 32),
2438 (DWORD)inTimeStamp->mWordClockTime, inTimeStamp->mFlags, inBusNumber, inNumberFrames);
2440 /* Render into audio buffer */
2441 /* FIXME: implement sample rate conversion on input. This will require
2442 * a different render strategy. We'll need to buffer the sound data
2443 * received here and pass it off to an AUConverter in another thread. */
2444 err = AudioUnitRender(wwi->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, wwi->bufferList);
2445 if (err)
2447 if (wwi->err_on)
2448 fprintf(stderr, "err:wave:CoreAudio_wiAudioUnitIOProc AudioUnitRender failed with error %li\n", err);
2449 return err;
2452 /* Copy from audio buffer to the wavehdrs */
2453 dataToStore = wwi->bufferList->mBuffers[0].mDataByteSize;
2455 OSSpinLockLock(&wwi->lock);
2457 lpStorePtr = wwi->lpQueuePtr;
2459 /* We might have been called while the waveIn device is being closed in
2460 * widClose. We have to do nothing in that case. The check of wwi->state
2461 * below ensures that. */
2462 while (dataToStore > 0 && wwi->state == WINE_WS_PLAYING && lpStorePtr)
2464 unsigned int room = lpStorePtr->dwBufferLength - lpStorePtr->dwBytesRecorded;
2465 unsigned int toCopy;
2467 if (wwi->trace_on)
2468 fprintf(stderr, "trace:wave:CoreAudio_wiAudioUnitIOProc Looking to store %u bytes to wavehdr %p, which has room for %u\n",
2469 dataToStore, lpStorePtr, room);
2471 if (room >= dataToStore)
2472 toCopy = dataToStore;
2473 else
2474 toCopy = room;
2476 if (toCopy > 0)
2478 memcpy(lpStorePtr->lpData + lpStorePtr->dwBytesRecorded,
2479 (char*)wwi->bufferList->mBuffers[0].mData + dataStored, toCopy);
2480 lpStorePtr->dwBytesRecorded += toCopy;
2481 wwi->dwTotalRecorded += toCopy;
2482 dataStored += toCopy;
2483 dataToStore -= toCopy;
2484 room -= toCopy;
2487 if (room == 0)
2489 lpStorePtr = lpStorePtr->lpNext;
2490 needNotify = TRUE;
2494 OSSpinLockUnlock(&wwi->lock);
2496 /* Restore the audio buffer list structure from backup, in case
2497 * AudioUnitRender clobbered it. (It modifies mDataByteSize and may even
2498 * give us a different mData buffer to avoid a copy.) */
2499 memcpy(wwi->bufferList, wwi->bufferListCopy, AUDIOBUFFERLISTSIZE(wwi->bufferList->mNumberBuffers));
2501 if (needNotify) wodSendNotifyInputCompletionsMessage(wwi);
2502 return err;
2505 #else
2507 /**************************************************************************
2508 * widMessage (WINECOREAUDIO.6)
2510 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2511 DWORD dwParam1, DWORD dwParam2)
2513 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2514 return MMSYSERR_NOTENABLED;
2517 /**************************************************************************
2518 * wodMessage (WINECOREAUDIO.7)
2520 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2521 DWORD dwParam1, DWORD dwParam2)
2523 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2524 return MMSYSERR_NOTENABLED;
2527 #endif