winecoreaudio: Implement widStop.
[wine/multimedia.git] / dlls / winmm / winecoreaudio / audio.c
blobb8e060e83e241c4c94fd7b912d77932d1568445c
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>
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"
49 WINE_DEFAULT_DEBUG_CHANNEL(wave);
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>
58 Due to AudioUnit headers conflict define some needed types.
61 typedef void *AudioUnit;
63 /* From AudioUnit/AUComponents.h */
64 enum
66 kAudioUnitRenderAction_OutputIsSilence = (1 << 4),
67 /* provides hint on return from Render(): if set the buffer contains all zeroes */
69 typedef UInt32 AudioUnitRenderActionFlags;
71 /* only allow 10 output devices through this driver, this ought to be adequate */
72 #define MAX_WAVEOUTDRV (1)
73 #define MAX_WAVEINDRV (1)
75 /* state diagram for waveOut writing:
77 * +---------+-------------+---------------+---------------------------------+
78 * | state | function | event | new state |
79 * +---------+-------------+---------------+---------------------------------+
80 * | | open() | | STOPPED |
81 * | PAUSED | write() | | PAUSED |
82 * | STOPPED | write() | <thrd create> | PLAYING |
83 * | PLAYING | write() | HEADER | PLAYING |
84 * | (other) | write() | <error> | |
85 * | (any) | pause() | PAUSING | PAUSED |
86 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
87 * | (any) | reset() | RESETTING | STOPPED |
88 * | (any) | close() | CLOSING | CLOSED |
89 * +---------+-------------+---------------+---------------------------------+
92 /* states of the playing device */
93 #define WINE_WS_PLAYING 0
94 #define WINE_WS_PAUSED 1
95 #define WINE_WS_STOPPED 2
96 #define WINE_WS_CLOSED 3
98 typedef struct tagCoreAudio_Device {
99 char dev_name[32];
100 char mixer_name[32];
101 unsigned open_count;
102 char* interface_name;
104 WAVEOUTCAPSW out_caps;
105 WAVEINCAPSW in_caps;
106 DWORD in_caps_support;
107 int sample_rate;
108 int stereo;
109 int format;
110 unsigned audio_fragment;
111 BOOL full_duplex;
112 BOOL bTriggerSupport;
113 BOOL bOutputEnabled;
114 BOOL bInputEnabled;
115 DSDRIVERDESC ds_desc;
116 DSDRIVERCAPS ds_caps;
117 DSCDRIVERCAPS dsc_caps;
118 GUID ds_guid;
119 GUID dsc_guid;
121 AudioDeviceID outputDeviceID;
122 AudioDeviceID inputDeviceID;
123 AudioStreamBasicDescription streamDescription;
124 } CoreAudio_Device;
126 /* for now use the default device */
127 static CoreAudio_Device CoreAudio_DefaultDevice;
129 typedef struct {
130 volatile int state; /* one of the WINE_WS_ manifest constants */
131 CoreAudio_Device *cadev;
132 WAVEOPENDESC waveDesc;
133 WORD wFlags;
134 PCMWAVEFORMAT format;
135 DWORD woID;
136 AudioUnit audioUnit;
137 AudioStreamBasicDescription streamDescription;
139 WAVEOUTCAPSW caps;
140 char interface_name[32];
141 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
142 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
143 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
145 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
146 DWORD dwLoops; /* private copy of loop counter */
148 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
149 DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */
151 DWORD tickCountMS; /* time in MS of last AudioUnit callback */
153 OSSpinLock lock; /* synchronization stuff */
155 BOOL trace_on;
156 BOOL warn_on;
157 BOOL err_on;
158 } WINE_WAVEOUT;
160 typedef struct {
161 /* This device's device number */
162 DWORD wiID;
164 /* Access to the following fields is synchronized across threads. */
165 volatile int state;
166 LPWAVEHDR lpQueuePtr;
167 DWORD dwTotalRecorded;
169 /* Synchronization mechanism to protect above fields */
170 OSSpinLock lock;
172 /* Capabilities description */
173 WAVEINCAPSW caps;
174 char interface_name[32];
176 /* Record the arguments used when opening the device. */
177 WAVEOPENDESC waveDesc;
178 WORD wFlags;
179 PCMWAVEFORMAT format;
181 AudioUnit audioUnit;
182 AudioBufferList*bufferList;
184 /* Record state of debug channels at open. Used to control fprintf's since
185 * we can't use Wine debug channel calls in non-Wine AudioUnit threads. */
186 BOOL trace_on;
187 BOOL warn_on;
188 BOOL err_on;
190 /* These fields aren't used. */
191 #if 0
192 CoreAudio_Device *cadev;
194 AudioStreamBasicDescription streamDescription;
195 #endif
196 } WINE_WAVEIN;
198 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
199 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
201 static CFMessagePortRef Port_SendToMessageThread;
203 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
204 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
205 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi);
207 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
208 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
209 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
211 extern OSStatus AudioOutputUnitStart(AudioUnit au);
212 extern OSStatus AudioOutputUnitStop(AudioUnit au);
213 extern OSStatus AudioUnitUninitialize(AudioUnit au);
215 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
216 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
218 extern int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
219 WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
220 UInt32* outFrameCount);
222 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
223 AudioUnitRenderActionFlags *ioActionFlags,
224 const AudioTimeStamp *inTimeStamp,
225 UInt32 inBusNumber,
226 UInt32 inNumberFrames,
227 AudioBufferList *ioData);
228 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
229 AudioUnitRenderActionFlags *ioActionFlags,
230 const AudioTimeStamp *inTimeStamp,
231 UInt32 inBusNumber,
232 UInt32 inNumberFrames,
233 AudioBufferList *ioData);
235 /* These strings used only for tracing */
237 static const char * getMessage(UINT msg)
239 static char unknown[32];
240 #define MSG_TO_STR(x) case x: return #x
241 switch(msg) {
242 MSG_TO_STR(DRVM_INIT);
243 MSG_TO_STR(DRVM_EXIT);
244 MSG_TO_STR(DRVM_ENABLE);
245 MSG_TO_STR(DRVM_DISABLE);
246 MSG_TO_STR(WIDM_OPEN);
247 MSG_TO_STR(WIDM_CLOSE);
248 MSG_TO_STR(WIDM_ADDBUFFER);
249 MSG_TO_STR(WIDM_PREPARE);
250 MSG_TO_STR(WIDM_UNPREPARE);
251 MSG_TO_STR(WIDM_GETDEVCAPS);
252 MSG_TO_STR(WIDM_GETNUMDEVS);
253 MSG_TO_STR(WIDM_GETPOS);
254 MSG_TO_STR(WIDM_RESET);
255 MSG_TO_STR(WIDM_START);
256 MSG_TO_STR(WIDM_STOP);
257 MSG_TO_STR(WODM_OPEN);
258 MSG_TO_STR(WODM_CLOSE);
259 MSG_TO_STR(WODM_WRITE);
260 MSG_TO_STR(WODM_PAUSE);
261 MSG_TO_STR(WODM_GETPOS);
262 MSG_TO_STR(WODM_BREAKLOOP);
263 MSG_TO_STR(WODM_PREPARE);
264 MSG_TO_STR(WODM_UNPREPARE);
265 MSG_TO_STR(WODM_GETDEVCAPS);
266 MSG_TO_STR(WODM_GETNUMDEVS);
267 MSG_TO_STR(WODM_GETPITCH);
268 MSG_TO_STR(WODM_SETPITCH);
269 MSG_TO_STR(WODM_GETPLAYBACKRATE);
270 MSG_TO_STR(WODM_SETPLAYBACKRATE);
271 MSG_TO_STR(WODM_GETVOLUME);
272 MSG_TO_STR(WODM_SETVOLUME);
273 MSG_TO_STR(WODM_RESTART);
274 MSG_TO_STR(WODM_RESET);
275 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
276 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
277 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
278 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
280 #undef MSG_TO_STR
281 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
282 return unknown;
285 #define kStopLoopMessage 0
286 #define kWaveOutNotifyCompletionsMessage 1
287 #define kWaveInNotifyCompletionsMessage 2
289 /* Mach Message Handling */
290 static CFDataRef wodMessageHandler(CFMessagePortRef port_ReceiveInMessageThread, SInt32 msgid, CFDataRef data, void *info)
292 UInt32 *buffer = NULL;
294 switch (msgid)
296 case kWaveOutNotifyCompletionsMessage:
297 buffer = (UInt32 *) CFDataGetBytePtr(data);
298 wodHelper_NotifyCompletions(&WOutDev[buffer[0]], FALSE);
299 break;
300 case kWaveInNotifyCompletionsMessage:
301 buffer = (UInt32 *) CFDataGetBytePtr(data);
302 widHelper_NotifyCompletions(&WInDev[buffer[0]]);
303 break;
304 default:
305 CFRunLoopStop(CFRunLoopGetCurrent());
306 break;
309 return NULL;
312 static DWORD WINAPI messageThread(LPVOID p)
314 CFMessagePortRef port_ReceiveInMessageThread = (CFMessagePortRef) p;
315 CFRunLoopSourceRef source;
317 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port_ReceiveInMessageThread, (CFIndex)0);
318 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
320 CFRunLoopRun();
322 CFRunLoopSourceInvalidate(source);
323 CFRelease(source);
324 CFRelease(port_ReceiveInMessageThread);
326 return 0;
329 /**************************************************************************
330 * wodSendNotifyCompletionsMessage [internal]
331 * Call from AudioUnit IO thread can't use Wine debug channels.
333 static void wodSendNotifyCompletionsMessage(WINE_WAVEOUT* wwo)
335 CFDataRef data;
336 UInt32 buffer;
338 buffer = (UInt32) wwo->woID;
340 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
341 if (!data)
342 return;
344 CFMessagePortSendRequest(Port_SendToMessageThread, kWaveOutNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
345 CFRelease(data);
348 /**************************************************************************
349 * wodSendNotifyInputCompletionsMessage [internal]
350 * Call from AudioUnit IO thread can't use Wine debug channels.
352 static void wodSendNotifyInputCompletionsMessage(WINE_WAVEIN* wwi)
354 CFDataRef data;
355 UInt32 buffer;
357 buffer = (UInt32) wwi->wiID;
359 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
360 if (!data)
361 return;
363 CFMessagePortSendRequest(Port_SendToMessageThread, kWaveInNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
364 CFRelease(data);
367 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
368 PCMWAVEFORMAT* format)
370 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
371 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
372 format->wf.nChannels, format->wf.nAvgBytesPerSec);
373 TRACE("Position in bytes=%u\n", position);
375 switch (lpTime->wType) {
376 case TIME_SAMPLES:
377 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
378 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
379 break;
380 case TIME_MS:
381 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
382 TRACE("TIME_MS=%u\n", lpTime->u.ms);
383 break;
384 case TIME_SMPTE:
385 lpTime->u.smpte.fps = 30;
386 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
387 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
388 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
389 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
390 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
391 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
392 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
393 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
394 lpTime->u.smpte.fps = 30;
395 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
396 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
397 lpTime->u.smpte.hour, lpTime->u.smpte.min,
398 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
399 break;
400 default:
401 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
402 lpTime->wType = TIME_BYTES;
403 /* fall through */
404 case TIME_BYTES:
405 lpTime->u.cb = position;
406 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
407 break;
409 return MMSYSERR_NOERROR;
412 /**************************************************************************
413 * CoreAudio_GetDevCaps [internal]
415 BOOL CoreAudio_GetDevCaps (void)
417 OSStatus status;
418 UInt32 propertySize;
419 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
421 char name[MAXPNAMELEN];
423 propertySize = MAXPNAMELEN;
424 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
425 if (status) {
426 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
427 (char) (status >> 16),
428 (char) (status >> 8),
429 (char) status);
430 return FALSE;
433 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
434 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
435 MultiByteToWideChar(CP_ACP, 0, name, sizeof(name),
436 CoreAudio_DefaultDevice.out_caps.szPname,
437 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
438 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
440 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
441 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
442 if (status != noErr) {
443 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
444 (char) (status >> 16),
445 (char) (status >> 8),
446 (char) status);
447 return FALSE;
450 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
451 "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
452 "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
453 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
454 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
455 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
456 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
457 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
458 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
459 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
460 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
461 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
462 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
463 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
465 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
466 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
468 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
469 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
470 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
471 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
472 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
474 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
475 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
477 return TRUE;
480 /******************************************************************
481 * CoreAudio_WaveInit
483 * Initialize CoreAudio_DefaultDevice
485 LONG CoreAudio_WaveInit(void)
487 OSStatus status;
488 UInt32 propertySize;
489 CHAR szPname[MAXPNAMELEN];
490 int i;
491 HANDLE hThread;
492 CFStringRef messageThreadPortName;
493 CFMessagePortRef port_ReceiveInMessageThread;
495 TRACE("()\n");
497 /* number of sound cards */
498 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
499 propertySize /= sizeof(AudioDeviceID);
500 TRACE("sound cards : %lu\n", propertySize);
502 /* Get the output device */
503 propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
504 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
505 if (status) {
506 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
507 (char) (status >> 16),
508 (char) (status >> 8),
509 (char) status);
510 return 1;
512 if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
513 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
514 return 1;
517 if ( ! CoreAudio_GetDevCaps() )
518 return 1;
520 CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
521 sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name);
523 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
525 WOutDev[i].state = WINE_WS_CLOSED;
526 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
527 WOutDev[i].woID = i;
529 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
531 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
532 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
533 snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
534 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
535 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
537 WOutDev[i].caps.vDriverVersion = 0x0001;
538 WOutDev[i].caps.dwFormats = 0x00000000;
539 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
541 WOutDev[i].caps.wChannels = 2;
542 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
544 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
545 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
546 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
547 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
548 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
549 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
550 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
551 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
552 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
553 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
554 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
555 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
557 WOutDev[i].lock = 0; /* initialize the mutex */
560 for (i = 0; i < MAX_WAVEINDRV; ++i)
562 memset(&WInDev[i], 0, sizeof(WInDev[i]));
563 WInDev[i].wiID = i;
565 /* Establish preconditions for widOpen */
566 WInDev[i].state = WINE_WS_CLOSED;
567 WInDev[i].lock = 0; /* initialize the mutex */
569 /* Fill in capabilities. widGetDevCaps can be called at any time. */
570 WInDev[i].caps.wMid = 0xcafe; /* Manufac ID */
571 WInDev[i].caps.wPid = 0x0001; /* Product ID */
572 WInDev[i].caps.vDriverVersion = 0x0001;
574 snprintf(szPname, sizeof(szPname), "CoreAudio WaveIn %d", i);
575 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WInDev[i].caps.szPname, sizeof(WInDev[i].caps.szPname)/sizeof(WCHAR));
576 snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "winecoreaudio in: %d", i);
578 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
579 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
580 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
581 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
582 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
583 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
584 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
585 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
586 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
587 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
588 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
589 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
591 WInDev[i].caps.wChannels = 2;
594 /* create mach messages handler */
595 srandomdev();
596 messageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
597 CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
598 if (!messageThreadPortName)
600 ERR("Can't create message thread port name\n");
601 return 1;
604 port_ReceiveInMessageThread = CFMessagePortCreateLocal(kCFAllocatorDefault, messageThreadPortName,
605 &wodMessageHandler, NULL, NULL);
606 if (!port_ReceiveInMessageThread)
608 ERR("Can't create message thread local port\n");
609 CFRelease(messageThreadPortName);
610 return 1;
613 Port_SendToMessageThread = CFMessagePortCreateRemote(kCFAllocatorDefault, messageThreadPortName);
614 CFRelease(messageThreadPortName);
615 if (!Port_SendToMessageThread)
617 ERR("Can't create port for sending to message thread\n");
618 CFRelease(port_ReceiveInMessageThread);
619 return 1;
622 hThread = CreateThread(NULL, 0, messageThread, (LPVOID)port_ReceiveInMessageThread, 0, NULL);
623 if ( !hThread )
625 ERR("Can't create message thread\n");
626 CFRelease(port_ReceiveInMessageThread);
627 CFRelease(Port_SendToMessageThread);
628 Port_SendToMessageThread = NULL;
629 return 1;
632 /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
634 return 0;
637 void CoreAudio_WaveRelease(void)
639 /* Stop CFRunLoop in messageThread */
640 TRACE("()\n");
642 CFMessagePortSendRequest(Port_SendToMessageThread, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
643 CFRelease(Port_SendToMessageThread);
644 Port_SendToMessageThread = NULL;
647 /*======================================================================*
648 * Low level WAVE OUT implementation *
649 *======================================================================*/
651 /**************************************************************************
652 * wodNotifyClient [internal]
654 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
656 switch (wMsg) {
657 case WOM_OPEN:
658 case WOM_CLOSE:
659 case WOM_DONE:
660 if (wwo->wFlags != DCB_NULL &&
661 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
662 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
663 dwParam1, dwParam2))
665 return MMSYSERR_ERROR;
667 break;
668 default:
669 return MMSYSERR_INVALPARAM;
671 return MMSYSERR_NOERROR;
675 /**************************************************************************
676 * wodGetDevCaps [internal]
678 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
680 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
682 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
684 if (wDevID >= MAX_WAVEOUTDRV)
686 TRACE("MAX_WAVOUTDRV reached !\n");
687 return MMSYSERR_BADDEVICEID;
690 TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
691 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
692 return MMSYSERR_NOERROR;
695 /**************************************************************************
696 * wodOpen [internal]
698 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
700 WINE_WAVEOUT* wwo;
701 DWORD retval;
702 DWORD ret;
703 AudioStreamBasicDescription streamFormat;
705 TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
706 if (lpDesc == NULL)
708 WARN("Invalid Parameter !\n");
709 return MMSYSERR_INVALPARAM;
711 if (wDevID >= MAX_WAVEOUTDRV) {
712 TRACE("MAX_WAVOUTDRV reached !\n");
713 return MMSYSERR_BADDEVICEID;
716 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
717 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
718 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
720 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
721 lpDesc->lpFormat->nChannels == 0 ||
722 lpDesc->lpFormat->nSamplesPerSec == 0
725 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
726 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
727 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
728 return WAVERR_BADFORMAT;
731 if (dwFlags & WAVE_FORMAT_QUERY)
733 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
734 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
735 lpDesc->lpFormat->nSamplesPerSec);
736 return MMSYSERR_NOERROR;
739 wwo = &WOutDev[wDevID];
740 if (!OSSpinLockTry(&wwo->lock))
741 return MMSYSERR_ALLOCATED;
743 if (wwo->state != WINE_WS_CLOSED)
745 OSSpinLockUnlock(&wwo->lock);
746 return MMSYSERR_ALLOCATED;
749 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
751 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
752 OSSpinLockUnlock(&wwo->lock);
753 return MMSYSERR_ERROR;
756 if ((dwFlags & WAVE_DIRECTSOUND) &&
757 !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
758 /* not supported, ignore it */
759 dwFlags &= ~WAVE_DIRECTSOUND;
761 streamFormat.mFormatID = kAudioFormatLinearPCM;
762 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
763 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
764 if (lpDesc->lpFormat->wBitsPerSample != 8)
765 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
766 # ifdef WORDS_BIGENDIAN
767 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
768 # endif
770 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
771 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
772 streamFormat.mFramesPerPacket = 1;
773 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
774 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
775 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
777 ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, &streamFormat);
778 if (!ret)
780 AudioUnit_CloseAudioUnit(wwo->audioUnit);
781 OSSpinLockUnlock(&wwo->lock);
782 return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
784 wwo->streamDescription = streamFormat;
786 ret = AudioOutputUnitStart(wwo->audioUnit);
787 if (ret)
789 ERR("AudioOutputUnitStart failed: %08x\n", ret);
790 AudioUnitUninitialize(wwo->audioUnit);
791 AudioUnit_CloseAudioUnit(wwo->audioUnit);
792 OSSpinLockUnlock(&wwo->lock);
793 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
796 wwo->state = WINE_WS_STOPPED;
798 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
800 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
801 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
803 if (wwo->format.wBitsPerSample == 0) {
804 WARN("Resetting zeroed wBitsPerSample\n");
805 wwo->format.wBitsPerSample = 8 *
806 (wwo->format.wf.nAvgBytesPerSec /
807 wwo->format.wf.nSamplesPerSec) /
808 wwo->format.wf.nChannels;
811 wwo->dwPlayedTotal = 0;
812 wwo->dwWrittenTotal = 0;
814 wwo->trace_on = TRACE_ON(wave);
815 wwo->warn_on = WARN_ON(wave);
816 wwo->err_on = ERR_ON(wave);
818 OSSpinLockUnlock(&wwo->lock);
820 retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
822 return retval;
825 /**************************************************************************
826 * wodClose [internal]
828 static DWORD wodClose(WORD wDevID)
830 DWORD ret = MMSYSERR_NOERROR;
831 WINE_WAVEOUT* wwo;
833 TRACE("(%u);\n", wDevID);
835 if (wDevID >= MAX_WAVEOUTDRV)
837 WARN("bad device ID !\n");
838 return MMSYSERR_BADDEVICEID;
841 wwo = &WOutDev[wDevID];
842 OSSpinLockLock(&wwo->lock);
843 if (wwo->lpQueuePtr)
845 WARN("buffers still playing !\n");
846 OSSpinLockUnlock(&wwo->lock);
847 ret = WAVERR_STILLPLAYING;
848 } else
850 OSStatus err;
851 /* sanity check: this should not happen since the device must have been reset before */
852 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
854 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
856 OSSpinLockUnlock(&wwo->lock);
858 err = AudioUnitUninitialize(wwo->audioUnit);
859 if (err) {
860 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
861 (char) (err >> 16),
862 (char) (err >> 8),
863 (char) err);
864 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
867 if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
869 ERR("Can't close AudioUnit\n");
870 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
873 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
876 return ret;
879 /**************************************************************************
880 * wodPrepare [internal]
882 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
884 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
886 if (wDevID >= MAX_WAVEOUTDRV) {
887 WARN("bad device ID !\n");
888 return MMSYSERR_BADDEVICEID;
891 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
892 return WAVERR_STILLPLAYING;
894 lpWaveHdr->dwFlags |= WHDR_PREPARED;
895 lpWaveHdr->dwFlags &= ~WHDR_DONE;
897 return MMSYSERR_NOERROR;
900 /**************************************************************************
901 * wodUnprepare [internal]
903 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
905 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
907 if (wDevID >= MAX_WAVEOUTDRV) {
908 WARN("bad device ID !\n");
909 return MMSYSERR_BADDEVICEID;
912 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
913 return WAVERR_STILLPLAYING;
915 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
916 lpWaveHdr->dwFlags |= WHDR_DONE;
918 return MMSYSERR_NOERROR;
922 /**************************************************************************
923 * wodHelper_CheckForLoopBegin [internal]
925 * Check if the new waveheader is the beginning of a loop, and set up
926 * state if so.
927 * This is called with the WAVEOUT lock held.
928 * Call from AudioUnit IO thread can't use Wine debug channels.
930 static void wodHelper_CheckForLoopBegin(WINE_WAVEOUT* wwo)
932 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
934 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
936 if (wwo->lpLoopPtr)
938 if (wwo->warn_on)
939 fprintf(stderr, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
941 else
943 if (wwo->trace_on)
944 fprintf(stderr, "trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
946 wwo->lpLoopPtr = lpWaveHdr;
947 /* Windows does not touch WAVEHDR.dwLoops,
948 * so we need to make an internal copy */
949 wwo->dwLoops = lpWaveHdr->dwLoops;
955 /**************************************************************************
956 * wodHelper_PlayPtrNext [internal]
958 * Advance the play pointer to the next waveheader, looping if required.
959 * This is called with the WAVEOUT lock held.
960 * Call from AudioUnit IO thread can't use Wine debug channels.
962 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
964 BOOL didLoopBack = FALSE;
966 wwo->dwPartialOffset = 0;
967 if ((wwo->lpPlayPtr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
969 /* We're at the end of a loop, loop if required */
970 if (wwo->dwLoops > 1)
972 wwo->dwLoops--;
973 wwo->lpPlayPtr = wwo->lpLoopPtr;
974 didLoopBack = TRUE;
976 else
978 wwo->lpLoopPtr = NULL;
981 if (!didLoopBack)
983 /* We didn't loop back. Advance to the next wave header */
984 wwo->lpPlayPtr = wwo->lpPlayPtr->lpNext;
986 if (!wwo->lpPlayPtr)
987 wwo->state = WINE_WS_STOPPED;
988 else
989 wodHelper_CheckForLoopBegin(wwo);
993 /* if force is TRUE then notify the client that all the headers were completed
995 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
997 LPWAVEHDR lpWaveHdr;
998 LPWAVEHDR lpFirstDoneWaveHdr = NULL;
1000 OSSpinLockLock(&wwo->lock);
1002 /* First, excise all of the done headers from the queue into
1003 * a free-standing list. */
1004 if (force)
1006 lpFirstDoneWaveHdr = wwo->lpQueuePtr;
1007 wwo->lpQueuePtr = NULL;
1009 else
1011 LPWAVEHDR lpLastDoneWaveHdr = NULL;
1013 /* Start from lpQueuePtr and keep notifying until:
1014 * - we hit an unwritten wavehdr
1015 * - we hit the beginning of a running loop
1016 * - we hit a wavehdr which hasn't finished playing
1018 for (
1019 lpWaveHdr = wwo->lpQueuePtr;
1020 lpWaveHdr &&
1021 lpWaveHdr != wwo->lpPlayPtr &&
1022 lpWaveHdr != wwo->lpLoopPtr;
1023 lpWaveHdr = lpWaveHdr->lpNext
1026 if (!lpFirstDoneWaveHdr)
1027 lpFirstDoneWaveHdr = lpWaveHdr;
1028 lpLastDoneWaveHdr = lpWaveHdr;
1031 if (lpLastDoneWaveHdr)
1033 wwo->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1034 lpLastDoneWaveHdr->lpNext = NULL;
1038 OSSpinLockUnlock(&wwo->lock);
1040 /* Now, send the "done" notification for each header in our list. */
1041 for (lpWaveHdr = lpFirstDoneWaveHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
1043 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1044 lpWaveHdr->dwFlags |= WHDR_DONE;
1046 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1051 /**************************************************************************
1052 * wodWrite [internal]
1055 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1057 LPWAVEHDR*wh;
1058 WINE_WAVEOUT *wwo;
1060 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1062 /* first, do the sanity checks... */
1063 if (wDevID >= MAX_WAVEOUTDRV)
1065 WARN("bad dev ID !\n");
1066 return MMSYSERR_BADDEVICEID;
1069 wwo = &WOutDev[wDevID];
1071 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1073 TRACE("unprepared\n");
1074 return WAVERR_UNPREPARED;
1077 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1079 TRACE("still playing\n");
1080 return WAVERR_STILLPLAYING;
1083 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1084 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1085 lpWaveHdr->lpNext = 0;
1087 OSSpinLockLock(&wwo->lock);
1088 /* insert buffer at the end of queue */
1089 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1090 /* Do nothing */;
1091 *wh = lpWaveHdr;
1093 if (!wwo->lpPlayPtr)
1095 wwo->lpPlayPtr = lpWaveHdr;
1097 if (wwo->state == WINE_WS_STOPPED)
1098 wwo->state = WINE_WS_PLAYING;
1100 wodHelper_CheckForLoopBegin(wwo);
1102 wwo->dwPartialOffset = 0;
1104 OSSpinLockUnlock(&wwo->lock);
1106 return MMSYSERR_NOERROR;
1109 /**************************************************************************
1110 * wodPause [internal]
1112 static DWORD wodPause(WORD wDevID)
1114 OSStatus status;
1116 TRACE("(%u);!\n", wDevID);
1118 if (wDevID >= MAX_WAVEOUTDRV)
1120 WARN("bad device ID !\n");
1121 return MMSYSERR_BADDEVICEID;
1124 /* The order of the following operations is important since we can't hold
1125 * the mutex while we make an Audio Unit call. Stop the Audio Unit before
1126 * setting the PAUSED state. In wodRestart, the order is reversed. This
1127 * guarantees that we can't get into a situation where the state is
1128 * PLAYING or STOPPED but the Audio Unit isn't running. Although we can
1129 * be in PAUSED state with the Audio Unit still running, that's harmless
1130 * because the render callback will just produce silence.
1132 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1133 if (status) {
1134 WARN("AudioOutputUnitStop return %c%c%c%c\n",
1135 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1138 OSSpinLockLock(&WOutDev[wDevID].lock);
1139 if (WOutDev[wDevID].state == WINE_WS_PLAYING || WOutDev[wDevID].state == WINE_WS_STOPPED)
1140 WOutDev[wDevID].state = WINE_WS_PAUSED;
1141 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1143 return MMSYSERR_NOERROR;
1146 /**************************************************************************
1147 * wodRestart [internal]
1149 static DWORD wodRestart(WORD wDevID)
1151 OSStatus status;
1153 TRACE("(%u);\n", wDevID);
1155 if (wDevID >= MAX_WAVEOUTDRV )
1157 WARN("bad device ID !\n");
1158 return MMSYSERR_BADDEVICEID;
1161 /* The order of the following operations is important since we can't hold
1162 * the mutex while we make an Audio Unit call. Set the PLAYING/STOPPED
1163 * state before starting the Audio Unit. In wodPause, the order is
1164 * reversed. This guarantees that we can't get into a situation where
1165 * the state is PLAYING or STOPPED but the Audio Unit isn't running.
1166 * Although we can be in PAUSED state with the Audio Unit still running,
1167 * that's harmless because the render callback will just produce silence.
1169 OSSpinLockLock(&WOutDev[wDevID].lock);
1170 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1172 if (WOutDev[wDevID].lpPlayPtr)
1173 WOutDev[wDevID].state = WINE_WS_PLAYING;
1174 else
1175 WOutDev[wDevID].state = WINE_WS_STOPPED;
1177 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1179 status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1180 if (status) {
1181 ERR("AudioOutputUnitStart return %c%c%c%c\n",
1182 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1183 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1186 return MMSYSERR_NOERROR;
1189 /**************************************************************************
1190 * wodReset [internal]
1192 static DWORD wodReset(WORD wDevID)
1194 WINE_WAVEOUT* wwo;
1195 OSStatus status;
1197 TRACE("(%u);\n", wDevID);
1199 if (wDevID >= MAX_WAVEOUTDRV)
1201 WARN("bad device ID !\n");
1202 return MMSYSERR_BADDEVICEID;
1205 wwo = &WOutDev[wDevID];
1207 FIXME("\n");
1209 /* updates current notify list */
1210 /* if resetting, remove all wave headers and notify client that all headers were completed */
1211 wodHelper_NotifyCompletions(wwo, TRUE);
1213 OSSpinLockLock(&wwo->lock);
1215 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1216 wwo->state = WINE_WS_STOPPED;
1217 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1219 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
1221 OSSpinLockUnlock(&wwo->lock);
1223 status = AudioOutputUnitStart(wwo->audioUnit);
1225 if (status) {
1226 ERR( "AudioOutputUnitStart return %c%c%c%c\n",
1227 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1228 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1231 return MMSYSERR_NOERROR;
1234 /**************************************************************************
1235 * wodGetPosition [internal]
1237 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1239 DWORD val;
1240 WINE_WAVEOUT* wwo;
1242 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1244 if (wDevID >= MAX_WAVEOUTDRV)
1246 WARN("bad device ID !\n");
1247 return MMSYSERR_BADDEVICEID;
1250 /* if null pointer to time structure return error */
1251 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1253 wwo = &WOutDev[wDevID];
1255 OSSpinLockLock(&WOutDev[wDevID].lock);
1256 val = wwo->dwPlayedTotal;
1257 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1259 return bytes_to_mmtime(lpTime, val, &wwo->format);
1262 /**************************************************************************
1263 * wodGetVolume [internal]
1265 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1267 float left;
1268 float right;
1270 if (wDevID >= MAX_WAVEOUTDRV)
1272 WARN("bad device ID !\n");
1273 return MMSYSERR_BADDEVICEID;
1276 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1278 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1280 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1282 return MMSYSERR_NOERROR;
1285 /**************************************************************************
1286 * wodSetVolume [internal]
1288 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1290 float left;
1291 float right;
1293 if (wDevID >= MAX_WAVEOUTDRV)
1295 WARN("bad device ID !\n");
1296 return MMSYSERR_BADDEVICEID;
1299 left = LOWORD(dwParam) / 65535.0f;
1300 right = HIWORD(dwParam) / 65535.0f;
1302 TRACE("(%u, %08x);\n", wDevID, dwParam);
1304 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1306 return MMSYSERR_NOERROR;
1309 /**************************************************************************
1310 * wodGetNumDevs [internal]
1312 static DWORD wodGetNumDevs(void)
1314 TRACE("\n");
1315 return MAX_WAVEOUTDRV;
1318 /**************************************************************************
1319 * wodDevInterfaceSize [internal]
1321 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1323 TRACE("(%u, %p)\n", wDevID, dwParam1);
1325 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1326 NULL, 0 ) * sizeof(WCHAR);
1327 return MMSYSERR_NOERROR;
1330 /**************************************************************************
1331 * wodDevInterface [internal]
1333 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1335 TRACE("\n");
1336 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1337 NULL, 0 ) * sizeof(WCHAR))
1339 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1340 dwParam1, dwParam2 / sizeof(WCHAR));
1341 return MMSYSERR_NOERROR;
1343 return MMSYSERR_INVALPARAM;
1346 /**************************************************************************
1347 * wodMessage (WINECOREAUDIO.7)
1349 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1350 DWORD dwParam1, DWORD dwParam2)
1352 TRACE("(%u, %s, %08x, %08x, %08x);\n",
1353 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1355 switch (wMsg) {
1356 case DRVM_INIT:
1357 case DRVM_EXIT:
1358 case DRVM_ENABLE:
1359 case DRVM_DISABLE:
1361 /* FIXME: Pretend this is supported */
1362 return 0;
1363 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1364 case WODM_CLOSE: return wodClose(wDevID);
1365 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1366 case WODM_PAUSE: return wodPause(wDevID);
1367 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1368 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1369 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1370 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1372 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1373 case WODM_GETNUMDEVS: return wodGetNumDevs();
1375 case WODM_GETPITCH:
1376 case WODM_SETPITCH:
1377 case WODM_GETPLAYBACKRATE:
1378 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1379 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1380 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1381 case WODM_RESTART: return wodRestart(wDevID);
1382 case WODM_RESET: return wodReset(wDevID);
1384 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1385 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1386 case DRV_QUERYDSOUNDIFACE:
1387 case DRV_QUERYDSOUNDDESC:
1388 return MMSYSERR_NOTSUPPORTED;
1390 default:
1391 FIXME("unknown message %d!\n", wMsg);
1394 return MMSYSERR_NOTSUPPORTED;
1397 /*======================================================================*
1398 * Low level DSOUND implementation *
1399 *======================================================================*/
1401 typedef struct IDsDriverImpl IDsDriverImpl;
1402 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1404 struct IDsDriverImpl
1406 /* IUnknown fields */
1407 const IDsDriverVtbl *lpVtbl;
1408 DWORD ref;
1409 /* IDsDriverImpl fields */
1410 UINT wDevID;
1411 IDsDriverBufferImpl*primary;
1414 struct IDsDriverBufferImpl
1416 /* IUnknown fields */
1417 const IDsDriverBufferVtbl *lpVtbl;
1418 DWORD ref;
1419 /* IDsDriverBufferImpl fields */
1420 IDsDriverImpl* drv;
1421 DWORD buflen;
1426 CoreAudio IO threaded callback,
1427 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1429 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1430 AudioUnitRenderActionFlags *ioActionFlags,
1431 const AudioTimeStamp *inTimeStamp,
1432 UInt32 inBusNumber,
1433 UInt32 inNumberFrames,
1434 AudioBufferList *ioData)
1436 UInt32 buffer;
1437 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1438 int needNotify = 0;
1440 unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1441 unsigned int dataProvided = 0;
1443 OSSpinLockLock(&wwo->lock);
1445 while (dataNeeded > 0 && wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1447 unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1448 unsigned int toCopy;
1450 if (available >= dataNeeded)
1451 toCopy = dataNeeded;
1452 else
1453 toCopy = available;
1455 if (toCopy > 0)
1457 memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1458 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1459 wwo->dwPartialOffset += toCopy;
1460 wwo->dwPlayedTotal += toCopy;
1461 dataProvided += toCopy;
1462 dataNeeded -= toCopy;
1463 available -= toCopy;
1466 if (available == 0)
1468 wodHelper_PlayPtrNext(wwo);
1469 needNotify = 1;
1473 OSSpinLockUnlock(&wwo->lock);
1475 /* We can't provide any more wave data. Fill the rest with silence. */
1476 if (dataNeeded > 0)
1478 if (!dataProvided)
1479 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
1480 memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1481 dataProvided += dataNeeded;
1482 dataNeeded = 0;
1485 /* We only fill buffer 0. Set any others that might be requested to 0. */
1486 for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1488 memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1491 if (needNotify) wodSendNotifyCompletionsMessage(wwo);
1492 return noErr;
1496 /*======================================================================*
1497 * Low level WAVE IN implementation *
1498 *======================================================================*/
1500 /**************************************************************************
1501 * widNotifyClient [internal]
1503 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1505 TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
1507 switch (wMsg)
1509 case WIM_OPEN:
1510 case WIM_CLOSE:
1511 case WIM_DATA:
1512 if (wwi->wFlags != DCB_NULL &&
1513 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1514 (HDRVR)wwi->waveDesc.hWave, wMsg, wwi->waveDesc.dwInstance,
1515 dwParam1, dwParam2))
1517 WARN("can't notify client !\n");
1518 return MMSYSERR_ERROR;
1520 break;
1521 default:
1522 FIXME("Unknown callback message %u\n", wMsg);
1523 return MMSYSERR_INVALPARAM;
1525 return MMSYSERR_NOERROR;
1529 /**************************************************************************
1530 * widHelper_NotifyCompletions [internal]
1532 static void widHelper_NotifyCompletions(WINE_WAVEIN* wwi)
1534 LPWAVEHDR lpWaveHdr;
1535 LPWAVEHDR lpFirstDoneWaveHdr = NULL;
1536 LPWAVEHDR lpLastDoneWaveHdr = NULL;
1538 OSSpinLockLock(&wwi->lock);
1540 /* First, excise all of the done headers from the queue into
1541 * a free-standing list. */
1543 /* Start from lpQueuePtr and keep notifying until:
1544 * - we hit an unfilled wavehdr
1545 * - we hit the end of the list
1547 for (
1548 lpWaveHdr = wwi->lpQueuePtr;
1549 lpWaveHdr &&
1550 lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength;
1551 lpWaveHdr = lpWaveHdr->lpNext
1554 if (!lpFirstDoneWaveHdr)
1555 lpFirstDoneWaveHdr = lpWaveHdr;
1556 lpLastDoneWaveHdr = lpWaveHdr;
1559 if (lpLastDoneWaveHdr)
1561 wwi->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1562 lpLastDoneWaveHdr->lpNext = NULL;
1565 OSSpinLockUnlock(&wwi->lock);
1567 /* Now, send the "done" notification for each header in our list. */
1568 for (lpWaveHdr = lpFirstDoneWaveHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
1570 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1571 lpWaveHdr->dwFlags |= WHDR_DONE;
1573 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1578 /**************************************************************************
1579 * widGetDevCaps [internal]
1581 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1583 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1585 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1587 if (wDevID >= MAX_WAVEINDRV)
1589 TRACE("MAX_WAVEINDRV reached !\n");
1590 return MMSYSERR_BADDEVICEID;
1593 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1594 return MMSYSERR_NOERROR;
1598 /**************************************************************************
1599 * widHelper_DestroyAudioBufferList [internal]
1600 * Convenience function to dispose of our audio buffers
1602 static void widHelper_DestroyAudioBufferList(AudioBufferList* list)
1604 if (list)
1606 UInt32 i;
1607 for (i = 0; i < list->mNumberBuffers; i++)
1609 if (list->mBuffers[i].mData)
1610 HeapFree(GetProcessHeap(), 0, list->mBuffers[i].mData);
1612 HeapFree(GetProcessHeap(), 0, list);
1617 /**************************************************************************
1618 * widHelper_AllocateAudioBufferList [internal]
1619 * Convenience function to allocate our audio buffers
1621 static AudioBufferList* widHelper_AllocateAudioBufferList(UInt32 numChannels, UInt32 size)
1623 AudioBufferList* list;
1624 UInt32 i;
1626 list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
1627 if (list == NULL)
1628 return NULL;
1630 list->mNumberBuffers = numChannels;
1631 for (i = 0; i < numChannels; ++i)
1633 list->mBuffers[i].mNumberChannels = 1;
1634 list->mBuffers[i].mDataByteSize = size;
1635 list->mBuffers[i].mData = HeapAlloc(GetProcessHeap(), 0, size);
1636 if (list->mBuffers[i].mData == NULL)
1638 widHelper_DestroyAudioBufferList(list);
1639 return NULL;
1642 return list;
1646 /**************************************************************************
1647 * widOpen [internal]
1649 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1651 WINE_WAVEIN* wwi;
1652 UInt32 frameCount;
1653 UInt32 bytesPerFrame;
1655 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1656 if (lpDesc == NULL)
1658 WARN("Invalid Parameter !\n");
1659 return MMSYSERR_INVALPARAM;
1661 if (wDevID >= MAX_WAVEINDRV)
1663 TRACE ("MAX_WAVEINDRV reached !\n");
1664 return MMSYSERR_BADDEVICEID;
1667 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1668 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1669 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1671 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1672 lpDesc->lpFormat->nChannels == 0 ||
1673 lpDesc->lpFormat->nSamplesPerSec == 0
1676 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1677 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1678 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1679 return WAVERR_BADFORMAT;
1682 if (dwFlags & WAVE_FORMAT_QUERY)
1684 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1685 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1686 lpDesc->lpFormat->nSamplesPerSec);
1687 return MMSYSERR_NOERROR;
1690 wwi = &WInDev[wDevID];
1691 if (!OSSpinLockTry(&wwi->lock))
1692 return MMSYSERR_ALLOCATED;
1694 if (wwi->state != WINE_WS_CLOSED)
1696 OSSpinLockUnlock(&wwi->lock);
1697 return MMSYSERR_ALLOCATED;
1700 wwi->state = WINE_WS_STOPPED;
1701 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1703 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1704 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1706 if (wwi->format.wBitsPerSample == 0)
1708 WARN("Resetting zeroed wBitsPerSample\n");
1709 wwi->format.wBitsPerSample = 8 *
1710 (wwi->format.wf.nAvgBytesPerSec /
1711 wwi->format.wf.nSamplesPerSec) /
1712 wwi->format.wf.nChannels;
1715 wwi->dwTotalRecorded = 0;
1717 wwi->trace_on = TRACE_ON(wave);
1718 wwi->warn_on = WARN_ON(wave);
1719 wwi->err_on = ERR_ON(wave);
1721 if (!AudioUnit_CreateInputUnit(wwi, &wwi->audioUnit,
1722 wwi->format.wf.nChannels, wwi->format.wf.nSamplesPerSec,
1723 wwi->format.wBitsPerSample, &frameCount))
1725 ERR("AudioUnit_CreateInputUnit failed\n");
1726 OSSpinLockUnlock(&wwi->lock);
1727 return MMSYSERR_ERROR;
1730 /* Allocate our audio buffers */
1731 /* For interleaved audio, we allocate one buffer for all channels. */
1732 bytesPerFrame = wwi->format.wBitsPerSample * wwi->format.wf.nChannels / 8;
1733 wwi->bufferList = widHelper_AllocateAudioBufferList(1, wwi->format.wf.nChannels * frameCount * bytesPerFrame);
1734 if (wwi->bufferList == NULL)
1736 ERR("Failed to allocate buffer list\n");
1737 AudioUnitUninitialize(wwi->audioUnit);
1738 AudioUnit_CloseAudioUnit(wwi->audioUnit);
1739 OSSpinLockUnlock(&wwi->lock);
1740 return MMSYSERR_NOMEM;
1743 OSSpinLockUnlock(&wwi->lock);
1745 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1749 /**************************************************************************
1750 * widClose [internal]
1752 static DWORD widClose(WORD wDevID)
1754 DWORD ret = MMSYSERR_NOERROR;
1755 WINE_WAVEIN* wwi;
1757 TRACE("(%u);\n", wDevID);
1759 if (wDevID >= MAX_WAVEINDRV)
1761 WARN("bad device ID !\n");
1762 return MMSYSERR_BADDEVICEID;
1765 wwi = &WInDev[wDevID];
1766 OSSpinLockLock(&wwi->lock);
1767 if (wwi->state == WINE_WS_CLOSED)
1769 WARN("Device already closed.\n");
1770 ret = MMSYSERR_INVALHANDLE;
1772 else if (wwi->lpQueuePtr)
1774 WARN("Buffers in queue.\n");
1775 ret = WAVERR_STILLPLAYING;
1777 else
1779 wwi->state = WINE_WS_CLOSED;
1782 OSSpinLockUnlock(&wwi->lock);
1784 if (ret == MMSYSERR_NOERROR)
1786 OSStatus err = AudioUnitUninitialize(wwi->audioUnit);
1787 if (err)
1789 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
1790 (char) (err >> 16),
1791 (char) (err >> 8),
1792 (char) err);
1795 if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
1797 ERR("Can't close AudioUnit\n");
1800 /* Dellocate our audio buffers */
1801 widHelper_DestroyAudioBufferList(wwi->bufferList);
1802 wwi->bufferList = NULL;
1804 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1807 return ret;
1811 /**************************************************************************
1812 * widAddBuffer [internal]
1814 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1816 DWORD ret = MMSYSERR_NOERROR;
1817 WINE_WAVEIN* wwi;
1819 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1821 if (wDevID >= MAX_WAVEINDRV)
1823 WARN("invalid device ID\n");
1824 return MMSYSERR_INVALHANDLE;
1826 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
1828 TRACE("never been prepared !\n");
1829 return WAVERR_UNPREPARED;
1831 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1833 TRACE("header already in use !\n");
1834 return WAVERR_STILLPLAYING;
1837 wwi = &WInDev[wDevID];
1838 OSSpinLockLock(&wwi->lock);
1840 if (wwi->state == WINE_WS_CLOSED)
1842 WARN("Trying to add buffer to closed device.\n");
1843 ret = MMSYSERR_INVALHANDLE;
1845 else
1847 LPWAVEHDR* wh;
1849 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1850 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1851 lpWaveHdr->dwBytesRecorded = 0;
1852 lpWaveHdr->lpNext = NULL;
1854 /* insert buffer at end of queue */
1855 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1856 /* Do nothing */;
1857 *wh = lpWaveHdr;
1860 OSSpinLockUnlock(&wwi->lock);
1862 return ret;
1866 /**************************************************************************
1867 * widStart [internal]
1869 static DWORD widStart(WORD wDevID)
1871 TRACE("(%u);\n", wDevID);
1872 if (wDevID >= MAX_WAVEINDRV)
1874 WARN("invalid device ID\n");
1875 return MMSYSERR_INVALHANDLE;
1878 FIXME("unimplemented\n");
1879 return MMSYSERR_NOTENABLED;
1883 /**************************************************************************
1884 * widStop [internal]
1886 static DWORD widStop(WORD wDevID)
1888 DWORD ret = MMSYSERR_NOERROR;
1889 WINE_WAVEIN* wwi;
1890 WAVEHDR* lpWaveHdr = NULL;
1891 OSStatus err;
1893 TRACE("(%u);\n", wDevID);
1894 if (wDevID >= MAX_WAVEINDRV)
1896 WARN("invalid device ID\n");
1897 return MMSYSERR_INVALHANDLE;
1900 wwi = &WInDev[wDevID];
1902 /* The order of the following operations is important since we can't hold
1903 * the mutex while we make an Audio Unit call. Stop the Audio Unit before
1904 * setting the STOPPED state. In widStart, the order is reversed. This
1905 * guarantees that we can't get into a situation where the state is
1906 * PLAYING but the Audio Unit isn't running. Although we can be in STOPPED
1907 * state with the Audio Unit still running, that's harmless because the
1908 * input callback will just throw away the sound data.
1910 err = AudioOutputUnitStop(wwi->audioUnit);
1911 if (err != noErr)
1912 WARN("Failed to stop AU: %08lx\n", err);
1914 TRACE("Recording stopped.\n");
1916 OSSpinLockLock(&wwi->lock);
1918 if (wwi->state == WINE_WS_CLOSED)
1920 WARN("Trying to stop closed device.\n");
1921 ret = MMSYSERR_INVALHANDLE;
1923 else if (wwi->state != WINE_WS_STOPPED)
1925 wwi->state = WINE_WS_STOPPED;
1926 /* If there's a buffer in progress, it's done. Remove it from the
1927 * queue so that we can return it to the app, below. */
1928 if (wwi->lpQueuePtr)
1930 lpWaveHdr = wwi->lpQueuePtr;
1931 wwi->lpQueuePtr = lpWaveHdr->lpNext;
1935 OSSpinLockUnlock(&wwi->lock);
1937 if (lpWaveHdr)
1939 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1940 lpWaveHdr->dwFlags |= WHDR_DONE;
1941 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1944 return ret;
1948 /**************************************************************************
1949 * widReset [internal]
1951 static DWORD widReset(WORD wDevID)
1953 DWORD ret = MMSYSERR_NOERROR;
1954 WINE_WAVEIN* wwi;
1955 WAVEHDR* lpWaveHdr = NULL;
1957 TRACE("(%u);\n", wDevID);
1958 if (wDevID >= MAX_WAVEINDRV)
1960 WARN("invalid device ID\n");
1961 return MMSYSERR_INVALHANDLE;
1964 wwi = &WInDev[wDevID];
1965 OSSpinLockLock(&wwi->lock);
1967 if (wwi->state == WINE_WS_CLOSED)
1969 WARN("Trying to reset a closed device.\n");
1970 ret = MMSYSERR_INVALHANDLE;
1972 else
1974 lpWaveHdr = wwi->lpQueuePtr;
1975 wwi->lpQueuePtr = NULL;
1976 wwi->state = WINE_WS_STOPPED;
1977 wwi->dwTotalRecorded = 0;
1980 OSSpinLockUnlock(&wwi->lock);
1982 if (ret == MMSYSERR_NOERROR)
1984 OSStatus err = AudioOutputUnitStop(wwi->audioUnit);
1985 if (err != noErr)
1986 WARN("Failed to stop AU: %08lx\n", err);
1988 TRACE("Recording stopped.\n");
1991 while (lpWaveHdr)
1993 WAVEHDR* lpNext = lpWaveHdr->lpNext;
1995 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1996 lpWaveHdr->dwFlags |= WHDR_DONE;
1997 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1999 lpWaveHdr = lpNext;
2002 return ret;
2006 /**************************************************************************
2007 * widGetNumDevs [internal]
2009 static DWORD widGetNumDevs(void)
2011 return MAX_WAVEINDRV;
2015 /**************************************************************************
2016 * widDevInterfaceSize [internal]
2018 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2020 TRACE("(%u, %p)\n", wDevID, dwParam1);
2022 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2023 NULL, 0 ) * sizeof(WCHAR);
2024 return MMSYSERR_NOERROR;
2028 /**************************************************************************
2029 * widDevInterface [internal]
2031 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2033 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2034 NULL, 0 ) * sizeof(WCHAR))
2036 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
2037 dwParam1, dwParam2 / sizeof(WCHAR));
2038 return MMSYSERR_NOERROR;
2040 return MMSYSERR_INVALPARAM;
2044 /**************************************************************************
2045 * widMessage (WINECOREAUDIO.6)
2047 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2048 DWORD dwParam1, DWORD dwParam2)
2050 TRACE("(%u, %04X, %08X, %08X, %08X);\n",
2051 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2053 switch (wMsg)
2055 case DRVM_INIT:
2056 case DRVM_EXIT:
2057 case DRVM_ENABLE:
2058 case DRVM_DISABLE:
2059 /* FIXME: Pretend this is supported */
2060 return 0;
2061 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2062 case WIDM_CLOSE: return widClose (wDevID);
2063 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2064 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
2065 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
2066 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
2067 case WIDM_GETNUMDEVS: return widGetNumDevs ();
2068 case WIDM_RESET: return widReset (wDevID);
2069 case WIDM_START: return widStart (wDevID);
2070 case WIDM_STOP: return widStop (wDevID);
2071 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2072 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2073 default:
2074 FIXME("unknown message %d!\n", wMsg);
2077 return MMSYSERR_NOTSUPPORTED;
2081 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
2082 AudioUnitRenderActionFlags *ioActionFlags,
2083 const AudioTimeStamp *inTimeStamp,
2084 UInt32 inBusNumber,
2085 UInt32 inNumberFrames,
2086 AudioBufferList *ioData)
2088 return noErr;
2091 #else
2093 /**************************************************************************
2094 * widMessage (WINECOREAUDIO.6)
2096 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2097 DWORD dwParam1, DWORD dwParam2)
2099 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2100 return MMSYSERR_NOTENABLED;
2103 /**************************************************************************
2104 * wodMessage (WINECOREAUDIO.7)
2106 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2107 DWORD dwParam1, DWORD dwParam2)
2109 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2110 return MMSYSERR_NOTENABLED;
2113 #endif