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
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 */
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
{
102 char* interface_name
;
104 WAVEOUTCAPSW out_caps
;
106 DWORD in_caps_support
;
110 unsigned audio_fragment
;
112 BOOL bTriggerSupport
;
115 DSDRIVERDESC ds_desc
;
116 DSDRIVERCAPS ds_caps
;
117 DSCDRIVERCAPS dsc_caps
;
121 AudioDeviceID outputDeviceID
;
122 AudioDeviceID inputDeviceID
;
123 AudioStreamBasicDescription streamDescription
;
126 /* for now use the default device */
127 static CoreAudio_Device CoreAudio_DefaultDevice
;
130 volatile int state
; /* one of the WINE_WS_ manifest constants */
131 CoreAudio_Device
*cadev
;
132 WAVEOPENDESC waveDesc
;
134 PCMWAVEFORMAT format
;
137 AudioStreamBasicDescription streamDescription
;
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 */
161 /* Access to the following fields is synchronized across threads. */
163 LPWAVEHDR lpQueuePtr
;
164 DWORD dwTotalRecorded
;
166 /* Synchronization mechanism to protect above fields */
169 /* Capabilities description */
172 /* Record the arguments used when opening the device. */
173 WAVEOPENDESC waveDesc
;
175 PCMWAVEFORMAT format
;
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. */
185 /* These fields aren't used. */
187 CoreAudio_Device
*cadev
;
189 AudioStreamBasicDescription streamDescription
;
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
,
219 UInt32 inNumberFrames
,
220 AudioBufferList
*ioData
);
221 OSStatus
CoreAudio_wiAudioUnitIOProc(void *inRefCon
,
222 AudioUnitRenderActionFlags
*ioActionFlags
,
223 const AudioTimeStamp
*inTimeStamp
,
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
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
);
274 sprintf(unknown
, "UNKNOWN(0x%04x)", msg
);
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
;
289 case kWaveOutNotifyCompletionsMessage
:
290 buffer
= (UInt32
*) CFDataGetBytePtr(data
);
291 wodHelper_NotifyCompletions(&WOutDev
[buffer
[0]], FALSE
);
293 case kWaveInCallbackMessage
:
295 CFRunLoopStop(CFRunLoopGetCurrent());
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
);
312 CFRunLoopSourceInvalidate(source
);
314 CFRelease(port_ReceiveInMessageThread
);
319 /**************************************************************************
320 * wodSendNotifyCompletionsMessage [internal]
321 * Call from AudioUnit IO thread can't use Wine debug channels.
323 static void wodSendNotifyCompletionsMessage(WINE_WAVEOUT
* wwo
)
328 buffer
= (UInt32
) wwo
->woID
;
330 data
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*)&buffer
, sizeof(buffer
));
334 CFMessagePortSendRequest(Port_SendToMessageThread
, kWaveOutNotifyCompletionsMessage
, data
, 0.0, 0.0, NULL
, NULL
);
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
) {
348 lpTime
->u
.sample
= position
/ (format
->wBitsPerSample
/ 8 * format
->wf
.nChannels
);
349 TRACE("TIME_SAMPLES=%u\n", lpTime
->u
.sample
);
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
);
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
);
372 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime
->wType
);
373 lpTime
->wType
= TIME_BYTES
;
376 lpTime
->u
.cb
= position
;
377 TRACE("TIME_BYTES=%u\n", lpTime
->u
.cb
);
380 return MMSYSERR_NOERROR
;
383 /**************************************************************************
384 * CoreAudio_GetDevCaps [internal]
386 BOOL
CoreAudio_GetDevCaps (void)
390 AudioDeviceID devId
= CoreAudio_DefaultDevice
.outputDeviceID
;
392 char name
[MAXPNAMELEN
];
394 propertySize
= MAXPNAMELEN
;
395 status
= AudioDeviceGetProperty(devId
, 0 , FALSE
, kAudioDevicePropertyDeviceName
, &propertySize
, name
);
397 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status
>> 24),
398 (char) (status
>> 16),
399 (char) (status
>> 8),
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),
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
;
451 /******************************************************************
454 * Initialize CoreAudio_DefaultDevice
456 LONG
CoreAudio_WaveInit(void)
460 CHAR szPname
[MAXPNAMELEN
];
463 CFStringRef messageThreadPortName
;
464 CFMessagePortRef port_ReceiveInMessageThread
;
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
);
477 ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status
>> 24),
478 (char) (status
>> 16),
479 (char) (status
>> 8),
483 if (CoreAudio_DefaultDevice
.outputDeviceID
== kAudioDeviceUnknown
) {
484 ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n");
488 if ( ! CoreAudio_GetDevCaps() )
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
;
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 */
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");
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
);
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
);
591 hThread
= CreateThread(NULL
, 0, messageThread
, (LPVOID
)port_ReceiveInMessageThread
, 0, NULL
);
594 ERR("Can't create message thread\n");
595 CFRelease(port_ReceiveInMessageThread
);
596 CFRelease(Port_SendToMessageThread
);
597 Port_SendToMessageThread
= NULL
;
601 /* The message thread is responsible for releasing port_ReceiveInMessageThread. */
606 void CoreAudio_WaveRelease(void)
608 /* Stop CFRunLoop in messageThread */
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
)
629 if (wwo
->wFlags
!= DCB_NULL
&&
630 !DriverCallback(wwo
->waveDesc
.dwCallback
, wwo
->wFlags
,
631 (HDRVR
)wwo
->waveDesc
.hWave
, wMsg
, wwo
->waveDesc
.dwInstance
,
634 return MMSYSERR_ERROR
;
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 /**************************************************************************
667 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
672 AudioStreamBasicDescription streamFormat
;
674 TRACE("(%u, %p, %08x);\n", wDevID
, lpDesc
, dwFlags
);
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 */
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
);
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
);
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);
794 /**************************************************************************
795 * wodClose [internal]
797 static DWORD
wodClose(WORD wDevID
)
799 DWORD ret
= MMSYSERR_NOERROR
;
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
);
814 WARN("buffers still playing !\n");
815 OSSpinLockUnlock(&wwo
->lock
);
816 ret
= WAVERR_STILLPLAYING
;
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
);
829 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err
>> 24),
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);
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
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
)
908 fprintf(stderr
, "warn:winecoreaudio:wodHelper_CheckForLoopBegin Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr
);
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)
942 wwo
->lpPlayPtr
= wwo
->lpLoopPtr
;
947 wwo
->lpLoopPtr
= NULL
;
952 /* We didn't loop back. Advance to the next wave header */
953 wwo
->lpPlayPtr
= wwo
->lpPlayPtr
->lpNext
;
956 wwo
->state
= WINE_WS_STOPPED
;
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
)
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. */
975 lpFirstDoneWaveHdr
= wwo
->lpQueuePtr
;
976 wwo
->lpQueuePtr
= NULL
;
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
988 lpWaveHdr
= wwo
->lpQueuePtr
;
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
)
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
))
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
)
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
);
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
)
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
;
1144 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
1146 OSSpinLockUnlock(&WOutDev
[wDevID
].lock
);
1148 status
= AudioOutputUnitStart(WOutDev
[wDevID
].audioUnit
);
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
)
1166 TRACE("(%u);\n", wDevID
);
1168 if (wDevID
>= MAX_WAVEOUTDRV
)
1170 WARN("bad device ID !\n");
1171 return MMSYSERR_BADDEVICEID
;
1174 wwo
= &WOutDev
[wDevID
];
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
);
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
)
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
)
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
)
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)
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
)
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
);
1330 /* FIXME: Pretend this is supported */
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();
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
;
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
;
1378 /* IDsDriverImpl fields */
1380 IDsDriverBufferImpl
*primary
;
1383 struct IDsDriverBufferImpl
1385 /* IUnknown fields */
1386 const IDsDriverBufferVtbl
*lpVtbl
;
1388 /* IDsDriverBufferImpl fields */
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
,
1402 UInt32 inNumberFrames
,
1403 AudioBufferList
*ioData
)
1406 WINE_WAVEOUT
*wwo
= (WINE_WAVEOUT
*) inRefCon
;
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
;
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
;
1437 wodHelper_PlayPtrNext(wwo
);
1442 OSSpinLockUnlock(&wwo
->lock
);
1444 /* We can't provide any more wave data. Fill the rest with silence. */
1448 *ioActionFlags
|= kAudioUnitRenderAction_OutputIsSilence
;
1449 memset((char*)ioData
->mBuffers
[0].mData
+ dataProvided
, 0, dataNeeded
);
1450 dataProvided
+= dataNeeded
;
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
);
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
);
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
;
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
)
1525 TRACE("(%u, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
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
;
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
;
1636 wwi
->state
= WINE_WS_CLOSED
;
1639 OSSpinLockUnlock(&wwi
->lock
);
1641 if (ret
== MMSYSERR_NOERROR
)
1643 OSStatus err
= AudioUnitUninitialize(wwi
->audioUnit
);
1646 ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err
>> 24),
1652 if (!AudioUnit_CloseAudioUnit(wwi
->audioUnit
))
1654 ERR("Can't close AudioUnit\n");
1657 ret
= widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
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
);
1789 /* FIXME: Pretend this is supported */
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
);
1804 FIXME("unknown message %d!\n", wMsg
);
1807 return MMSYSERR_NOTSUPPORTED
;
1811 OSStatus
CoreAudio_wiAudioUnitIOProc(void *inRefCon
,
1812 AudioUnitRenderActionFlags
*ioActionFlags
,
1813 const AudioTimeStamp
*inTimeStamp
,
1815 UInt32 inNumberFrames
,
1816 AudioBufferList
*ioData
)
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
;