winecoreaudio: Implement widOpen and widClose.
[wine/multimedia.git] / dlls / winmm / winecoreaudio / audio.c
blob51e71a86ed92fa03da70068c5c91fae062807667
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 /* Access to the following fields is synchronized across threads. */
162 volatile int state;
163 LPWAVEHDR lpQueuePtr;
164 DWORD dwTotalRecorded;
166 /* Synchronization mechanism to protect above fields */
167 OSSpinLock lock;
169 /* Capabilities description */
170 WAVEINCAPSW caps;
172 /* Record the arguments used when opening the device. */
173 WAVEOPENDESC waveDesc;
174 WORD wFlags;
175 PCMWAVEFORMAT format;
177 AudioUnit audioUnit;
179 /* Record state of debug channels at open. Used to control fprintf's since
180 * we can't use Wine debug channel calls in non-Wine AudioUnit threads. */
181 BOOL trace_on;
182 BOOL warn_on;
183 BOOL err_on;
185 /* These fields aren't used. */
186 #if 0
187 CoreAudio_Device *cadev;
189 AudioStreamBasicDescription streamDescription;
190 #endif
191 } WINE_WAVEIN;
193 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
194 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
196 static CFMessagePortRef Port_SendToMessageThread;
198 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo);
199 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force);
201 extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au);
202 extern int AudioUnit_CloseAudioUnit(AudioUnit au);
203 extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *streamFormat);
205 extern OSStatus AudioOutputUnitStart(AudioUnit au);
206 extern OSStatus AudioOutputUnitStop(AudioUnit au);
207 extern OSStatus AudioUnitUninitialize(AudioUnit au);
209 extern int AudioUnit_SetVolume(AudioUnit au, float left, float right);
210 extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right);
212 extern int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
213 WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample);
215 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
216 AudioUnitRenderActionFlags *ioActionFlags,
217 const AudioTimeStamp *inTimeStamp,
218 UInt32 inBusNumber,
219 UInt32 inNumberFrames,
220 AudioBufferList *ioData);
221 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
222 AudioUnitRenderActionFlags *ioActionFlags,
223 const AudioTimeStamp *inTimeStamp,
224 UInt32 inBusNumber,
225 UInt32 inNumberFrames,
226 AudioBufferList *ioData);
228 /* These strings used only for tracing */
230 static const char * getMessage(UINT msg)
232 static char unknown[32];
233 #define MSG_TO_STR(x) case x: return #x
234 switch(msg) {
235 MSG_TO_STR(DRVM_INIT);
236 MSG_TO_STR(DRVM_EXIT);
237 MSG_TO_STR(DRVM_ENABLE);
238 MSG_TO_STR(DRVM_DISABLE);
239 MSG_TO_STR(WIDM_OPEN);
240 MSG_TO_STR(WIDM_CLOSE);
241 MSG_TO_STR(WIDM_ADDBUFFER);
242 MSG_TO_STR(WIDM_PREPARE);
243 MSG_TO_STR(WIDM_UNPREPARE);
244 MSG_TO_STR(WIDM_GETDEVCAPS);
245 MSG_TO_STR(WIDM_GETNUMDEVS);
246 MSG_TO_STR(WIDM_GETPOS);
247 MSG_TO_STR(WIDM_RESET);
248 MSG_TO_STR(WIDM_START);
249 MSG_TO_STR(WIDM_STOP);
250 MSG_TO_STR(WODM_OPEN);
251 MSG_TO_STR(WODM_CLOSE);
252 MSG_TO_STR(WODM_WRITE);
253 MSG_TO_STR(WODM_PAUSE);
254 MSG_TO_STR(WODM_GETPOS);
255 MSG_TO_STR(WODM_BREAKLOOP);
256 MSG_TO_STR(WODM_PREPARE);
257 MSG_TO_STR(WODM_UNPREPARE);
258 MSG_TO_STR(WODM_GETDEVCAPS);
259 MSG_TO_STR(WODM_GETNUMDEVS);
260 MSG_TO_STR(WODM_GETPITCH);
261 MSG_TO_STR(WODM_SETPITCH);
262 MSG_TO_STR(WODM_GETPLAYBACKRATE);
263 MSG_TO_STR(WODM_SETPLAYBACKRATE);
264 MSG_TO_STR(WODM_GETVOLUME);
265 MSG_TO_STR(WODM_SETVOLUME);
266 MSG_TO_STR(WODM_RESTART);
267 MSG_TO_STR(WODM_RESET);
268 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
269 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
270 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
271 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
273 #undef MSG_TO_STR
274 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
275 return unknown;
278 #define kStopLoopMessage 0
279 #define kWaveOutNotifyCompletionsMessage 1
280 #define kWaveInCallbackMessage 2
282 /* Mach Message Handling */
283 static CFDataRef wodMessageHandler(CFMessagePortRef port_ReceiveInMessageThread, SInt32 msgid, CFDataRef data, void *info)
285 UInt32 *buffer = NULL;
287 switch (msgid)
289 case kWaveOutNotifyCompletionsMessage:
290 buffer = (UInt32 *) CFDataGetBytePtr(data);
291 wodHelper_NotifyCompletions(&WOutDev[buffer[0]], FALSE);
292 break;
293 case kWaveInCallbackMessage:
294 default:
295 CFRunLoopStop(CFRunLoopGetCurrent());
296 break;
299 return NULL;
302 static DWORD WINAPI messageThread(LPVOID p)
304 CFMessagePortRef port_ReceiveInMessageThread = (CFMessagePortRef) p;
305 CFRunLoopSourceRef source;
307 source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port_ReceiveInMessageThread, (CFIndex)0);
308 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
310 CFRunLoopRun();
312 CFRunLoopSourceInvalidate(source);
313 CFRelease(source);
314 CFRelease(port_ReceiveInMessageThread);
316 return 0;
319 /**************************************************************************
320 * wodSendNotifyCompletionsMessage [internal]
321 * Call from AudioUnit IO thread can't use Wine debug channels.
323 static void wodSendNotifyCompletionsMessage(WINE_WAVEOUT* wwo)
325 CFDataRef data;
326 UInt32 buffer;
328 buffer = (UInt32) wwo->woID;
330 data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&buffer, sizeof(buffer));
331 if (!data)
332 return;
334 CFMessagePortSendRequest(Port_SendToMessageThread, kWaveOutNotifyCompletionsMessage, data, 0.0, 0.0, NULL, NULL);
335 CFRelease(data);
338 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
339 PCMWAVEFORMAT* format)
341 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
342 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
343 format->wf.nChannels, format->wf.nAvgBytesPerSec);
344 TRACE("Position in bytes=%u\n", position);
346 switch (lpTime->wType) {
347 case TIME_SAMPLES:
348 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
349 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
350 break;
351 case TIME_MS:
352 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
353 TRACE("TIME_MS=%u\n", lpTime->u.ms);
354 break;
355 case TIME_SMPTE:
356 lpTime->u.smpte.fps = 30;
357 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
358 position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
359 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
360 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
361 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
362 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
363 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
364 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
365 lpTime->u.smpte.fps = 30;
366 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
367 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
368 lpTime->u.smpte.hour, lpTime->u.smpte.min,
369 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
370 break;
371 default:
372 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
373 lpTime->wType = TIME_BYTES;
374 /* fall through */
375 case TIME_BYTES:
376 lpTime->u.cb = position;
377 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
378 break;
380 return MMSYSERR_NOERROR;
383 /**************************************************************************
384 * CoreAudio_GetDevCaps [internal]
386 BOOL CoreAudio_GetDevCaps (void)
388 OSStatus status;
389 UInt32 propertySize;
390 AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID;
392 char name[MAXPNAMELEN];
394 propertySize = MAXPNAMELEN;
395 status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
396 if (status) {
397 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
398 (char) (status >> 16),
399 (char) (status >> 8),
400 (char) status);
401 return FALSE;
404 memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name));
405 strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv");
406 MultiByteToWideChar(CP_ACP, 0, name, sizeof(name),
407 CoreAudio_DefaultDevice.out_caps.szPname,
408 sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR));
409 memcpy(CoreAudio_DefaultDevice.dev_name, name, 32);
411 propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription);
412 status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription);
413 if (status != noErr) {
414 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
415 (char) (status >> 16),
416 (char) (status >> 8),
417 (char) status);
418 return FALSE;
421 TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n"
422 "mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n"
423 "mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
424 CoreAudio_DefaultDevice.streamDescription.mSampleRate,
425 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24),
426 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16),
427 (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8),
428 (char) CoreAudio_DefaultDevice.streamDescription.mFormatID,
429 CoreAudio_DefaultDevice.streamDescription.mFormatFlags,
430 CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket,
431 CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket,
432 CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame,
433 CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame,
434 CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel);
436 CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe;
437 CoreAudio_DefaultDevice.out_caps.wPid = 0x0001;
439 CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001;
440 CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000;
441 CoreAudio_DefaultDevice.out_caps.wReserved1 = 0;
442 CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME;
443 CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME;
445 CoreAudio_DefaultDevice.out_caps.wChannels = 2;
446 CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16;
448 return TRUE;
451 /******************************************************************
452 * CoreAudio_WaveInit
454 * Initialize CoreAudio_DefaultDevice
456 LONG CoreAudio_WaveInit(void)
458 OSStatus status;
459 UInt32 propertySize;
460 CHAR szPname[MAXPNAMELEN];
461 int i;
462 HANDLE hThread;
463 CFStringRef messageThreadPortName;
464 CFMessagePortRef port_ReceiveInMessageThread;
466 TRACE("()\n");
468 /* number of sound cards */
469 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
470 propertySize /= sizeof(AudioDeviceID);
471 TRACE("sound cards : %lu\n", propertySize);
473 /* Get the output device */
474 propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID);
475 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID);
476 if (status) {
477 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24),
478 (char) (status >> 16),
479 (char) (status >> 8),
480 (char) status);
481 return 1;
483 if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) {
484 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
485 return 1;
488 if ( ! CoreAudio_GetDevCaps() )
489 return 1;
491 CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1);
492 sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name);
494 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
496 WOutDev[i].state = WINE_WS_CLOSED;
497 WOutDev[i].cadev = &CoreAudio_DefaultDevice;
498 WOutDev[i].woID = i;
500 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps));
502 WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */
503 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
504 snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i);
505 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR));
506 snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i);
508 WOutDev[i].caps.vDriverVersion = 0x0001;
509 WOutDev[i].caps.dwFormats = 0x00000000;
510 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
512 WOutDev[i].caps.wChannels = 2;
513 /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */
515 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
516 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
517 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
518 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
519 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
520 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
521 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
522 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
523 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
524 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
525 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
526 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
528 WOutDev[i].lock = 0; /* initialize the mutex */
531 for (i = 0; i < MAX_WAVEINDRV; ++i)
533 memset(&WInDev[i], 0, sizeof(WInDev[i]));
535 /* Establish preconditions for widOpen */
536 WInDev[i].state = WINE_WS_CLOSED;
537 WInDev[i].lock = 0; /* initialize the mutex */
539 /* Fill in capabilities. widGetDevCaps can be called at any time. */
540 WInDev[i].caps.wMid = 0xcafe; /* Manufac ID */
541 WInDev[i].caps.wPid = 0x0001; /* Product ID */
542 WInDev[i].caps.vDriverVersion = 0x0001;
544 snprintf(szPname, sizeof(szPname), "CoreAudio WaveIn %d", i);
545 MultiByteToWideChar(CP_ACP, 0, szPname, -1, WInDev[i].caps.szPname, sizeof(WInDev[i].caps.szPname)/sizeof(WCHAR));
547 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
548 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
549 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
550 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
551 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
552 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
553 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
554 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
555 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
556 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
557 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
558 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
560 WInDev[i].caps.wChannels = 2;
563 /* create mach messages handler */
564 srandomdev();
565 messageThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
566 CFSTR("WaveMessagePort.%d.%lu"), getpid(), (unsigned long)random());
567 if (!messageThreadPortName)
569 ERR("Can't create message thread port name\n");
570 return 1;
573 port_ReceiveInMessageThread = CFMessagePortCreateLocal(kCFAllocatorDefault, messageThreadPortName,
574 &wodMessageHandler, NULL, NULL);
575 if (!port_ReceiveInMessageThread)
577 ERR("Can't create message thread local port\n");
578 CFRelease(messageThreadPortName);
579 return 1;
582 Port_SendToMessageThread = CFMessagePortCreateRemote(kCFAllocatorDefault, messageThreadPortName);
583 CFRelease(messageThreadPortName);
584 if (!Port_SendToMessageThread)
586 ERR("Can't create port for sending to message thread\n");
587 CFRelease(port_ReceiveInMessageThread);
588 return 1;
591 hThread = CreateThread(NULL, 0, messageThread, (LPVOID)port_ReceiveInMessageThread, 0, NULL);
592 if ( !hThread )
594 ERR("Can't create message thread\n");
595 CFRelease(port_ReceiveInMessageThread);
596 CFRelease(Port_SendToMessageThread);
597 Port_SendToMessageThread = NULL;
598 return 1;
601 /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
603 return 0;
606 void CoreAudio_WaveRelease(void)
608 /* Stop CFRunLoop in messageThread */
609 TRACE("()\n");
611 CFMessagePortSendRequest(Port_SendToMessageThread, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL);
612 CFRelease(Port_SendToMessageThread);
613 Port_SendToMessageThread = NULL;
616 /*======================================================================*
617 * Low level WAVE OUT implementation *
618 *======================================================================*/
620 /**************************************************************************
621 * wodNotifyClient [internal]
623 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
625 switch (wMsg) {
626 case WOM_OPEN:
627 case WOM_CLOSE:
628 case WOM_DONE:
629 if (wwo->wFlags != DCB_NULL &&
630 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
631 (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance,
632 dwParam1, dwParam2))
634 return MMSYSERR_ERROR;
636 break;
637 default:
638 return MMSYSERR_INVALPARAM;
640 return MMSYSERR_NOERROR;
644 /**************************************************************************
645 * wodGetDevCaps [internal]
647 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
649 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
651 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
653 if (wDevID >= MAX_WAVEOUTDRV)
655 TRACE("MAX_WAVOUTDRV reached !\n");
656 return MMSYSERR_BADDEVICEID;
659 TRACE("dwSupport=(0x%x), dwFormats=(0x%x)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats);
660 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
661 return MMSYSERR_NOERROR;
664 /**************************************************************************
665 * wodOpen [internal]
667 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
669 WINE_WAVEOUT* wwo;
670 DWORD retval;
671 DWORD ret;
672 AudioStreamBasicDescription streamFormat;
674 TRACE("(%u, %p, %08x);\n", wDevID, lpDesc, dwFlags);
675 if (lpDesc == NULL)
677 WARN("Invalid Parameter !\n");
678 return MMSYSERR_INVALPARAM;
680 if (wDevID >= MAX_WAVEOUTDRV) {
681 TRACE("MAX_WAVOUTDRV reached !\n");
682 return MMSYSERR_BADDEVICEID;
685 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
686 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
687 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
689 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
690 lpDesc->lpFormat->nChannels == 0 ||
691 lpDesc->lpFormat->nSamplesPerSec == 0
694 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
695 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
696 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
697 return WAVERR_BADFORMAT;
700 if (dwFlags & WAVE_FORMAT_QUERY)
702 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
703 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
704 lpDesc->lpFormat->nSamplesPerSec);
705 return MMSYSERR_NOERROR;
708 wwo = &WOutDev[wDevID];
709 if (!OSSpinLockTry(&wwo->lock))
710 return MMSYSERR_ALLOCATED;
712 if (wwo->state != WINE_WS_CLOSED)
714 OSSpinLockUnlock(&wwo->lock);
715 return MMSYSERR_ALLOCATED;
718 if (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit))
720 ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo);
721 OSSpinLockUnlock(&wwo->lock);
722 return MMSYSERR_ERROR;
725 if ((dwFlags & WAVE_DIRECTSOUND) &&
726 !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
727 /* not supported, ignore it */
728 dwFlags &= ~WAVE_DIRECTSOUND;
730 streamFormat.mFormatID = kAudioFormatLinearPCM;
731 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
732 /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */
733 if (lpDesc->lpFormat->wBitsPerSample != 8)
734 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
735 # ifdef WORDS_BIGENDIAN
736 streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; /* FIXME Wave format is little endian */
737 # endif
739 streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec;
740 streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels;
741 streamFormat.mFramesPerPacket = 1;
742 streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample;
743 streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8;
744 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
746 ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, &streamFormat);
747 if (!ret)
749 AudioUnit_CloseAudioUnit(wwo->audioUnit);
750 OSSpinLockUnlock(&wwo->lock);
751 return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */
753 wwo->streamDescription = streamFormat;
755 ret = AudioOutputUnitStart(wwo->audioUnit);
756 if (ret)
758 ERR("AudioOutputUnitStart failed: %08x\n", ret);
759 AudioUnitUninitialize(wwo->audioUnit);
760 AudioUnit_CloseAudioUnit(wwo->audioUnit);
761 OSSpinLockUnlock(&wwo->lock);
762 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
765 wwo->state = WINE_WS_STOPPED;
767 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
769 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
770 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
772 if (wwo->format.wBitsPerSample == 0) {
773 WARN("Resetting zeroed wBitsPerSample\n");
774 wwo->format.wBitsPerSample = 8 *
775 (wwo->format.wf.nAvgBytesPerSec /
776 wwo->format.wf.nSamplesPerSec) /
777 wwo->format.wf.nChannels;
780 wwo->dwPlayedTotal = 0;
781 wwo->dwWrittenTotal = 0;
783 wwo->trace_on = TRACE_ON(wave);
784 wwo->warn_on = WARN_ON(wave);
785 wwo->err_on = ERR_ON(wave);
787 OSSpinLockUnlock(&wwo->lock);
789 retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
791 return retval;
794 /**************************************************************************
795 * wodClose [internal]
797 static DWORD wodClose(WORD wDevID)
799 DWORD ret = MMSYSERR_NOERROR;
800 WINE_WAVEOUT* wwo;
802 TRACE("(%u);\n", wDevID);
804 if (wDevID >= MAX_WAVEOUTDRV)
806 WARN("bad device ID !\n");
807 return MMSYSERR_BADDEVICEID;
810 wwo = &WOutDev[wDevID];
811 OSSpinLockLock(&wwo->lock);
812 if (wwo->lpQueuePtr)
814 WARN("buffers still playing !\n");
815 OSSpinLockUnlock(&wwo->lock);
816 ret = WAVERR_STILLPLAYING;
817 } else
819 OSStatus err;
820 /* sanity check: this should not happen since the device must have been reset before */
821 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
823 wwo->state = WINE_WS_CLOSED; /* mark the device as closed */
825 OSSpinLockUnlock(&wwo->lock);
827 err = AudioUnitUninitialize(wwo->audioUnit);
828 if (err) {
829 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
830 (char) (err >> 16),
831 (char) (err >> 8),
832 (char) err);
833 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
836 if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) )
838 ERR("Can't close AudioUnit\n");
839 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
842 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
845 return ret;
848 /**************************************************************************
849 * wodPrepare [internal]
851 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
853 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
855 if (wDevID >= MAX_WAVEOUTDRV) {
856 WARN("bad device ID !\n");
857 return MMSYSERR_BADDEVICEID;
860 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
861 return WAVERR_STILLPLAYING;
863 lpWaveHdr->dwFlags |= WHDR_PREPARED;
864 lpWaveHdr->dwFlags &= ~WHDR_DONE;
866 return MMSYSERR_NOERROR;
869 /**************************************************************************
870 * wodUnprepare [internal]
872 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
874 TRACE("(%u, %p, %08x);\n", wDevID, lpWaveHdr, dwSize);
876 if (wDevID >= MAX_WAVEOUTDRV) {
877 WARN("bad device ID !\n");
878 return MMSYSERR_BADDEVICEID;
881 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
882 return WAVERR_STILLPLAYING;
884 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
885 lpWaveHdr->dwFlags |= WHDR_DONE;
887 return MMSYSERR_NOERROR;
891 /**************************************************************************
892 * wodHelper_CheckForLoopBegin [internal]
894 * Check if the new waveheader is the beginning of a loop, and set up
895 * state if so.
896 * This is called with the WAVEOUT lock held.
897 * Call from AudioUnit IO thread can't use Wine debug channels.
899 static void wodHelper_CheckForLoopBegin(WINE_WAVEOUT* wwo)
901 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
903 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
905 if (wwo->lpLoopPtr)
907 if (wwo->warn_on)
908 fprintf(stderr, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
910 else
912 if (wwo->trace_on)
913 fprintf(stderr, "trace:winecoreaudio:wodHelper_CheckForLoopBegin Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
915 wwo->lpLoopPtr = lpWaveHdr;
916 /* Windows does not touch WAVEHDR.dwLoops,
917 * so we need to make an internal copy */
918 wwo->dwLoops = lpWaveHdr->dwLoops;
924 /**************************************************************************
925 * wodHelper_PlayPtrNext [internal]
927 * Advance the play pointer to the next waveheader, looping if required.
928 * This is called with the WAVEOUT lock held.
929 * Call from AudioUnit IO thread can't use Wine debug channels.
931 static void wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo)
933 BOOL didLoopBack = FALSE;
935 wwo->dwPartialOffset = 0;
936 if ((wwo->lpPlayPtr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr)
938 /* We're at the end of a loop, loop if required */
939 if (wwo->dwLoops > 1)
941 wwo->dwLoops--;
942 wwo->lpPlayPtr = wwo->lpLoopPtr;
943 didLoopBack = TRUE;
945 else
947 wwo->lpLoopPtr = NULL;
950 if (!didLoopBack)
952 /* We didn't loop back. Advance to the next wave header */
953 wwo->lpPlayPtr = wwo->lpPlayPtr->lpNext;
955 if (!wwo->lpPlayPtr)
956 wwo->state = WINE_WS_STOPPED;
957 else
958 wodHelper_CheckForLoopBegin(wwo);
962 /* if force is TRUE then notify the client that all the headers were completed
964 static void wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
966 LPWAVEHDR lpWaveHdr;
967 LPWAVEHDR lpFirstDoneWaveHdr = NULL;
969 OSSpinLockLock(&wwo->lock);
971 /* First, excise all of the done headers from the queue into
972 * a free-standing list. */
973 if (force)
975 lpFirstDoneWaveHdr = wwo->lpQueuePtr;
976 wwo->lpQueuePtr = NULL;
978 else
980 LPWAVEHDR lpLastDoneWaveHdr = NULL;
982 /* Start from lpQueuePtr and keep notifying until:
983 * - we hit an unwritten wavehdr
984 * - we hit the beginning of a running loop
985 * - we hit a wavehdr which hasn't finished playing
987 for (
988 lpWaveHdr = wwo->lpQueuePtr;
989 lpWaveHdr &&
990 lpWaveHdr != wwo->lpPlayPtr &&
991 lpWaveHdr != wwo->lpLoopPtr;
992 lpWaveHdr = lpWaveHdr->lpNext
995 if (!lpFirstDoneWaveHdr)
996 lpFirstDoneWaveHdr = lpWaveHdr;
997 lpLastDoneWaveHdr = lpWaveHdr;
1000 if (lpLastDoneWaveHdr)
1002 wwo->lpQueuePtr = lpLastDoneWaveHdr->lpNext;
1003 lpLastDoneWaveHdr->lpNext = NULL;
1007 OSSpinLockUnlock(&wwo->lock);
1009 /* Now, send the "done" notification for each header in our list. */
1010 for (lpWaveHdr = lpFirstDoneWaveHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext)
1012 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1013 lpWaveHdr->dwFlags |= WHDR_DONE;
1015 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1020 /**************************************************************************
1021 * wodWrite [internal]
1024 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1026 LPWAVEHDR*wh;
1027 WINE_WAVEOUT *wwo;
1029 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1031 /* first, do the sanity checks... */
1032 if (wDevID >= MAX_WAVEOUTDRV)
1034 WARN("bad dev ID !\n");
1035 return MMSYSERR_BADDEVICEID;
1038 wwo = &WOutDev[wDevID];
1040 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1042 TRACE("unprepared\n");
1043 return WAVERR_UNPREPARED;
1046 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1048 TRACE("still playing\n");
1049 return WAVERR_STILLPLAYING;
1052 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1053 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1054 lpWaveHdr->lpNext = 0;
1056 OSSpinLockLock(&wwo->lock);
1057 /* insert buffer at the end of queue */
1058 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1059 /* Do nothing */;
1060 *wh = lpWaveHdr;
1062 if (!wwo->lpPlayPtr)
1064 wwo->lpPlayPtr = lpWaveHdr;
1066 if (wwo->state == WINE_WS_STOPPED)
1067 wwo->state = WINE_WS_PLAYING;
1069 wodHelper_CheckForLoopBegin(wwo);
1071 wwo->dwPartialOffset = 0;
1073 OSSpinLockUnlock(&wwo->lock);
1075 return MMSYSERR_NOERROR;
1078 /**************************************************************************
1079 * wodPause [internal]
1081 static DWORD wodPause(WORD wDevID)
1083 OSStatus status;
1085 TRACE("(%u);!\n", wDevID);
1087 if (wDevID >= MAX_WAVEOUTDRV)
1089 WARN("bad device ID !\n");
1090 return MMSYSERR_BADDEVICEID;
1093 /* The order of the following operations is important since we can't hold
1094 * the mutex while we make an Audio Unit call. Stop the Audio Unit before
1095 * setting the PAUSED state. In wodRestart, the order is reversed. This
1096 * guarantees that we can't get into a situation where the state is
1097 * PLAYING or STOPPED but the Audio Unit isn't running. Although we can
1098 * be in PAUSED state with the Audio Unit still running, that's harmless
1099 * because the render callback will just produce silence.
1101 status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit);
1102 if (status) {
1103 WARN("AudioOutputUnitStop return %c%c%c%c\n",
1104 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1107 OSSpinLockLock(&WOutDev[wDevID].lock);
1108 if (WOutDev[wDevID].state == WINE_WS_PLAYING || WOutDev[wDevID].state == WINE_WS_STOPPED)
1109 WOutDev[wDevID].state = WINE_WS_PAUSED;
1110 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1112 return MMSYSERR_NOERROR;
1115 /**************************************************************************
1116 * wodRestart [internal]
1118 static DWORD wodRestart(WORD wDevID)
1120 OSStatus status;
1122 TRACE("(%u);\n", wDevID);
1124 if (wDevID >= MAX_WAVEOUTDRV )
1126 WARN("bad device ID !\n");
1127 return MMSYSERR_BADDEVICEID;
1130 /* The order of the following operations is important since we can't hold
1131 * the mutex while we make an Audio Unit call. Set the PLAYING/STOPPED
1132 * state before starting the Audio Unit. In wodPause, the order is
1133 * reversed. This guarantees that we can't get into a situation where
1134 * the state is PLAYING or STOPPED but the Audio Unit isn't running.
1135 * Although we can be in PAUSED state with the Audio Unit still running,
1136 * that's harmless because the render callback will just produce silence.
1138 OSSpinLockLock(&WOutDev[wDevID].lock);
1139 if (WOutDev[wDevID].state == WINE_WS_PAUSED)
1141 if (WOutDev[wDevID].lpPlayPtr)
1142 WOutDev[wDevID].state = WINE_WS_PLAYING;
1143 else
1144 WOutDev[wDevID].state = WINE_WS_STOPPED;
1146 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1148 status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit);
1149 if (status) {
1150 ERR("AudioOutputUnitStart return %c%c%c%c\n",
1151 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1152 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1155 return MMSYSERR_NOERROR;
1158 /**************************************************************************
1159 * wodReset [internal]
1161 static DWORD wodReset(WORD wDevID)
1163 WINE_WAVEOUT* wwo;
1164 OSStatus status;
1166 TRACE("(%u);\n", wDevID);
1168 if (wDevID >= MAX_WAVEOUTDRV)
1170 WARN("bad device ID !\n");
1171 return MMSYSERR_BADDEVICEID;
1174 wwo = &WOutDev[wDevID];
1176 FIXME("\n");
1178 /* updates current notify list */
1179 /* if resetting, remove all wave headers and notify client that all headers were completed */
1180 wodHelper_NotifyCompletions(wwo, TRUE);
1182 OSSpinLockLock(&wwo->lock);
1184 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1185 wwo->state = WINE_WS_STOPPED;
1186 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1188 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
1190 OSSpinLockUnlock(&wwo->lock);
1192 status = AudioOutputUnitStart(wwo->audioUnit);
1194 if (status) {
1195 ERR( "AudioOutputUnitStart return %c%c%c%c\n",
1196 (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status);
1197 return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */
1200 return MMSYSERR_NOERROR;
1203 /**************************************************************************
1204 * wodGetPosition [internal]
1206 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1208 DWORD val;
1209 WINE_WAVEOUT* wwo;
1211 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
1213 if (wDevID >= MAX_WAVEOUTDRV)
1215 WARN("bad device ID !\n");
1216 return MMSYSERR_BADDEVICEID;
1219 /* if null pointer to time structure return error */
1220 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1222 wwo = &WOutDev[wDevID];
1224 OSSpinLockLock(&WOutDev[wDevID].lock);
1225 val = wwo->dwPlayedTotal;
1226 OSSpinLockUnlock(&WOutDev[wDevID].lock);
1228 return bytes_to_mmtime(lpTime, val, &wwo->format);
1231 /**************************************************************************
1232 * wodGetVolume [internal]
1234 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1236 float left;
1237 float right;
1239 if (wDevID >= MAX_WAVEOUTDRV)
1241 WARN("bad device ID !\n");
1242 return MMSYSERR_BADDEVICEID;
1245 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1247 AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right);
1249 *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
1251 return MMSYSERR_NOERROR;
1254 /**************************************************************************
1255 * wodSetVolume [internal]
1257 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1259 float left;
1260 float right;
1262 if (wDevID >= MAX_WAVEOUTDRV)
1264 WARN("bad device ID !\n");
1265 return MMSYSERR_BADDEVICEID;
1268 left = LOWORD(dwParam) / 65535.0f;
1269 right = HIWORD(dwParam) / 65535.0f;
1271 TRACE("(%u, %08x);\n", wDevID, dwParam);
1273 AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right);
1275 return MMSYSERR_NOERROR;
1278 /**************************************************************************
1279 * wodGetNumDevs [internal]
1281 static DWORD wodGetNumDevs(void)
1283 TRACE("\n");
1284 return MAX_WAVEOUTDRV;
1287 /**************************************************************************
1288 * wodDevInterfaceSize [internal]
1290 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1292 TRACE("(%u, %p)\n", wDevID, dwParam1);
1294 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1295 NULL, 0 ) * sizeof(WCHAR);
1296 return MMSYSERR_NOERROR;
1299 /**************************************************************************
1300 * wodDevInterface [internal]
1302 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1304 TRACE("\n");
1305 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1306 NULL, 0 ) * sizeof(WCHAR))
1308 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1,
1309 dwParam1, dwParam2 / sizeof(WCHAR));
1310 return MMSYSERR_NOERROR;
1312 return MMSYSERR_INVALPARAM;
1315 /**************************************************************************
1316 * wodMessage (WINECOREAUDIO.7)
1318 DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1319 DWORD dwParam1, DWORD dwParam2)
1321 TRACE("(%u, %s, %08x, %08x, %08x);\n",
1322 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
1324 switch (wMsg) {
1325 case DRVM_INIT:
1326 case DRVM_EXIT:
1327 case DRVM_ENABLE:
1328 case DRVM_DISABLE:
1330 /* FIXME: Pretend this is supported */
1331 return 0;
1332 case WODM_OPEN: return wodOpen(wDevID, (LPWAVEOPENDESC) dwParam1, dwParam2);
1333 case WODM_CLOSE: return wodClose(wDevID);
1334 case WODM_WRITE: return wodWrite(wDevID, (LPWAVEHDR) dwParam1, dwParam2);
1335 case WODM_PAUSE: return wodPause(wDevID);
1336 case WODM_GETPOS: return wodGetPosition(wDevID, (LPMMTIME) dwParam1, dwParam2);
1337 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1338 case WODM_PREPARE: return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1339 case WODM_UNPREPARE: return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1341 case WODM_GETDEVCAPS: return wodGetDevCaps(wDevID, (LPWAVEOUTCAPSW) dwParam1, dwParam2);
1342 case WODM_GETNUMDEVS: return wodGetNumDevs();
1344 case WODM_GETPITCH:
1345 case WODM_SETPITCH:
1346 case WODM_GETPLAYBACKRATE:
1347 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1348 case WODM_GETVOLUME: return wodGetVolume(wDevID, (LPDWORD)dwParam1);
1349 case WODM_SETVOLUME: return wodSetVolume(wDevID, dwParam1);
1350 case WODM_RESTART: return wodRestart(wDevID);
1351 case WODM_RESET: return wodReset(wDevID);
1353 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1354 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1355 case DRV_QUERYDSOUNDIFACE:
1356 case DRV_QUERYDSOUNDDESC:
1357 return MMSYSERR_NOTSUPPORTED;
1359 default:
1360 FIXME("unknown message %d!\n", wMsg);
1363 return MMSYSERR_NOTSUPPORTED;
1366 /*======================================================================*
1367 * Low level DSOUND implementation *
1368 *======================================================================*/
1370 typedef struct IDsDriverImpl IDsDriverImpl;
1371 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1373 struct IDsDriverImpl
1375 /* IUnknown fields */
1376 const IDsDriverVtbl *lpVtbl;
1377 DWORD ref;
1378 /* IDsDriverImpl fields */
1379 UINT wDevID;
1380 IDsDriverBufferImpl*primary;
1383 struct IDsDriverBufferImpl
1385 /* IUnknown fields */
1386 const IDsDriverBufferVtbl *lpVtbl;
1387 DWORD ref;
1388 /* IDsDriverBufferImpl fields */
1389 IDsDriverImpl* drv;
1390 DWORD buflen;
1395 CoreAudio IO threaded callback,
1396 we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
1398 OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
1399 AudioUnitRenderActionFlags *ioActionFlags,
1400 const AudioTimeStamp *inTimeStamp,
1401 UInt32 inBusNumber,
1402 UInt32 inNumberFrames,
1403 AudioBufferList *ioData)
1405 UInt32 buffer;
1406 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon;
1407 int needNotify = 0;
1409 unsigned int dataNeeded = ioData->mBuffers[0].mDataByteSize;
1410 unsigned int dataProvided = 0;
1412 OSSpinLockLock(&wwo->lock);
1414 while (dataNeeded > 0 && wwo->state == WINE_WS_PLAYING && wwo->lpPlayPtr)
1416 unsigned int available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1417 unsigned int toCopy;
1419 if (available >= dataNeeded)
1420 toCopy = dataNeeded;
1421 else
1422 toCopy = available;
1424 if (toCopy > 0)
1426 memcpy((char*)ioData->mBuffers[0].mData + dataProvided,
1427 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toCopy);
1428 wwo->dwPartialOffset += toCopy;
1429 wwo->dwPlayedTotal += toCopy;
1430 dataProvided += toCopy;
1431 dataNeeded -= toCopy;
1432 available -= toCopy;
1435 if (available == 0)
1437 wodHelper_PlayPtrNext(wwo);
1438 needNotify = 1;
1442 OSSpinLockUnlock(&wwo->lock);
1444 /* We can't provide any more wave data. Fill the rest with silence. */
1445 if (dataNeeded > 0)
1447 if (!dataProvided)
1448 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
1449 memset((char*)ioData->mBuffers[0].mData + dataProvided, 0, dataNeeded);
1450 dataProvided += dataNeeded;
1451 dataNeeded = 0;
1454 /* We only fill buffer 0. Set any others that might be requested to 0. */
1455 for (buffer = 1; buffer < ioData->mNumberBuffers; buffer++)
1457 memset(ioData->mBuffers[buffer].mData, 0, ioData->mBuffers[buffer].mDataByteSize);
1460 if (needNotify) wodSendNotifyCompletionsMessage(wwo);
1461 return noErr;
1465 /*======================================================================*
1466 * Low level WAVE IN implementation *
1467 *======================================================================*/
1469 /**************************************************************************
1470 * widNotifyClient [internal]
1472 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1474 TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
1476 switch (wMsg)
1478 case WIM_OPEN:
1479 case WIM_CLOSE:
1480 case WIM_DATA:
1481 if (wwi->wFlags != DCB_NULL &&
1482 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1483 (HDRVR)wwi->waveDesc.hWave, wMsg, wwi->waveDesc.dwInstance,
1484 dwParam1, dwParam2))
1486 WARN("can't notify client !\n");
1487 return MMSYSERR_ERROR;
1489 break;
1490 default:
1491 FIXME("Unknown callback message %u\n", wMsg);
1492 return MMSYSERR_INVALPARAM;
1494 return MMSYSERR_NOERROR;
1498 /**************************************************************************
1499 * widGetDevCaps [internal]
1501 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1503 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
1505 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1507 if (wDevID >= MAX_WAVEINDRV)
1509 TRACE("MAX_WAVEINDRV reached !\n");
1510 return MMSYSERR_BADDEVICEID;
1513 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1514 return MMSYSERR_NOERROR;
1518 /**************************************************************************
1519 * widOpen [internal]
1521 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1523 WINE_WAVEIN* wwi;
1525 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1526 if (lpDesc == NULL)
1528 WARN("Invalid Parameter !\n");
1529 return MMSYSERR_INVALPARAM;
1531 if (wDevID >= MAX_WAVEINDRV)
1533 TRACE ("MAX_WAVEINDRV reached !\n");
1534 return MMSYSERR_BADDEVICEID;
1537 TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1538 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1539 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1541 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1542 lpDesc->lpFormat->nChannels == 0 ||
1543 lpDesc->lpFormat->nSamplesPerSec == 0
1546 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d !\n",
1547 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1548 lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample);
1549 return WAVERR_BADFORMAT;
1552 if (dwFlags & WAVE_FORMAT_QUERY)
1554 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
1555 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1556 lpDesc->lpFormat->nSamplesPerSec);
1557 return MMSYSERR_NOERROR;
1560 wwi = &WInDev[wDevID];
1561 if (!OSSpinLockTry(&wwi->lock))
1562 return MMSYSERR_ALLOCATED;
1564 if (wwi->state != WINE_WS_CLOSED)
1566 OSSpinLockUnlock(&wwi->lock);
1567 return MMSYSERR_ALLOCATED;
1570 wwi->state = WINE_WS_STOPPED;
1571 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1573 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1574 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1576 if (wwi->format.wBitsPerSample == 0)
1578 WARN("Resetting zeroed wBitsPerSample\n");
1579 wwi->format.wBitsPerSample = 8 *
1580 (wwi->format.wf.nAvgBytesPerSec /
1581 wwi->format.wf.nSamplesPerSec) /
1582 wwi->format.wf.nChannels;
1585 wwi->dwTotalRecorded = 0;
1587 wwi->trace_on = TRACE_ON(wave);
1588 wwi->warn_on = WARN_ON(wave);
1589 wwi->err_on = ERR_ON(wave);
1591 if (!AudioUnit_CreateInputUnit(wwi, &wwi->audioUnit,
1592 wwi->format.wf.nChannels, wwi->format.wf.nSamplesPerSec,
1593 wwi->format.wBitsPerSample))
1595 ERR("AudioUnit_CreateInputUnit failed\n");
1596 OSSpinLockUnlock(&wwi->lock);
1597 return MMSYSERR_ERROR;
1600 OSSpinLockUnlock(&wwi->lock);
1602 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1606 /**************************************************************************
1607 * widClose [internal]
1609 static DWORD widClose(WORD wDevID)
1611 DWORD ret = MMSYSERR_NOERROR;
1612 WINE_WAVEIN* wwi;
1614 TRACE("(%u);\n", wDevID);
1616 if (wDevID >= MAX_WAVEINDRV)
1618 WARN("bad device ID !\n");
1619 return MMSYSERR_BADDEVICEID;
1622 wwi = &WInDev[wDevID];
1623 OSSpinLockLock(&wwi->lock);
1624 if (wwi->state == WINE_WS_CLOSED)
1626 WARN("Device already closed.\n");
1627 ret = MMSYSERR_INVALHANDLE;
1629 else if (wwi->lpQueuePtr)
1631 WARN("Buffers in queue.\n");
1632 ret = WAVERR_STILLPLAYING;
1634 else
1636 wwi->state = WINE_WS_CLOSED;
1639 OSSpinLockUnlock(&wwi->lock);
1641 if (ret == MMSYSERR_NOERROR)
1643 OSStatus err = AudioUnitUninitialize(wwi->audioUnit);
1644 if (err)
1646 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
1647 (char) (err >> 16),
1648 (char) (err >> 8),
1649 (char) err);
1652 if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
1654 ERR("Can't close AudioUnit\n");
1657 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1660 return ret;
1664 /**************************************************************************
1665 * widAddBuffer [internal]
1667 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1669 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1671 if (wDevID >= MAX_WAVEINDRV)
1673 WARN("invalid device ID\n");
1674 return MMSYSERR_INVALHANDLE;
1676 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
1678 TRACE("never been prepared !\n");
1679 return WAVERR_UNPREPARED;
1681 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1683 TRACE("header already in use !\n");
1684 return WAVERR_STILLPLAYING;
1687 FIXME("unimplemented\n");
1688 return MMSYSERR_NOTENABLED;
1692 /**************************************************************************
1693 * widStart [internal]
1695 static DWORD widStart(WORD wDevID)
1697 TRACE("(%u);\n", wDevID);
1698 if (wDevID >= MAX_WAVEINDRV)
1700 WARN("invalid device ID\n");
1701 return MMSYSERR_INVALHANDLE;
1704 FIXME("unimplemented\n");
1705 return MMSYSERR_NOTENABLED;
1709 /**************************************************************************
1710 * widStop [internal]
1712 static DWORD widStop(WORD wDevID)
1714 TRACE("(%u);\n", wDevID);
1715 if (wDevID >= MAX_WAVEINDRV)
1717 WARN("invalid device ID\n");
1718 return MMSYSERR_INVALHANDLE;
1721 FIXME("unimplemented\n");
1722 return MMSYSERR_NOTENABLED;
1726 /**************************************************************************
1727 * widReset [internal]
1729 static DWORD widReset(WORD wDevID)
1731 TRACE("(%u);\n", wDevID);
1732 if (wDevID >= MAX_WAVEINDRV)
1734 WARN("invalid device ID\n");
1735 return MMSYSERR_INVALHANDLE;
1738 FIXME("unimplemented\n");
1739 return MMSYSERR_NOTENABLED;
1743 /**************************************************************************
1744 * widGetNumDevs [internal]
1746 static DWORD widGetNumDevs(void)
1748 return MAX_WAVEINDRV;
1752 /**************************************************************************
1753 * widDevInterfaceSize [internal]
1755 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1757 TRACE("(%u, %p)\n", wDevID, dwParam1);
1759 FIXME("unimplemented\n");
1760 return MMSYSERR_NOTENABLED;
1764 /**************************************************************************
1765 * widDevInterface [internal]
1767 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1769 FIXME("unimplemented\n");
1770 return MMSYSERR_NOTENABLED;
1774 /**************************************************************************
1775 * widMessage (WINECOREAUDIO.6)
1777 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1778 DWORD dwParam1, DWORD dwParam2)
1780 TRACE("(%u, %04X, %08X, %08X, %08X);\n",
1781 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1783 switch (wMsg)
1785 case DRVM_INIT:
1786 case DRVM_EXIT:
1787 case DRVM_ENABLE:
1788 case DRVM_DISABLE:
1789 /* FIXME: Pretend this is supported */
1790 return 0;
1791 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1792 case WIDM_CLOSE: return widClose (wDevID);
1793 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1794 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
1795 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
1796 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
1797 case WIDM_GETNUMDEVS: return widGetNumDevs ();
1798 case WIDM_RESET: return widReset (wDevID);
1799 case WIDM_START: return widStart (wDevID);
1800 case WIDM_STOP: return widStop (wDevID);
1801 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
1802 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
1803 default:
1804 FIXME("unknown message %d!\n", wMsg);
1807 return MMSYSERR_NOTSUPPORTED;
1811 OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
1812 AudioUnitRenderActionFlags *ioActionFlags,
1813 const AudioTimeStamp *inTimeStamp,
1814 UInt32 inBusNumber,
1815 UInt32 inNumberFrames,
1816 AudioBufferList *ioData)
1818 return noErr;
1821 #else
1823 /**************************************************************************
1824 * widMessage (WINECOREAUDIO.6)
1826 DWORD WINAPI CoreAudio_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1827 DWORD dwParam1, DWORD dwParam2)
1829 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1830 return MMSYSERR_NOTENABLED;
1833 /**************************************************************************
1834 * wodMessage (WINECOREAUDIO.7)
1836 DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1837 DWORD dwParam1, DWORD dwParam2)
1839 FIXME("(%u, %04X, %08X, %08X, %08X): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1840 return MMSYSERR_NOTENABLED;
1843 #endif