winecoreaudio.drv: Remove obsolete FIXME.
[wine] / dlls / winecoreaudio.drv / audio.c
1 /*
2  * Wine Driver for CoreAudio based on Jack Driver
3  *
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
9  *
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.
14  *
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.
19  *
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
23  */
24
25 #include "config.h"
26
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
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "wingdi.h"
40 #include "winerror.h"
41 #include "mmddk.h"
42 #include "dsound.h"
43 #include "dsdriver.h"
44 #include "coreaudio.h"
45 #include "wine/unicode.h"
46 #include "wine/library.h"
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(wave);
50
51
52 #if defined(HAVE_COREAUDIO_COREAUDIO_H) && defined(HAVE_AUDIOUNIT_AUDIOUNIT_H)
53 #include <CoreAudio/CoreAudio.h>
54 #include <CoreFoundation/CoreFoundation.h>
55 #include <libkern/OSAtomic.h>
56
57 /*
58     Due to AudioUnit headers conflict define some needed types.
59 */
60
61 typedef void *AudioUnit;
62
63 /* From AudioUnit/AUComponents.h */
64 enum
65 {
66     kAudioUnitRenderAction_OutputIsSilence  = (1 << 4),
67         /* provides hint on return from Render(): if set the buffer contains all zeroes */
68 };
69 typedef UInt32 AudioUnitRenderActionFlags;
70
71 typedef long ComponentResult;
72 extern ComponentResult
73 AudioUnitRender(                    AudioUnit                       ci,
74                                     AudioUnitRenderActionFlags *    ioActionFlags,
75                                     const AudioTimeStamp *          inTimeStamp,
76                                     UInt32                          inOutputBusNumber,
77                                     UInt32                          inNumberFrames,
78                                     AudioBufferList *               ioData)         AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER;
79
80 /* only allow 10 output devices through this driver, this ought to be adequate */
81 #define MAX_WAVEOUTDRV  (1)
82 #define MAX_WAVEINDRV   (1)
83
84 /* state diagram for waveOut writing:
85 *
86 * +---------+-------------+---------------+---------------------------------+
87 * |  state  |  function   |     event     |            new state             |
88 * +---------+-------------+---------------+---------------------------------+
89 * |          | open()      |               | STOPPED                         |
90 * | PAUSED  | write()      |               | PAUSED                          |
91 * | STOPPED | write()      | <thrd create> | PLAYING                         |
92 * | PLAYING | write()      | HEADER        | PLAYING                         |
93 * | (other) | write()      | <error>       |                                 |
94 * | (any)   | pause()      | PAUSING       | PAUSED                          |
95 * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
96 * | (any)   | reset()      | RESETTING     | STOPPED                         |
97 * | (any)   | close()      | CLOSING       | CLOSED                          |
98 * +---------+-------------+---------------+---------------------------------+
99 */
100
101 /* states of the playing device */
102 #define WINE_WS_PLAYING   0
103 #define WINE_WS_PAUSED    1
104 #define WINE_WS_STOPPED   2
105 #define WINE_WS_CLOSED    3
106
107 typedef struct tagCoreAudio_Device {
108     char                        dev_name[32];
109     char                        mixer_name[32];
110     unsigned                    open_count;
111     char*                       interface_name;
112     
113     WAVEOUTCAPSW                out_caps;
114     WAVEINCAPSW                 in_caps;
115     DWORD                       in_caps_support;
116     int                         sample_rate;
117     int                         stereo;
118     int                         format;
119     unsigned                    audio_fragment;
120     BOOL                        full_duplex;
121     BOOL                        bTriggerSupport;
122     BOOL                        bOutputEnabled;
123     BOOL                        bInputEnabled;
124     DSDRIVERDESC                ds_desc;
125     DSDRIVERCAPS                ds_caps;
126     DSCDRIVERCAPS               dsc_caps;
127     GUID                        ds_guid;
128     GUID                        dsc_guid;
129     
130     AudioDeviceID outputDeviceID;
131     AudioDeviceID inputDeviceID;
132     AudioStreamBasicDescription streamDescription;
133 } CoreAudio_Device;
134
135 /* for now use the default device */
136 static CoreAudio_Device CoreAudio_DefaultDevice;
137
138 typedef struct {
139     volatile int                state;      /* one of the WINE_WS_ manifest constants */
140     CoreAudio_Device            *cadev;
141     WAVEOPENDESC                waveDesc;
142     WORD                        wFlags;
143     PCMWAVEFORMAT               format;
144     DWORD                       woID;
145     AudioUnit                   audioUnit;
146     AudioStreamBasicDescription streamDescription;
147     
148     WAVEOUTCAPSW                caps;
149     char                        interface_name[32];
150     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
151     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
152     DWORD                       dwPartialOffset;        /* Offset of not yet written bytes in lpPlayPtr */
153     
154     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
155     DWORD                       dwLoops;                /* private copy of loop counter */
156     
157     DWORD                       dwPlayedTotal;          /* number of bytes actually played since opening */
158     DWORD                       dwWrittenTotal;         /* number of bytes written to OSS buffer since opening */
159         
160     DWORD                       tickCountMS; /* time in MS of last AudioUnit callback */
161
162     OSSpinLock                  lock;         /* synchronization stuff */
163
164     BOOL trace_on;
165     BOOL warn_on;
166     BOOL err_on;
167 } WINE_WAVEOUT;
168
169 typedef struct {
170     /* This device's device number */
171     DWORD           wiID;
172
173     /* Access to the following fields is synchronized across threads. */
174     volatile int    state;
175     LPWAVEHDR       lpQueuePtr;
176     DWORD           dwTotalRecorded;
177
178     /* Synchronization mechanism to protect above fields */
179     OSSpinLock      lock;
180
181     /* Capabilities description */
182     WAVEINCAPSW     caps;
183     char            interface_name[32];
184
185     /* Record the arguments used when opening the device. */
186     WAVEOPENDESC    waveDesc;
187     WORD            wFlags;
188     PCMWAVEFORMAT   format;
189
190     AudioUnit       audioUnit;
191     AudioBufferList*bufferList;
192
193     /* Record state of debug channels at open.  Used to control fprintf's since
194      * we can't use Wine debug channel calls in non-Wine AudioUnit threads. */
195     BOOL            trace_on;
196     BOOL            warn_on;
197     BOOL            err_on;
198
199 /* These fields aren't used. */
200 #if 0
201     CoreAudio_Device *cadev;
202
203     AudioStreamBasicDescription streamDescription;
204 #endif
205 } WINE_WAVEIN;
206
207 static WINE_WAVEOUT WOutDev   [MAX_WAVEOUTDRV];
208 static WINE_WAVEIN  WInDev    [MAX_WAVEINDRV];
209
210 static HANDLE hThread = NULL; /* Track the thread we create so we can clean it up later */
211 static CFMessagePortRef Port_SendToMessageThread;
212
213 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
214 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
215 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi);
216
217 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
218 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
219 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
220
221 extern OSStatus AudioOutputUnitStart(AudioUnit au);
222 extern OSStatus AudioOutputUnitStop(AudioUnit au);
223 extern OSStatus AudioUnitUninitialize(AudioUnit au);
224
225 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
226 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
227
228 extern int AudioUnit_GetInputDeviceSampleRate(void);
229
230 extern int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
231         WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
232         UInt32* outFrameCount);
233
234 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, 
235                                      AudioUnitRenderActionFlags *ioActionFlags, 
236                                      const AudioTimeStamp *inTimeStamp, 
237                                      UInt32 inBusNumber, 
238                                      UInt32 inNumberFrames, 
239                                      AudioBufferList *ioData);
240 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
241                                      AudioUnitRenderActionFlags *ioActionFlags,
242                                      const AudioTimeStamp *inTimeStamp,
243                                      UInt32 inBusNumber,
244                                      UInt32 inNumberFrames,
245                                      AudioBufferList *ioData);
246
247 /* These strings used only for tracing */
248
249 static const char * getMessage(UINT msg)
250 {
251     static char unknown[32];
252 #define MSG_TO_STR(x) case x: return #x
253     switch(msg) {
254         MSG_TO_STR(DRVM_INIT);
255         MSG_TO_STR(DRVM_EXIT);
256         MSG_TO_STR(DRVM_ENABLE);
257         MSG_TO_STR(DRVM_DISABLE);
258         MSG_TO_STR(WIDM_OPEN);
259         MSG_TO_STR(WIDM_CLOSE);
260         MSG_TO_STR(WIDM_ADDBUFFER);
261         MSG_TO_STR(WIDM_PREPARE);
262         MSG_TO_STR(WIDM_UNPREPARE);
263         MSG_TO_STR(WIDM_GETDEVCAPS);
264         MSG_TO_STR(WIDM_GETNUMDEVS);
265         MSG_TO_STR(WIDM_GETPOS);
266         MSG_TO_STR(WIDM_RESET);
267         MSG_TO_STR(WIDM_START);
268         MSG_TO_STR(WIDM_STOP);
269         MSG_TO_STR(WODM_OPEN);
270         MSG_TO_STR(WODM_CLOSE);
271         MSG_TO_STR(WODM_WRITE);
272         MSG_TO_STR(WODM_PAUSE);
273         MSG_TO_STR(WODM_GETPOS);
274         MSG_TO_STR(WODM_BREAKLOOP);
275         MSG_TO_STR(WODM_PREPARE);
276         MSG_TO_STR(WODM_UNPREPARE);
277         MSG_TO_STR(WODM_GETDEVCAPS);
278         MSG_TO_STR(WODM_GETNUMDEVS);
279         MSG_TO_STR(WODM_GETPITCH);
280         MSG_TO_STR(WODM_SETPITCH);
281         MSG_TO_STR(WODM_GETPLAYBACKRATE);
282         MSG_TO_STR(WODM_SETPLAYBACKRATE);
283         MSG_TO_STR(WODM_GETVOLUME);
284         MSG_TO_STR(WODM_SETVOLUME);
285         MSG_TO_STR(WODM_RESTART);
286         MSG_TO_STR(WODM_RESET);
287         MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
288         MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
289         MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
290         MSG_TO_STR(DRV_QUERYDSOUNDDESC);
291     }
292 #undef MSG_TO_STR
293     sprintf(unknown, "UNKNOWN(0x%04x)", msg);
294     return unknown;
295 }
296
297 #define kStopLoopMessage 0
298 #define kWaveOutNotifyCompletionsMessage 1
299 #define kWaveInNotifyCompletionsMessage 2
300
301 /* Mach Message Handling */
302 static CFDataRef wodMessageHandler(CFMessagePortRef port_ReceiveInMessageThread, SInt32 msgid, CFDataRef data, void *info)
303 {
304     UInt32 *buffer = NULL;
305
306     switch (msgid)
307     {
308         case kWaveOutNotifyCompletionsMessage:
309             buffer = (UInt32 *) CFDataGetBytePtr(data);
310             wodHelper_NotifyCompletions(&WOutDev[buffer[0]], FALSE);
311             break;
312         case kWaveInNotifyCompletionsMessage:
313             buffer = (UInt32 *) CFDataGetBytePtr(data);
314             widHelper_NotifyCompletions(&WInDev[buffer[0]]);
315             break;
316         default:
317             CFRunLoopStop(CFRunLoopGetCurrent());
318             break;
319     }
320     
321     return NULL;
322 }
323
324 static DWORD WINAPI messageThread(LPVOID p)
325 {
326     CFMessagePortRef port_ReceiveInMessageThread = (CFMessagePortRef) p;
327     CFRunLoopSourceRef source;
328     
329     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port_ReceiveInMessageThread, (CFIndex)0);
330     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
331
332     CFRunLoopRun();
333
334     CFRunLoopSourceInvalidate(source);
335     CFRelease(source);
336     CFRelease(port_ReceiveInMessageThread);
337
338     return 0;
339 }
340
341 /**************************************************************************
342 *                       wodSendNotifyCompletionsMessage                 [internal]
343 *   Call from AudioUnit IO thread can't use Wine debug channels.
344 */
345 static void wodSendNotifyCompletionsMessage(WINE_WAVEOUT* wwo)
346 {
347     CFDataRef data;
348     UInt32 buffer;
349
350     buffer = (UInt32) wwo->woID;
351
352     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
353     if (!data)
354         return;
355
356     CFMessagePortSendRequest(Port_SendToMessageThread, kWaveOutNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
357     CFRelease(data);
358 }
359
360 /**************************************************************************
361 *                       wodSendNotifyInputCompletionsMessage     [internal]
362 *   Call from AudioUnit IO thread can't use Wine debug channels.
363 */
364 static void wodSendNotifyInputCompletionsMessage(WINE_WAVEIN* wwi)
365 {
366     CFDataRef data;
367     UInt32 buffer;
368
369     buffer = (UInt32) wwi->wiID;
370
371     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
372     if (!data)
373         return;
374
375     CFMessagePortSendRequest(Port_SendToMessageThread, kWaveInNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
376     CFRelease(data);
377 }
378
379 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
380                              PCMWAVEFORMAT* format)
381 {
382     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
383           lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
384           format->wf.nChannels, format->wf.nAvgBytesPerSec);
385     TRACE("Position in bytes=%u\n", position);
386
387     switch (lpTime->wType) {
388     case TIME_SAMPLES:
389         lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
390         TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
391         break;
392     case TIME_MS:
393         lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
394         TRACE("TIME_MS=%u\n", lpTime->u.ms);
395         break;
396     case TIME_SMPTE:
397         lpTime->u.smpte.fps = 30;
398         position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
399         position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
400         lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
401         position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
402         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
403         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
404         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
405         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
406         lpTime->u.smpte.fps = 30;
407         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
408         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
409               lpTime->u.smpte.hour, lpTime->u.smpte.min,
410               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
411         break;
412     default:
413         WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
414         lpTime->wType = TIME_BYTES;
415         /* fall through */
416     case TIME_BYTES:
417         lpTime->u.cb = position;
418         TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
419         break;
420     }
421     return MMSYSERR_NOERROR;
422 }
423
424 /**************************************************************************
425 *                       CoreAudio_GetDevCaps            [internal]
426 */
427 BOOL CoreAudio_GetDevCaps (void)
428 {
429     OSStatus status;
430     UInt32 propertySize;
431     AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
432     
433     char name[MAXPNAMELEN];
434     
435     propertySize = MAXPNAMELEN;
436     status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
437     if (status) {
438         ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
439                                                                                              (char) (status >> 16),
440                                                                                              (char) (status >> 8),
441                                                                                              (char) status);
442         return FALSE;
443     }
444     
445     memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
446     strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
447     MultiByteToWideChar(CP_ACP, 0, name, sizeof(name), 
448                         CoreAudio_DefaultDevice.out_caps.szPname, 
449                         sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
450     memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
451     
452     propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
453     status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
454     if (status != noErr) {
455         ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
456                                                                                                 (char) (status >> 16),
457                                                                                                 (char) (status >> 8),
458                                                                                                 (char) status);
459         return FALSE;
460     }
461     
462     TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
463             "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
464             "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
465                                CoreAudio_DefaultDevice.streamDescription.mSampleRate,
466                                (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
467                                (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
468                                (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
469                                (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
470                                CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
471                                CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
472                                CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
473                                CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
474                                CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
475                                CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
476     
477     CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
478     CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
479     
480     CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
481     CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
482     CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
483     CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
484     CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
485     
486     CoreAudio_DefaultDevice.out_caps.wChannels = 2;
487     CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
488     
489     return TRUE;
490 }
491
492 /******************************************************************
493 *               CoreAudio_WaveInit
494 *
495 * Initialize CoreAudio_DefaultDevice
496 */
497 LONG CoreAudio_WaveInit(void)
498 {
499     OSStatus status;
500     UInt32 propertySize;
501     CHAR szPname[MAXPNAMELEN];
502     int i;
503     CFStringRef  messageThreadPortName;
504     CFMessagePortRef port_ReceiveInMessageThread;
505     int inputSampleRate;
506
507     TRACE("()\n");
508     
509     /* number of sound cards */
510     AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
511     propertySize /= sizeof(AudioDeviceID);
512     TRACE("sound cards : %lu\n", propertySize);
513     
514     /* Get the output device */
515     propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
516     status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
517     if (status) {
518         ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
519                                                                                                 (char) (status >> 16),
520                                                                                                 (char) (status >> 8),
521                                                                                                 (char) status);
522         return 1;
523     }
524     if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
525         ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
526         return 1;
527     }
528     
529     if ( ! CoreAudio_GetDevCaps() )
530         return 1;
531     
532     CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
533     sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name);
534     
535     for (i = 0; i < MAX_WAVEOUTDRV; ++i)
536     {
537         WOutDev[i].state = WINE_WS_CLOSED;
538         WOutDev[i].cadev = &CoreAudio_DefaultDevice; 
539         WOutDev[i].woID = i;
540         
541         memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
542         
543         WOutDev[i].caps.wMid = 0xcafe;  /* Manufac ID */
544         WOutDev[i].caps.wPid = 0x0001;  /* Product ID */
545         snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
546         MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
547         snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
548         
549         WOutDev[i].caps.vDriverVersion = 0x0001;
550         WOutDev[i].caps.dwFormats = 0x00000000;
551         WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
552         
553         WOutDev[i].caps.wChannels = 2;
554       /*  WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
555         
556         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
557         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
558         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
559         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
560         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
561         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
562         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
563         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
564         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
565         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08; 
566         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
567         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
568         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
569         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08; 
570         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
571         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
572         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
573         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
574         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
575         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
576
577         WOutDev[i].lock = 0; /* initialize the mutex */
578     }
579
580     /* FIXME: implement sample rate conversion on input */
581     inputSampleRate = AudioUnit_GetInputDeviceSampleRate();
582
583     for (i = 0; i < MAX_WAVEINDRV; ++i)
584     {
585         memset(&WInDev[i], 0, sizeof(WInDev[i]));
586         WInDev[i].wiID = i;
587
588         /* Establish preconditions for widOpen */
589         WInDev[i].state = WINE_WS_CLOSED;
590         WInDev[i].lock = 0; /* initialize the mutex */
591
592         /* Fill in capabilities.  widGetDevCaps can be called at any time. */
593         WInDev[i].caps.wMid = 0xcafe;   /* Manufac ID */
594         WInDev[i].caps.wPid = 0x0001;   /* Product ID */
595         WInDev[i].caps.vDriverVersion = 0x0001;
596
597         snprintf(szPname, sizeof(szPname), "CoreAudio WaveIn %d", i);
598         MultiByteToWideChar(CP_ACP, 0, szPname, -1, WInDev[i].caps.szPname, sizeof(WInDev[i].caps.szPname)/sizeof(WCHAR));
599         snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "winecoreaudio in: %d", i);
600
601         if (inputSampleRate == 96000)
602         {
603             WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
604             WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
605             WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
606             WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
607         }
608         if (inputSampleRate == 48000)
609         {
610             WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
611             WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
612             WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
613             WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
614         }
615         if (inputSampleRate == 44100)
616         {
617             WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
618             WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
619             WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
620             WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
621         }
622         if (inputSampleRate == 22050)
623         {
624             WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
625             WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
626             WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
627             WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
628         }
629         if (inputSampleRate == 11025)
630         {
631             WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
632             WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
633             WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
634             WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
635         }
636
637         WInDev[i].caps.wChannels = 2;
638     }
639
640     /* create mach messages handler */
641     srandomdev();
642     messageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
643         CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
644     if (!messageThreadPortName)
645     {
646         ERR("Can't create message thread port name\n");
647         return 1;
648     }
649
650     port_ReceiveInMessageThread = CFMessagePortCreateLocal(kCFAllocatorDefault, messageThreadPortName,
651                                         &wodMessageHandler, NULL, NULL);
652     if (!port_ReceiveInMessageThread)
653     {
654         ERR("Can't create message thread local port\n");
655         CFRelease(messageThreadPortName);
656         return 1;
657     }
658
659     Port_SendToMessageThread = CFMessagePortCreateRemote(kCFAllocatorDefault, messageThreadPortName);
660     CFRelease(messageThreadPortName);
661     if (!Port_SendToMessageThread)
662     {
663         ERR("Can't create port for sending to message thread\n");
664         CFRelease(port_ReceiveInMessageThread);
665         return 1;
666     }
667
668     /* Cannot WAIT for any events because we are called from the loader (which has a lock on loading stuff) */
669     /* We might want to wait for this thread to be created -- but we cannot -- not here at least */
670     /* Instead track the thread so we can clean it up later */
671     if ( hThread )
672     {
673         ERR("Message thread already started -- expect problems\n");
674     }
675     hThread = CreateThread(NULL, 0, messageThread, (LPVOID)port_ReceiveInMessageThread, 0, NULL);
676     if ( !hThread )
677     {
678         ERR("Can't create message thread\n");
679         CFRelease(port_ReceiveInMessageThread);
680         CFRelease(Port_SendToMessageThread);
681         Port_SendToMessageThread = NULL;
682         return 1;
683     }
684
685     /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
686
687     return 0;
688 }
689
690 void CoreAudio_WaveRelease(void)
691 {
692     /* Stop CFRunLoop in messageThread */
693     TRACE("()\n");
694
695     CFMessagePortSendRequest(Port_SendToMessageThread, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
696     CFRelease(Port_SendToMessageThread);
697     Port_SendToMessageThread = NULL;
698
699     /* Wait for the thread to finish and clean it up */
700     /* This rids us of any quick start/shutdown driver crashes */
701     WaitForSingleObject(hThread, INFINITE);
702     CloseHandle(hThread);
703     hThread = NULL;
704 }
705
706 /*======================================================================*
707 *                  Low level WAVE OUT implementation                    *
708 *======================================================================*/
709
710 /**************************************************************************
711 *                       wodNotifyClient                 [internal]
712 */
713 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
714 {
715     switch (wMsg) {
716         case WOM_OPEN:
717         case WOM_CLOSE:
718         case WOM_DONE:
719             if (wwo->wFlags != DCB_NULL &&
720                 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
721                                 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
722                                 dwParam1, dwParam2))
723             {
724                 return MMSYSERR_ERROR;
725             }
726             break;
727         default:
728             return MMSYSERR_INVALPARAM;
729     }
730     return MMSYSERR_NOERROR;
731 }
732
733
734 /**************************************************************************
735 *                       wodGetDevCaps               [internal]
736 */
737 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
738 {
739     TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
740     
741     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
742     
743     if (wDevID >= MAX_WAVEOUTDRV)
744     {
745         TRACE("MAX_WAVOUTDRV reached !\n");
746         return MMSYSERR_BADDEVICEID;
747     }
748     
749     TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
750     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
751     return MMSYSERR_NOERROR;
752 }
753
754 /**************************************************************************
755 *                               wodOpen                         [internal]
756 */
757 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
758 {
759     WINE_WAVEOUT*       wwo;
760     DWORD retval;
761     DWORD               ret;
762     AudioStreamBasicDescription streamFormat;
763
764     TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
765     if (lpDesc == NULL)
766     {
767         WARN("Invalid Parameter !\n");
768         return MMSYSERR_INVALPARAM;
769     }
770     if (wDevID >= MAX_WAVEOUTDRV) {
771         TRACE("MAX_WAVOUTDRV reached !\n");
772         return MMSYSERR_BADDEVICEID;
773     }
774     
775     TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
776           lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
777           lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
778     
779     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
780         lpDesc->lpFormat->nChannels == 0 ||
781         lpDesc->lpFormat->nSamplesPerSec == 0
782          )
783     {
784         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
785              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
786              lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
787         return WAVERR_BADFORMAT;
788     }
789     
790     if (dwFlags & WAVE_FORMAT_QUERY)
791     {
792         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
793               lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
794               lpDesc->lpFormat->nSamplesPerSec);
795         return MMSYSERR_NOERROR;
796     }
797     
798     wwo = &WOutDev[wDevID];
799     if (!OSSpinLockTry(&wwo->lock))
800         return MMSYSERR_ALLOCATED;
801
802     if (wwo->state != WINE_WS_CLOSED)
803     {
804         OSSpinLockUnlock(&wwo->lock);
805         return MMSYSERR_ALLOCATED;
806     }
807
808     if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
809     {
810         ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
811         OSSpinLockUnlock(&wwo->lock);
812         return MMSYSERR_ERROR;
813     }
814
815     if ((dwFlags & WAVE_DIRECTSOUND) && 
816         !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
817         /* not supported, ignore it */
818         dwFlags &= ~WAVE_DIRECTSOUND;
819
820     streamFormat.mFormatID = kAudioFormatLinearPCM;
821     streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
822     /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
823     if (lpDesc->lpFormat->wBitsPerSample != 8)
824         streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
825 # ifdef WORDS_BIGENDIAN
826     streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
827 # endif
828
829     streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
830     streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;       
831     streamFormat.mFramesPerPacket = 1;  
832     streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
833     streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;    
834     streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;         
835
836     ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, &streamFormat);
837     if (!ret) 
838     {
839         AudioUnit_CloseAudioUnit(wwo->audioUnit);
840         OSSpinLockUnlock(&wwo->lock);
841         return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
842     }
843     wwo->streamDescription = streamFormat;
844
845     ret = AudioOutputUnitStart(wwo->audioUnit);
846     if (ret)
847     {
848         ERR("AudioOutputUnitStart failed: %08x\n", ret);
849         AudioUnitUninitialize(wwo->audioUnit);
850         AudioUnit_CloseAudioUnit(wwo->audioUnit);
851         OSSpinLockUnlock(&wwo->lock);
852         return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
853     }
854
855     wwo->state = WINE_WS_STOPPED;
856                 
857     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
858     
859     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
860     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
861     
862     if (wwo->format.wBitsPerSample == 0) {
863         WARN("Resetting zeroed wBitsPerSample\n");
864         wwo->format.wBitsPerSample = 8 *
865             (wwo->format.wf.nAvgBytesPerSec /
866              wwo->format.wf.nSamplesPerSec) /
867             wwo->format.wf.nChannels;
868     }
869     
870     wwo->dwPlayedTotal = 0;
871     wwo->dwWrittenTotal = 0;
872
873     wwo->trace_on = TRACE_ON(wave);
874     wwo->warn_on  = WARN_ON(wave);
875     wwo->err_on   = ERR_ON(wave);
876
877     OSSpinLockUnlock(&wwo->lock);
878     
879     retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
880     
881     return retval;
882 }
883
884 /**************************************************************************
885 *                               wodClose                        [internal]
886 */
887 static DWORD wodClose(WORD wDevID)
888 {
889     DWORD               ret = MMSYSERR_NOERROR;
890     WINE_WAVEOUT*       wwo;
891     
892     TRACE("(%u);\n", wDevID);
893     
894     if (wDevID >= MAX_WAVEOUTDRV)
895     {
896         WARN("bad device ID !\n");
897         return MMSYSERR_BADDEVICEID;
898     }
899     
900     wwo = &WOutDev[wDevID];
901     OSSpinLockLock(&wwo->lock);
902     if (wwo->lpQueuePtr)
903     {
904         WARN("buffers still playing !\n");
905         OSSpinLockUnlock(&wwo->lock);
906         ret = WAVERR_STILLPLAYING;
907     } else
908     {
909         OSStatus err;
910         /* sanity check: this should not happen since the device must have been reset before */
911         if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
912         
913         wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
914         
915         OSSpinLockUnlock(&wwo->lock);
916
917         err = AudioUnitUninitialize(wwo->audioUnit);
918         if (err) {
919             ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
920                                                             (char) (err >> 16),
921                                                             (char) (err >> 8),
922                                                             (char) err);
923             return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
924         }
925         
926         if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
927         {
928             ERR("Can't close AudioUnit\n");
929             return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
930         }  
931         
932         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
933     }
934     
935     return ret;
936 }
937
938 /**************************************************************************
939 *                               wodPrepare                      [internal]
940 */
941 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
942 {
943     TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
944     
945     if (wDevID >= MAX_WAVEOUTDRV) {
946         WARN("bad device ID !\n");
947         return MMSYSERR_BADDEVICEID;
948     }
949     
950     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
951         return WAVERR_STILLPLAYING;
952     
953     lpWaveHdr->dwFlags |= WHDR_PREPARED;
954     lpWaveHdr->dwFlags &= ~WHDR_DONE;
955
956     return MMSYSERR_NOERROR;
957 }
958
959 /**************************************************************************
960 *                               wodUnprepare                    [internal]
961 */
962 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
963 {
964     TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
965     
966     if (wDevID >= MAX_WAVEOUTDRV) {
967         WARN("bad device ID !\n");
968         return MMSYSERR_BADDEVICEID;
969     }
970     
971     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
972         return WAVERR_STILLPLAYING;
973     
974     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
975     lpWaveHdr->dwFlags |= WHDR_DONE;
976    
977     return MMSYSERR_NOERROR;
978 }
979
980
981 /**************************************************************************
982 *                               wodHelper_CheckForLoopBegin             [internal]
983 *
984 * Check if the new waveheader is the beginning of a loop, and set up
985 * state if so.
986 * This is called with the WAVEOUT lock held.
987 * Call from AudioUnit IO thread can't use Wine debug channels.
988 */
989 static void wodHelper_CheckForLoopBegin(WINE_WAVEOUT* wwo)
990 {
991     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
992
993     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
994     {
995         if (wwo->lpLoopPtr)
996         {
997             if (wwo->warn_on)
998                 fprintf(stderr, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
999         }
1000         else
1001         {
1002             if (wwo->trace_on)
1003                 fprintf(stderr, "trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1004
1005             wwo->lpLoopPtr = lpWaveHdr;
1006             /* Windows does not touch WAVEHDR.dwLoops,
1007                 * so we need to make an internal copy */
1008             wwo->dwLoops = lpWaveHdr->dwLoops;
1009         }
1010     }
1011 }
1012
1013
1014 /**************************************************************************
1015 *                               wodHelper_PlayPtrNext           [internal]
1016 *
1017 * Advance the play pointer to the next waveheader, looping if required.
1018 * This is called with the WAVEOUT lock held.
1019 * Call from AudioUnit IO thread can't use Wine debug channels.
1020 */
1021 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
1022 {
1023     BOOL didLoopBack = FALSE;
1024
1025     wwo->dwPartialOffset = 0;
1026     if ((wwo->lpPlayPtr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
1027     {
1028         /* We're at the end of a loop, loop if required */
1029         if (wwo->dwLoops > 1)
1030         {
1031             wwo->dwLoops--;
1032             wwo->lpPlayPtr = wwo->lpLoopPtr;
1033             didLoopBack = TRUE;
1034         }
1035         else
1036         {
1037             wwo->lpLoopPtr = NULL;
1038         }
1039     }
1040     if (!didLoopBack)
1041     {
1042         /* We didn't loop back.  Advance to the next wave header */
1043         wwo->lpPlayPtr = wwo->lpPlayPtr->lpNext;
1044
1045         if (!wwo->lpPlayPtr)
1046             wwo->state = WINE_WS_STOPPED;
1047         else
1048             wodHelper_CheckForLoopBegin(wwo);
1049     }
1050 }
1051
1052 /* if force is TRUE then notify the client that all the headers were completed
1053  */
1054 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1055 {
1056     LPWAVEHDR           lpWaveHdr;
1057     LPWAVEHDR           lpFirstDoneWaveHdr = NULL;
1058
1059     OSSpinLockLock(&wwo->lock);
1060
1061     /* First, excise all of the done headers from the queue into
1062      * a free-standing list. */
1063     if (force)
1064     {
1065         lpFirstDoneWaveHdr = wwo->lpQueuePtr;
1066         wwo->lpQueuePtr = NULL;
1067     }
1068     else
1069     {
1070         LPWAVEHDR lpLastDoneWaveHdr = NULL;
1071
1072         /* Start from lpQueuePtr and keep notifying until:
1073             * - we hit an unwritten wavehdr
1074             * - we hit the beginning of a running loop
1075             * - we hit a wavehdr which hasn't finished playing
1076             */
1077         for (
1078             lpWaveHdr = wwo->lpQueuePtr;
1079             lpWaveHdr &&
1080                 lpWaveHdr != wwo->lpPlayPtr &&
1081                 lpWaveHdr != wwo->lpLoopPtr;
1082             lpWaveHdr = lpWaveHdr->lpNext
1083             )
1084         {
1085             if (!lpFirstDoneWaveHdr)
1086                 lpFirstDoneWaveHdr = lpWaveHdr;
1087             lpLastDoneWaveHdr = lpWaveHdr;
1088         }
1089
1090         if (lpLastDoneWaveHdr)
1091         {
1092             wwo->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1093             lpLastDoneWaveHdr->lpNext = NULL;
1094         }
1095     }
1096     
1097     OSSpinLockUnlock(&wwo->lock);
1098
1099     /* Now, send the "done" notification for each header in our list. */
1100     for (lpWaveHdr = lpFirstDoneWaveHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
1101     {
1102         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1103         lpWaveHdr->dwFlags |= WHDR_DONE;
1104
1105         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1106     }
1107 }
1108
1109
1110 /**************************************************************************
1111 *                               wodWrite                        [internal]
1112
1113 */
1114 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1115 {
1116     LPWAVEHDR*wh;
1117     WINE_WAVEOUT *wwo;
1118     
1119     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1120     
1121     /* first, do the sanity checks... */
1122     if (wDevID >= MAX_WAVEOUTDRV)
1123     {
1124         WARN("bad dev ID !\n");
1125         return MMSYSERR_BADDEVICEID;
1126     }
1127     
1128     wwo = &WOutDev[wDevID];
1129     
1130     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1131     {
1132         TRACE("unprepared\n");
1133         return WAVERR_UNPREPARED;
1134     }
1135     
1136     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) 
1137     {
1138         TRACE("still playing\n");
1139         return WAVERR_STILLPLAYING;
1140     }
1141     
1142     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1143     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1144     lpWaveHdr->lpNext = 0;
1145
1146     OSSpinLockLock(&wwo->lock);
1147     /* insert buffer at the end of queue */
1148     for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1149         /* Do nothing */;
1150     *wh = lpWaveHdr;
1151     
1152     if (!wwo->lpPlayPtr)
1153     {
1154         wwo->lpPlayPtr = lpWaveHdr;
1155
1156         if (wwo->state == WINE_WS_STOPPED)
1157             wwo->state = WINE_WS_PLAYING;
1158
1159         wodHelper_CheckForLoopBegin(wwo);
1160
1161         wwo->dwPartialOffset = 0;
1162     }
1163     OSSpinLockUnlock(&wwo->lock);
1164
1165     return MMSYSERR_NOERROR;
1166 }
1167
1168 /**************************************************************************
1169 *                       wodPause                                [internal]
1170 */
1171 static DWORD wodPause(WORD wDevID)
1172 {
1173     OSStatus status;
1174
1175     TRACE("(%u);!\n", wDevID);
1176     
1177     if (wDevID >= MAX_WAVEOUTDRV)
1178     {
1179         WARN("bad device ID !\n");
1180         return MMSYSERR_BADDEVICEID;
1181     }
1182
1183     /* The order of the following operations is important since we can't hold
1184      * the mutex while we make an Audio Unit call.  Stop the Audio Unit before
1185      * setting the PAUSED state.  In wodRestart, the order is reversed.  This
1186      * guarantees that we can't get into a situation where the state is
1187      * PLAYING or STOPPED but the Audio Unit isn't running.  Although we can
1188      * be in PAUSED state with the Audio Unit still running, that's harmless
1189      * because the render callback will just produce silence.
1190      */
1191     status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1192     if (status) {
1193         WARN("AudioOutputUnitStop return %c%c%c%c\n",
1194              (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1195     }
1196
1197     OSSpinLockLock(&WOutDev[wDevID].lock);
1198     if (WOutDev[wDevID].state == WINE_WS_PLAYING || WOutDev[wDevID].state == WINE_WS_STOPPED)
1199         WOutDev[wDevID].state = WINE_WS_PAUSED;
1200     OSSpinLockUnlock(&WOutDev[wDevID].lock);
1201
1202     return MMSYSERR_NOERROR;
1203 }
1204
1205 /**************************************************************************
1206 *                       wodRestart                              [internal]
1207 */
1208 static DWORD wodRestart(WORD wDevID)
1209 {
1210     OSStatus status;
1211
1212     TRACE("(%u);\n", wDevID);
1213     
1214     if (wDevID >= MAX_WAVEOUTDRV )
1215     {
1216         WARN("bad device ID !\n");
1217         return MMSYSERR_BADDEVICEID;
1218     }
1219
1220     /* The order of the following operations is important since we can't hold
1221      * the mutex while we make an Audio Unit call.  Set the PLAYING/STOPPED
1222      * state before starting the Audio Unit.  In wodPause, the order is
1223      * reversed.  This guarantees that we can't get into a situation where
1224      * the state is PLAYING or STOPPED but the Audio Unit isn't running.
1225      * Although we can be in PAUSED state with the Audio Unit still running,
1226      * that's harmless because the render callback will just produce silence.
1227      */
1228     OSSpinLockLock(&WOutDev[wDevID].lock);
1229     if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1230     {
1231         if (WOutDev[wDevID].lpPlayPtr)
1232             WOutDev[wDevID].state = WINE_WS_PLAYING;
1233         else
1234             WOutDev[wDevID].state = WINE_WS_STOPPED;
1235     }
1236     OSSpinLockUnlock(&WOutDev[wDevID].lock);
1237
1238     status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1239     if (status) {
1240         ERR("AudioOutputUnitStart return %c%c%c%c\n",
1241             (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1242         return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1243     }
1244
1245     return MMSYSERR_NOERROR;
1246 }
1247
1248 /**************************************************************************
1249 *                       wodReset                                [internal]
1250 */
1251 static DWORD wodReset(WORD wDevID)
1252 {
1253     WINE_WAVEOUT* wwo;
1254     OSStatus status;
1255
1256     TRACE("(%u);\n", wDevID);
1257
1258     if (wDevID >= MAX_WAVEOUTDRV)
1259     {
1260         WARN("bad device ID !\n");
1261         return MMSYSERR_BADDEVICEID;
1262     }
1263
1264     wwo = &WOutDev[wDevID];
1265
1266     /* updates current notify list */
1267     /* if resetting, remove all wave headers and notify client that all headers were completed */
1268     wodHelper_NotifyCompletions(wwo, TRUE);
1269     
1270     OSSpinLockLock(&wwo->lock);
1271     
1272     wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1273     wwo->state = WINE_WS_STOPPED;
1274     wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1275
1276     wwo->dwPartialOffset = 0;        /* Clear partial wavehdr */
1277
1278     OSSpinLockUnlock(&wwo->lock);
1279
1280     status = AudioOutputUnitStart(wwo->audioUnit);
1281
1282     if (status) {
1283         ERR( "AudioOutputUnitStart return %c%c%c%c\n",
1284              (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1285         return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1286     }
1287
1288     return MMSYSERR_NOERROR;
1289 }
1290
1291 /**************************************************************************
1292 *                               wodGetPosition                  [internal]
1293 */
1294 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1295 {
1296     DWORD               val;
1297     WINE_WAVEOUT*       wwo;
1298
1299     TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1300     
1301     if (wDevID >= MAX_WAVEOUTDRV)
1302     {
1303         WARN("bad device ID !\n");
1304         return MMSYSERR_BADDEVICEID;
1305     }
1306     
1307     /* if null pointer to time structure return error */
1308     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1309     
1310     wwo = &WOutDev[wDevID];
1311     
1312     OSSpinLockLock(&WOutDev[wDevID].lock);
1313     val = wwo->dwPlayedTotal;
1314     OSSpinLockUnlock(&WOutDev[wDevID].lock);
1315     
1316     return bytes_to_mmtime(lpTime, val, &wwo->format);
1317 }
1318
1319 /**************************************************************************
1320 *                               wodGetVolume                    [internal]
1321 */
1322 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1323 {
1324     float left;
1325     float right;
1326     
1327     if (wDevID >= MAX_WAVEOUTDRV)
1328     {
1329         WARN("bad device ID !\n");
1330         return MMSYSERR_BADDEVICEID;
1331     }    
1332     
1333     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1334
1335     AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right); 
1336
1337     *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1338     
1339     return MMSYSERR_NOERROR;
1340 }
1341
1342 /**************************************************************************
1343 *                               wodSetVolume                    [internal]
1344 */
1345 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1346 {
1347     float left;
1348     float right;
1349     
1350     if (wDevID >= MAX_WAVEOUTDRV)
1351     {
1352         WARN("bad device ID !\n");
1353         return MMSYSERR_BADDEVICEID;
1354     }
1355
1356     left  = LOWORD(dwParam) / 65535.0f;
1357     right = HIWORD(dwParam) / 65535.0f;
1358     
1359     TRACE("(%u, %08x);\n", wDevID, dwParam);
1360
1361     AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right); 
1362
1363     return MMSYSERR_NOERROR;
1364 }
1365
1366 /**************************************************************************
1367 *                               wodGetNumDevs                   [internal]
1368 */
1369 static DWORD wodGetNumDevs(void)
1370 {
1371     TRACE("\n");
1372     return MAX_WAVEOUTDRV;
1373 }
1374
1375 /**************************************************************************
1376 *                              wodDevInterfaceSize             [internal]
1377 */
1378 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1379 {
1380     TRACE("(%u, %p)\n", wDevID, dwParam1);
1381     
1382     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1383                                     NULL, 0 ) * sizeof(WCHAR);
1384     return MMSYSERR_NOERROR;
1385 }
1386
1387 /**************************************************************************
1388 *                              wodDevInterface                 [internal]
1389 */
1390 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1391 {
1392     TRACE("\n");
1393     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1394                                         NULL, 0 ) * sizeof(WCHAR))
1395     {
1396         MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1397                             dwParam1, dwParam2 / sizeof(WCHAR));
1398         return MMSYSERR_NOERROR;
1399     }
1400     return MMSYSERR_INVALPARAM;
1401 }
1402
1403 /**************************************************************************
1404 *                               wodMessage (WINECOREAUDIO.7)
1405 */
1406 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1407                                   DWORD dwParam1, DWORD dwParam2)
1408 {
1409     TRACE("(%u, %s, %08x, %08x, %08x);\n",
1410           wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1411     
1412     switch (wMsg) {
1413         case DRVM_INIT:
1414         case DRVM_EXIT:
1415         case DRVM_ENABLE:
1416         case DRVM_DISABLE:
1417             
1418             /* FIXME: Pretend this is supported */
1419             return 0;
1420         case WODM_OPEN:         return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);          
1421         case WODM_CLOSE:        return wodClose(wDevID);
1422         case WODM_WRITE:        return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1423         case WODM_PAUSE:        return wodPause(wDevID);  
1424         case WODM_GETPOS:       return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1425         case WODM_BREAKLOOP:    return MMSYSERR_NOTSUPPORTED;
1426         case WODM_PREPARE:      return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1427         case WODM_UNPREPARE:    return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1428             
1429         case WODM_GETDEVCAPS:   return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1430         case WODM_GETNUMDEVS:   return wodGetNumDevs();  
1431             
1432         case WODM_GETPITCH:         
1433         case WODM_SETPITCH:        
1434         case WODM_GETPLAYBACKRATE:      
1435         case WODM_SETPLAYBACKRATE:      return MMSYSERR_NOTSUPPORTED;
1436         case WODM_GETVOLUME:    return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1437         case WODM_SETVOLUME:    return wodSetVolume(wDevID, dwParam1);
1438         case WODM_RESTART:      return wodRestart(wDevID);
1439         case WODM_RESET:        return wodReset(wDevID);
1440             
1441         case DRV_QUERYDEVICEINTERFACESIZE:  return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1442         case DRV_QUERYDEVICEINTERFACE:      return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1443         case DRV_QUERYDSOUNDIFACE:      
1444         case DRV_QUERYDSOUNDDESC:       
1445             return MMSYSERR_NOTSUPPORTED;
1446             
1447         default:
1448             FIXME("unknown message %d!\n", wMsg);
1449     }
1450     
1451     return MMSYSERR_NOTSUPPORTED;
1452 }
1453
1454 /*======================================================================*
1455 *                  Low level DSOUND implementation                      *
1456 *======================================================================*/
1457
1458 typedef struct IDsDriverImpl IDsDriverImpl;
1459 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1460
1461 struct IDsDriverImpl
1462 {
1463     /* IUnknown fields */
1464     const IDsDriverVtbl *lpVtbl;
1465     DWORD               ref;
1466     /* IDsDriverImpl fields */
1467     UINT                wDevID;
1468     IDsDriverBufferImpl*primary;
1469 };
1470
1471 struct IDsDriverBufferImpl
1472 {
1473     /* IUnknown fields */
1474     const IDsDriverBufferVtbl *lpVtbl;
1475     DWORD ref;
1476     /* IDsDriverBufferImpl fields */
1477     IDsDriverImpl* drv;
1478     DWORD buflen;
1479 };
1480
1481
1482 /*
1483     CoreAudio IO threaded callback,
1484     we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1485 */
1486 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, 
1487                                      AudioUnitRenderActionFlags *ioActionFlags, 
1488                                      const AudioTimeStamp *inTimeStamp, 
1489                                      UInt32 inBusNumber, 
1490                                      UInt32 inNumberFrames, 
1491                                      AudioBufferList *ioData)
1492 {
1493     UInt32 buffer;
1494     WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1495     int needNotify = 0;
1496
1497     unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1498     unsigned int dataProvided = 0;
1499
1500     OSSpinLockLock(&wwo->lock);
1501
1502     while (dataNeeded > 0 && wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1503     {
1504         unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1505         unsigned int toCopy;
1506
1507         if (available >= dataNeeded)
1508             toCopy = dataNeeded;
1509         else
1510             toCopy = available;
1511
1512         if (toCopy > 0)
1513         {
1514             memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1515                 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1516             wwo->dwPartialOffset += toCopy;
1517             wwo->dwPlayedTotal += toCopy;
1518             dataProvided += toCopy;
1519             dataNeeded -= toCopy;
1520             available -= toCopy;
1521         }
1522
1523         if (available == 0)
1524         {
1525             wodHelper_PlayPtrNext(wwo);
1526             needNotify = 1;
1527         }
1528     }
1529
1530     OSSpinLockUnlock(&wwo->lock);
1531
1532     /* We can't provide any more wave data.  Fill the rest with silence. */
1533     if (dataNeeded > 0)
1534     {
1535         if (!dataProvided)
1536             *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
1537         memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1538         dataProvided += dataNeeded;
1539         dataNeeded = 0;
1540     }
1541
1542     /* We only fill buffer 0.  Set any others that might be requested to 0. */
1543     for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1544     {
1545         memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1546     }
1547
1548     if (needNotify) wodSendNotifyCompletionsMessage(wwo);
1549     return noErr;
1550 }
1551
1552
1553 /*======================================================================*
1554  *                  Low level WAVE IN implementation                    *
1555  *======================================================================*/
1556
1557 /**************************************************************************
1558  *                      widNotifyClient                 [internal]
1559  */
1560 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1561 {
1562     TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
1563
1564     switch (wMsg)
1565     {
1566         case WIM_OPEN:
1567         case WIM_CLOSE:
1568         case WIM_DATA:
1569             if (wwi->wFlags != DCB_NULL &&
1570                 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1571                                 (HDRVR)wwi->waveDesc.hWave, wMsg, wwi->waveDesc.dwInstance,
1572                                 dwParam1, dwParam2))
1573             {
1574                 WARN("can't notify client !\n");
1575                 return MMSYSERR_ERROR;
1576             }
1577             break;
1578         default:
1579             FIXME("Unknown callback message %u\n", wMsg);
1580             return MMSYSERR_INVALPARAM;
1581     }
1582     return MMSYSERR_NOERROR;
1583 }
1584
1585
1586 /**************************************************************************
1587  *                      widHelper_NotifyCompletions              [internal]
1588  */
1589 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi)
1590 {
1591     LPWAVEHDR       lpWaveHdr;
1592     LPWAVEHDR       lpFirstDoneWaveHdr = NULL;
1593     LPWAVEHDR       lpLastDoneWaveHdr = NULL;
1594
1595     OSSpinLockLock(&wwi->lock);
1596
1597     /* First, excise all of the done headers from the queue into
1598      * a free-standing list. */
1599
1600     /* Start from lpQueuePtr and keep notifying until:
1601         * - we hit an unfilled wavehdr
1602         * - we hit the end of the list
1603         */
1604     for (
1605         lpWaveHdr = wwi->lpQueuePtr;
1606         lpWaveHdr &&
1607             lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength;
1608         lpWaveHdr = lpWaveHdr->lpNext
1609         )
1610     {
1611         if (!lpFirstDoneWaveHdr)
1612             lpFirstDoneWaveHdr = lpWaveHdr;
1613         lpLastDoneWaveHdr = lpWaveHdr;
1614     }
1615
1616     if (lpLastDoneWaveHdr)
1617     {
1618         wwi->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1619         lpLastDoneWaveHdr->lpNext = NULL;
1620     }
1621
1622     OSSpinLockUnlock(&wwi->lock);
1623
1624     /* Now, send the "done" notification for each header in our list. */
1625     for (lpWaveHdr = lpFirstDoneWaveHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
1626     {
1627         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1628         lpWaveHdr->dwFlags |= WHDR_DONE;
1629
1630         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1631     }
1632 }
1633
1634
1635 /**************************************************************************
1636  *                      widGetDevCaps                           [internal]
1637  */
1638 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1639 {
1640     TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1641
1642     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1643
1644     if (wDevID >= MAX_WAVEINDRV)
1645     {
1646         TRACE("MAX_WAVEINDRV reached !\n");
1647         return MMSYSERR_BADDEVICEID;
1648     }
1649
1650     memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1651     return MMSYSERR_NOERROR;
1652 }
1653
1654
1655 /**************************************************************************
1656  *                    widHelper_DestroyAudioBufferList           [internal]
1657  * Convenience function to dispose of our audio buffers
1658  */
1659 static void widHelper_DestroyAudioBufferList(AudioBufferList* list)
1660 {
1661     if (list)
1662     {
1663         UInt32 i;
1664         for (i = 0; i < list->mNumberBuffers; i++)
1665         {
1666             if (list->mBuffers[i].mData)
1667                 HeapFree(GetProcessHeap(), 0, list->mBuffers[i].mData);
1668         }
1669         HeapFree(GetProcessHeap(), 0, list);
1670     }
1671 }
1672
1673
1674 /**************************************************************************
1675  *                    widHelper_AllocateAudioBufferList          [internal]
1676  * Convenience function to allocate our audio buffers
1677  */
1678 static AudioBufferList* widHelper_AllocateAudioBufferList(UInt32 numChannels, UInt32 size)
1679 {
1680     AudioBufferList*            list;
1681     UInt32                      i;
1682
1683     list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
1684     if (list == NULL)
1685         return NULL;
1686
1687     list->mNumberBuffers = numChannels;
1688     for (i = 0; i < numChannels; ++i)
1689     {
1690         list->mBuffers[i].mNumberChannels = 1;
1691         list->mBuffers[i].mDataByteSize = size;
1692         list->mBuffers[i].mData = HeapAlloc(GetProcessHeap(), 0, size);
1693         if (list->mBuffers[i].mData == NULL)
1694         {
1695             widHelper_DestroyAudioBufferList(list);
1696             return NULL;
1697         }
1698     }
1699     return list;
1700 }
1701
1702
1703 /**************************************************************************
1704  *                              widOpen                         [internal]
1705  */
1706 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1707 {
1708     WINE_WAVEIN*    wwi;
1709     UInt32          frameCount;
1710     UInt32          bytesPerFrame;
1711
1712     TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1713     if (lpDesc == NULL)
1714     {
1715         WARN("Invalid Parameter !\n");
1716         return MMSYSERR_INVALPARAM;
1717     }
1718     if (wDevID >= MAX_WAVEINDRV)
1719     {
1720         TRACE ("MAX_WAVEINDRV reached !\n");
1721         return MMSYSERR_BADDEVICEID;
1722     }
1723
1724     TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1725           lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1726           lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1727
1728     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1729         lpDesc->lpFormat->nChannels == 0 ||
1730         lpDesc->lpFormat->nSamplesPerSec == 0
1731         )
1732     {
1733         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1734              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1735              lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1736         return WAVERR_BADFORMAT;
1737     }
1738
1739     if (dwFlags & WAVE_FORMAT_QUERY)
1740     {
1741         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1742               lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1743               lpDesc->lpFormat->nSamplesPerSec);
1744         return MMSYSERR_NOERROR;
1745     }
1746
1747     wwi = &WInDev[wDevID];
1748     if (!OSSpinLockTry(&wwi->lock))
1749         return MMSYSERR_ALLOCATED;
1750
1751     if (wwi->state != WINE_WS_CLOSED)
1752     {
1753         OSSpinLockUnlock(&wwi->lock);
1754         return MMSYSERR_ALLOCATED;
1755     }
1756
1757     wwi->state = WINE_WS_STOPPED;
1758     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1759
1760     memcpy(&wwi->waveDesc, lpDesc,              sizeof(WAVEOPENDESC));
1761     memcpy(&wwi->format,   lpDesc->lpFormat,    sizeof(PCMWAVEFORMAT));
1762
1763     if (wwi->format.wBitsPerSample == 0)
1764     {
1765         WARN("Resetting zeroed wBitsPerSample\n");
1766         wwi->format.wBitsPerSample = 8 *
1767             (wwi->format.wf.nAvgBytesPerSec /
1768             wwi->format.wf.nSamplesPerSec) /
1769             wwi->format.wf.nChannels;
1770     }
1771
1772     wwi->dwTotalRecorded = 0;
1773
1774     wwi->trace_on = TRACE_ON(wave);
1775     wwi->warn_on  = WARN_ON(wave);
1776     wwi->err_on   = ERR_ON(wave);
1777
1778     if (!AudioUnit_CreateInputUnit(wwi, &wwi->audioUnit,
1779         wwi->format.wf.nChannels, wwi->format.wf.nSamplesPerSec,
1780         wwi->format.wBitsPerSample, &frameCount))
1781     {
1782         ERR("AudioUnit_CreateInputUnit failed\n");
1783         OSSpinLockUnlock(&wwi->lock);
1784         return MMSYSERR_ERROR;
1785     }
1786
1787     /* Allocate our audio buffers */
1788     /* For interleaved audio, we allocate one buffer for all channels. */
1789     bytesPerFrame = wwi->format.wBitsPerSample * wwi->format.wf.nChannels / 8;
1790     wwi->bufferList = widHelper_AllocateAudioBufferList(1, wwi->format.wf.nChannels * frameCount * bytesPerFrame);
1791     if (wwi->bufferList == NULL)
1792     {
1793         ERR("Failed to allocate buffer list\n");
1794         AudioUnitUninitialize(wwi->audioUnit);
1795         AudioUnit_CloseAudioUnit(wwi->audioUnit);
1796         OSSpinLockUnlock(&wwi->lock);
1797         return MMSYSERR_NOMEM;
1798     }
1799
1800     OSSpinLockUnlock(&wwi->lock);
1801
1802     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1803 }
1804
1805
1806 /**************************************************************************
1807  *                              widClose                        [internal]
1808  */
1809 static DWORD widClose(WORD wDevID)
1810 {
1811     DWORD           ret = MMSYSERR_NOERROR;
1812     WINE_WAVEIN*    wwi;
1813
1814     TRACE("(%u);\n", wDevID);
1815
1816     if (wDevID >= MAX_WAVEINDRV)
1817     {
1818         WARN("bad device ID !\n");
1819         return MMSYSERR_BADDEVICEID;
1820     }
1821
1822     wwi = &WInDev[wDevID];
1823     OSSpinLockLock(&wwi->lock);
1824     if (wwi->state == WINE_WS_CLOSED)
1825     {
1826         WARN("Device already closed.\n");
1827         ret = MMSYSERR_INVALHANDLE;
1828     }
1829     else if (wwi->lpQueuePtr)
1830     {
1831         WARN("Buffers in queue.\n");
1832         ret = WAVERR_STILLPLAYING;
1833     }
1834     else
1835     {
1836         wwi->state = WINE_WS_CLOSED;
1837     }
1838
1839     OSSpinLockUnlock(&wwi->lock);
1840
1841     if (ret == MMSYSERR_NOERROR)
1842     {
1843         OSStatus err = AudioUnitUninitialize(wwi->audioUnit);
1844         if (err)
1845         {
1846             ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
1847                                                            (char) (err >> 16),
1848                                                            (char) (err >> 8),
1849                                                            (char) err);
1850         }
1851
1852         if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
1853         {
1854             ERR("Can't close AudioUnit\n");
1855         }
1856
1857         /* Dellocate our audio buffers */
1858         widHelper_DestroyAudioBufferList(wwi->bufferList);
1859         wwi->bufferList = NULL;
1860
1861         ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1862     }
1863
1864     return ret;
1865 }
1866
1867
1868 /**************************************************************************
1869  *                              widAddBuffer            [internal]
1870  */
1871 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1872 {
1873     DWORD           ret = MMSYSERR_NOERROR;
1874     WINE_WAVEIN*    wwi;
1875
1876     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1877
1878     if (wDevID >= MAX_WAVEINDRV)
1879     {
1880         WARN("invalid device ID\n");
1881         return MMSYSERR_INVALHANDLE;
1882     }
1883     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
1884     {
1885         TRACE("never been prepared !\n");
1886         return WAVERR_UNPREPARED;
1887     }
1888     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1889     {
1890         TRACE("header already in use !\n");
1891         return WAVERR_STILLPLAYING;
1892     }
1893
1894     wwi = &WInDev[wDevID];
1895     OSSpinLockLock(&wwi->lock);
1896
1897     if (wwi->state == WINE_WS_CLOSED)
1898     {
1899         WARN("Trying to add buffer to closed device.\n");
1900         ret = MMSYSERR_INVALHANDLE;
1901     }
1902     else
1903     {
1904         LPWAVEHDR* wh;
1905
1906         lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1907         lpWaveHdr->dwFlags &= ~WHDR_DONE;
1908         lpWaveHdr->dwBytesRecorded = 0;
1909         lpWaveHdr->lpNext = NULL;
1910
1911         /* insert buffer at end of queue */
1912         for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1913             /* Do nothing */;
1914         *wh = lpWaveHdr;
1915     }
1916
1917     OSSpinLockUnlock(&wwi->lock);
1918
1919     return ret;
1920 }
1921
1922
1923 /**************************************************************************
1924  *                      widStart                                [internal]
1925  */
1926 static DWORD widStart(WORD wDevID)
1927 {
1928     DWORD           ret = MMSYSERR_NOERROR;
1929     WINE_WAVEIN*    wwi;
1930
1931     TRACE("(%u);\n", wDevID);
1932     if (wDevID >= MAX_WAVEINDRV)
1933     {
1934         WARN("invalid device ID\n");
1935         return MMSYSERR_INVALHANDLE;
1936     }
1937
1938     /* The order of the following operations is important since we can't hold
1939      * the mutex while we make an Audio Unit call.  Set the PLAYING state
1940      * before starting the Audio Unit.  In widStop, the order is reversed.
1941      * This guarantees that we can't get into a situation where the state is
1942      * PLAYING but the Audio Unit isn't running.  Although we can be in STOPPED
1943      * state with the Audio Unit still running, that's harmless because the
1944      * input callback will just throw away the sound data.
1945      */
1946     wwi = &WInDev[wDevID];
1947     OSSpinLockLock(&wwi->lock);
1948
1949     if (wwi->state == WINE_WS_CLOSED)
1950     {
1951         WARN("Trying to start closed device.\n");
1952         ret = MMSYSERR_INVALHANDLE;
1953     }
1954     else
1955         wwi->state = WINE_WS_PLAYING;
1956
1957     OSSpinLockUnlock(&wwi->lock);
1958
1959     if (ret == MMSYSERR_NOERROR)
1960     {
1961         /* Start pulling for audio data */
1962         OSStatus err = AudioOutputUnitStart(wwi->audioUnit);
1963         if (err != noErr)
1964             ERR("Failed to start AU: %08lx\n", err);
1965
1966         TRACE("Recording started...\n");
1967     }
1968
1969     return ret;
1970 }
1971
1972
1973 /**************************************************************************
1974  *                      widStop                                 [internal]
1975  */
1976 static DWORD widStop(WORD wDevID)
1977 {
1978     DWORD           ret = MMSYSERR_NOERROR;
1979     WINE_WAVEIN*    wwi;
1980     WAVEHDR*        lpWaveHdr = NULL;
1981     OSStatus        err;
1982
1983     TRACE("(%u);\n", wDevID);
1984     if (wDevID >= MAX_WAVEINDRV)
1985     {
1986         WARN("invalid device ID\n");
1987         return MMSYSERR_INVALHANDLE;
1988     }
1989
1990     wwi = &WInDev[wDevID];
1991
1992     /* The order of the following operations is important since we can't hold
1993      * the mutex while we make an Audio Unit call.  Stop the Audio Unit before
1994      * setting the STOPPED state.  In widStart, the order is reversed.  This
1995      * guarantees that we can't get into a situation where the state is
1996      * PLAYING but the Audio Unit isn't running.  Although we can be in STOPPED
1997      * state with the Audio Unit still running, that's harmless because the
1998      * input callback will just throw away the sound data.
1999      */
2000     err = AudioOutputUnitStop(wwi->audioUnit);
2001     if (err != noErr)
2002         WARN("Failed to stop AU: %08lx\n", err);
2003
2004     TRACE("Recording stopped.\n");
2005
2006     OSSpinLockLock(&wwi->lock);
2007
2008     if (wwi->state == WINE_WS_CLOSED)
2009     {
2010         WARN("Trying to stop closed device.\n");
2011         ret = MMSYSERR_INVALHANDLE;
2012     }
2013     else if (wwi->state != WINE_WS_STOPPED)
2014     {
2015         wwi->state = WINE_WS_STOPPED;
2016         /* If there's a buffer in progress, it's done.  Remove it from the
2017          * queue so that we can return it to the app, below. */
2018         if (wwi->lpQueuePtr)
2019         {
2020             lpWaveHdr = wwi->lpQueuePtr;
2021             wwi->lpQueuePtr = lpWaveHdr->lpNext;
2022         }
2023     }
2024
2025     OSSpinLockUnlock(&wwi->lock);
2026
2027     if (lpWaveHdr)
2028     {
2029         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2030         lpWaveHdr->dwFlags |= WHDR_DONE;
2031         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2032     }
2033
2034     return ret;
2035 }
2036
2037
2038 /**************************************************************************
2039  *                      widReset                                [internal]
2040  */
2041 static DWORD widReset(WORD wDevID)
2042 {
2043     DWORD           ret = MMSYSERR_NOERROR;
2044     WINE_WAVEIN*    wwi;
2045     WAVEHDR*        lpWaveHdr = NULL;
2046
2047     TRACE("(%u);\n", wDevID);
2048     if (wDevID >= MAX_WAVEINDRV)
2049     {
2050         WARN("invalid device ID\n");
2051         return MMSYSERR_INVALHANDLE;
2052     }
2053
2054     wwi = &WInDev[wDevID];
2055     OSSpinLockLock(&wwi->lock);
2056
2057     if (wwi->state == WINE_WS_CLOSED)
2058     {
2059         WARN("Trying to reset a closed device.\n");
2060         ret = MMSYSERR_INVALHANDLE;
2061     }
2062     else
2063     {
2064         lpWaveHdr               = wwi->lpQueuePtr;
2065         wwi->lpQueuePtr         = NULL;
2066         wwi->state              = WINE_WS_STOPPED;
2067         wwi->dwTotalRecorded    = 0;
2068     }
2069
2070     OSSpinLockUnlock(&wwi->lock);
2071
2072     if (ret == MMSYSERR_NOERROR)
2073     {
2074         OSStatus err = AudioOutputUnitStop(wwi->audioUnit);
2075         if (err != noErr)
2076             WARN("Failed to stop AU: %08lx\n", err);
2077
2078         TRACE("Recording stopped.\n");
2079     }
2080
2081     while (lpWaveHdr)
2082     {
2083         WAVEHDR* lpNext = lpWaveHdr->lpNext;
2084
2085         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2086         lpWaveHdr->dwFlags |= WHDR_DONE;
2087         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2088
2089         lpWaveHdr = lpNext;
2090     }
2091
2092     return ret;
2093 }
2094
2095
2096 /**************************************************************************
2097  *                              widGetNumDevs                   [internal]
2098  */
2099 static DWORD widGetNumDevs(void)
2100 {
2101     return MAX_WAVEINDRV;
2102 }
2103
2104
2105 /**************************************************************************
2106  *                              widDevInterfaceSize             [internal]
2107  */
2108 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2109 {
2110     TRACE("(%u, %p)\n", wDevID, dwParam1);
2111
2112     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2113                                     NULL, 0 ) * sizeof(WCHAR);
2114     return MMSYSERR_NOERROR;
2115 }
2116
2117
2118 /**************************************************************************
2119  *                              widDevInterface                 [internal]
2120  */
2121 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2122 {
2123     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2124                                         NULL, 0 ) * sizeof(WCHAR))
2125     {
2126         MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2127                             dwParam1, dwParam2 / sizeof(WCHAR));
2128         return MMSYSERR_NOERROR;
2129     }
2130     return MMSYSERR_INVALPARAM;
2131 }
2132
2133
2134 /**************************************************************************
2135  *                              widMessage (WINECOREAUDIO.6)
2136  */
2137 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2138                             DWORD dwParam1, DWORD dwParam2)
2139 {
2140     TRACE("(%u, %04X, %08X, %08X, %08X);\n",
2141             wDevID, wMsg, dwUser, dwParam1, dwParam2);
2142
2143     switch (wMsg)
2144     {
2145         case DRVM_INIT:
2146         case DRVM_EXIT:
2147         case DRVM_ENABLE:
2148         case DRVM_DISABLE:
2149             /* FIXME: Pretend this is supported */
2150             return 0;
2151         case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,  dwParam2);
2152         case WIDM_CLOSE:            return widClose         (wDevID);
2153         case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1,       dwParam2);
2154         case WIDM_PREPARE:          return MMSYSERR_NOTSUPPORTED;
2155         case WIDM_UNPREPARE:        return MMSYSERR_NOTSUPPORTED;
2156         case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEINCAPSW)dwParam1,   dwParam2);
2157         case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
2158         case WIDM_RESET:            return widReset         (wDevID);
2159         case WIDM_START:            return widStart         (wDevID);
2160         case WIDM_STOP:             return widStop          (wDevID);
2161         case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
2162         case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
2163         default:
2164             FIXME("unknown message %d!\n", wMsg);
2165     }
2166
2167     return MMSYSERR_NOTSUPPORTED;
2168 }
2169
2170
2171 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
2172                                      AudioUnitRenderActionFlags *ioActionFlags,
2173                                      const AudioTimeStamp *inTimeStamp,
2174                                      UInt32 inBusNumber,
2175                                      UInt32 inNumberFrames,
2176                                      AudioBufferList *ioData)
2177 {
2178     WINE_WAVEIN*    wwi = (WINE_WAVEIN*)inRefCon;
2179     OSStatus        err = noErr;
2180     BOOL            needNotify = FALSE;
2181     WAVEHDR*        lpStorePtr;
2182     unsigned int    dataToStore;
2183     unsigned int    dataStored = 0;
2184
2185
2186     if (wwi->trace_on)
2187         fprintf(stderr, "trace:wave:CoreAudio_wiAudioUnitIOProc (ioActionFlags = %08lx, "
2188             "inTimeStamp = { %f, %x%08x, %f, %x%08x, %08lx }, inBusNumber = %lu, inNumberFrames = %lu)\n",
2189             *ioActionFlags, inTimeStamp->mSampleTime, (DWORD)(inTimeStamp->mHostTime >>32),
2190             (DWORD)inTimeStamp->mHostTime, inTimeStamp->mRateScalar, (DWORD)(inTimeStamp->mWordClockTime >> 32),
2191             (DWORD)inTimeStamp->mWordClockTime, inTimeStamp->mFlags, inBusNumber, inNumberFrames);
2192
2193     /* Render into audio buffer */
2194     /* FIXME: implement sample rate conversion on input.  This will require
2195      * a different render strategy.  We'll need to buffer the sound data
2196      * received here and pass it off to an AUConverter in another thread. */
2197     err = AudioUnitRender(wwi->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, wwi->bufferList);
2198     if (err)
2199     {
2200         if (wwi->err_on)
2201             fprintf(stderr, "err:wave:CoreAudio_wiAudioUnitIOProc AudioUnitRender failed with error %li\n", err);
2202         return err;
2203     }
2204
2205     /* Copy from audio buffer to the wavehdrs */
2206     dataToStore = wwi->bufferList->mBuffers[0].mDataByteSize;
2207
2208     OSSpinLockLock(&wwi->lock);
2209
2210     lpStorePtr = wwi->lpQueuePtr;
2211
2212     while (dataToStore > 0 && wwi->state == WINE_WS_PLAYING && lpStorePtr)
2213     {
2214         unsigned int room = lpStorePtr->dwBufferLength - lpStorePtr->dwBytesRecorded;
2215         unsigned int toCopy;
2216
2217         if (wwi->trace_on)
2218             fprintf(stderr, "trace:wave:CoreAudio_wiAudioUnitIOProc Looking to store %u bytes to wavehdr %p, which has room for %u\n",
2219                 dataToStore, lpStorePtr, room);
2220
2221         if (room >= dataToStore)
2222             toCopy = dataToStore;
2223         else
2224             toCopy = room;
2225
2226         if (toCopy > 0)
2227         {
2228             memcpy(lpStorePtr->lpData + lpStorePtr->dwBytesRecorded,
2229                 (char*)wwi->bufferList->mBuffers[0].mData + dataStored, toCopy);
2230             lpStorePtr->dwBytesRecorded += toCopy;
2231             wwi->dwTotalRecorded += toCopy;
2232             dataStored += toCopy;
2233             dataToStore -= toCopy;
2234             room -= toCopy;
2235         }
2236
2237         if (room == 0)
2238         {
2239             lpStorePtr = lpStorePtr->lpNext;
2240             needNotify = TRUE;
2241         }
2242     }
2243
2244     OSSpinLockUnlock(&wwi->lock);
2245
2246     if (needNotify) wodSendNotifyInputCompletionsMessage(wwi);
2247     return err;
2248 }
2249
2250 #else
2251
2252 /**************************************************************************
2253  *                              widMessage (WINECOREAUDIO.6)
2254  */
2255 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2256                             DWORD dwParam1, DWORD dwParam2)
2257 {
2258     FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2259     return MMSYSERR_NOTENABLED;
2260 }
2261
2262 /**************************************************************************
2263 *                               wodMessage (WINECOREAUDIO.7)
2264 */
2265 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
2266                                   DWORD dwParam1, DWORD dwParam2)
2267 {
2268     FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2269     return MMSYSERR_NOTENABLED;
2270 }
2271
2272 #endif