Release 1.6-rc2.
[wine/testsucceed.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blob72641472f239e2b0f071e4d84b4214b75168b599
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28 #include "winreg.h"
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31 #include "wine/list.h"
33 #include "ole2.h"
34 #include "mmdeviceapi.h"
35 #include "devpkey.h"
36 #include "dshow.h"
37 #include "dsound.h"
39 #include "initguid.h"
40 #include "endpointvolume.h"
41 #include "audioclient.h"
42 #include "audiopolicy.h"
44 #include <errno.h>
45 #include <limits.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/ioctl.h>
52 #include <fcntl.h>
53 #include <unistd.h>
55 #include <libkern/OSAtomic.h>
56 #include <CoreAudio/CoreAudio.h>
57 #include <AudioToolbox/AudioQueue.h>
58 #include <AudioToolbox/AudioFormat.h>
60 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
62 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
64 #define CAPTURE_BUFFERS 5
66 static const REFERENCE_TIME DefaultPeriod = 200000;
67 static const REFERENCE_TIME MinimumPeriod = 100000;
69 typedef struct _QueuedBufInfo {
70 Float64 start_sampletime;
71 UINT64 start_pos;
72 UINT32 len_frames;
73 struct list entry;
74 } QueuedBufInfo;
76 typedef struct _AQBuffer {
77 AudioQueueBufferRef buf;
78 struct list entry;
79 BOOL used;
80 } AQBuffer;
82 struct ACImpl;
83 typedef struct ACImpl ACImpl;
85 typedef struct _AudioSession {
86 GUID guid;
87 struct list clients;
89 IMMDevice *device;
91 float master_vol;
92 UINT32 channel_count;
93 float *channel_vols;
94 BOOL mute;
96 CRITICAL_SECTION lock;
98 struct list entry;
99 } AudioSession;
101 typedef struct _AudioSessionWrapper {
102 IAudioSessionControl2 IAudioSessionControl2_iface;
103 IChannelAudioVolume IChannelAudioVolume_iface;
104 ISimpleAudioVolume ISimpleAudioVolume_iface;
106 LONG ref;
108 ACImpl *client;
109 AudioSession *session;
110 } AudioSessionWrapper;
112 struct ACImpl {
113 IAudioClient IAudioClient_iface;
114 IAudioRenderClient IAudioRenderClient_iface;
115 IAudioCaptureClient IAudioCaptureClient_iface;
116 IAudioClock IAudioClock_iface;
117 IAudioClock2 IAudioClock2_iface;
118 IAudioStreamVolume IAudioStreamVolume_iface;
120 LONG ref;
122 IMMDevice *parent;
124 WAVEFORMATEX *fmt;
126 EDataFlow dataflow;
127 DWORD flags;
128 AUDCLNT_SHAREMODE share;
129 HANDLE event;
130 float *vols;
132 AudioDeviceID adevid;
133 AudioQueueRef aqueue;
134 AudioObjectPropertyScope scope;
135 HANDLE timer;
136 UINT32 period_ms, bufsize_frames, inbuf_frames;
137 UINT64 last_time, written_frames;
138 AudioQueueBufferRef public_buffer;
139 UINT32 getbuf_last;
140 int playing;
142 Float64 highest_sampletime, next_sampletime;
144 AudioSession *session;
145 AudioSessionWrapper *session_wrapper;
147 struct list entry;
149 struct list avail_buffers;
150 struct list queued_buffers; /* either in avail, queued or public_buffer */
151 struct list queued_bufinfos;
153 OSSpinLock lock;
156 enum PlayingStates {
157 StateStopped = 0,
158 StatePlaying,
159 StateInTransition
162 static const IAudioClientVtbl AudioClient_Vtbl;
163 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
164 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
165 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
166 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
167 static const IAudioClockVtbl AudioClock_Vtbl;
168 static const IAudioClock2Vtbl AudioClock2_Vtbl;
169 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
170 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
171 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
173 typedef struct _SessionMgr {
174 IAudioSessionManager2 IAudioSessionManager2_iface;
176 LONG ref;
178 IMMDevice *device;
179 } SessionMgr;
181 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
182 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
183 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v',0};
184 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
185 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
186 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v','\\','d','e','v','i','c','e','s',0};
187 static const WCHAR guidW[] = {'g','u','i','d',0};
189 static HANDLE g_timer_q;
191 static CRITICAL_SECTION g_sessions_lock;
192 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
194 0, 0, &g_sessions_lock,
195 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
196 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
198 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
199 static struct list g_sessions = LIST_INIT(g_sessions);
201 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames);
202 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
203 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
205 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
207 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
210 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
212 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
215 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
220 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
222 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
225 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
227 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
230 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
232 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
235 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
237 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
240 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
242 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
245 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
247 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
250 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
252 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
255 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
257 switch (reason)
259 case DLL_PROCESS_ATTACH:
260 g_timer_q = CreateTimerQueue();
261 if(!g_timer_q)
262 return FALSE;
263 break;
265 case DLL_PROCESS_DETACH:
266 if (reserved) break;
267 DeleteCriticalSection(&g_sessions_lock);
268 break;
270 return TRUE;
273 /* From <dlls/mmdevapi/mmdevapi.h> */
274 enum DriverPriority {
275 Priority_Unavailable = 0,
276 Priority_Low,
277 Priority_Neutral,
278 Priority_Preferred
281 int WINAPI AUDDRV_GetPriority(void)
283 return Priority_Neutral;
286 static HRESULT osstatus_to_hresult(OSStatus sc)
288 switch(sc){
289 case kAudioFormatUnsupportedDataFormatError:
290 case kAudioFormatUnknownFormatError:
291 case kAudioDeviceUnsupportedFormatError:
292 return AUDCLNT_E_UNSUPPORTED_FORMAT;
293 case kAudioHardwareBadDeviceError:
294 return AUDCLNT_E_DEVICE_INVALIDATED;
296 return E_FAIL;
299 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
300 GUID *guid)
302 HKEY key;
303 BOOL opened = FALSE;
304 LONG lr;
306 if(!drv_key){
307 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
308 NULL, &drv_key, NULL);
309 if(lr != ERROR_SUCCESS){
310 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
311 return;
313 opened = TRUE;
316 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
317 NULL, &key, NULL);
318 if(lr != ERROR_SUCCESS){
319 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
320 goto exit;
323 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
324 sizeof(GUID));
325 if(lr != ERROR_SUCCESS)
326 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
328 RegCloseKey(key);
329 exit:
330 if(opened)
331 RegCloseKey(drv_key);
334 static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid)
336 HKEY key = NULL, dev_key;
337 DWORD type, size = sizeof(*guid);
338 WCHAR key_name[256];
340 static const WCHAR key_fmt[] = {'%','u',0};
342 if(flow == eCapture)
343 key_name[0] = '1';
344 else
345 key_name[0] = '0';
346 key_name[1] = ',';
348 sprintfW(key_name + 2, key_fmt, device);
350 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
351 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
352 if(RegQueryValueExW(dev_key, guidW, 0, &type,
353 (BYTE*)guid, &size) == ERROR_SUCCESS){
354 if(type == REG_BINARY){
355 RegCloseKey(dev_key);
356 RegCloseKey(key);
357 return;
359 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
360 wine_dbgstr_w(key_name), type);
362 RegCloseKey(dev_key);
366 CoCreateGuid(guid);
368 set_device_guid(flow, key, key_name, guid);
370 if(key)
371 RegCloseKey(key);
374 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
375 GUID **guids, UINT *num, UINT *def_index)
377 UInt32 devsize, size;
378 AudioDeviceID *devices;
379 AudioDeviceID default_id;
380 AudioObjectPropertyAddress addr;
381 OSStatus sc;
382 int i, ndevices;
384 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
386 addr.mScope = kAudioObjectPropertyScopeGlobal;
387 addr.mElement = kAudioObjectPropertyElementMaster;
388 if(flow == eRender)
389 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
390 else if(flow == eCapture)
391 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
392 else
393 return E_INVALIDARG;
395 size = sizeof(default_id);
396 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
397 NULL, &size, &default_id);
398 if(sc != noErr){
399 WARN("Getting _DefaultInputDevice property failed: %lx\n", sc);
400 default_id = -1;
403 addr.mSelector = kAudioHardwarePropertyDevices;
404 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
405 NULL, &devsize);
406 if(sc != noErr){
407 WARN("Getting _Devices property size failed: %lx\n", sc);
408 return osstatus_to_hresult(sc);
411 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
412 if(!devices)
413 return E_OUTOFMEMORY;
415 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
416 &devsize, devices);
417 if(sc != noErr){
418 WARN("Getting _Devices property failed: %lx\n", sc);
419 HeapFree(GetProcessHeap(), 0, devices);
420 return osstatus_to_hresult(sc);
423 ndevices = devsize / sizeof(AudioDeviceID);
425 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
426 if(!*ids){
427 HeapFree(GetProcessHeap(), 0, devices);
428 return E_OUTOFMEMORY;
431 *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
432 if(!*ids){
433 HeapFree(GetProcessHeap(), 0, *ids);
434 HeapFree(GetProcessHeap(), 0, devices);
435 return E_OUTOFMEMORY;
438 *num = 0;
439 *def_index = (UINT)-1;
440 for(i = 0; i < ndevices; ++i){
441 AudioBufferList *buffers;
442 CFStringRef name;
443 SIZE_T len;
444 int j;
446 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
447 if(flow == eRender)
448 addr.mScope = kAudioDevicePropertyScopeOutput;
449 else
450 addr.mScope = kAudioDevicePropertyScopeInput;
451 addr.mElement = 0;
452 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
453 if(sc != noErr){
454 WARN("Unable to get _StreamConfiguration property size for "
455 "device %lu: %lx\n", devices[i], sc);
456 continue;
459 buffers = HeapAlloc(GetProcessHeap(), 0, size);
460 if(!buffers){
461 HeapFree(GetProcessHeap(), 0, devices);
462 for(j = 0; j < *num; ++j)
463 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
464 HeapFree(GetProcessHeap(), 0, *guids);
465 HeapFree(GetProcessHeap(), 0, *ids);
466 return E_OUTOFMEMORY;
469 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
470 &size, buffers);
471 if(sc != noErr){
472 WARN("Unable to get _StreamConfiguration property for "
473 "device %lu: %lx\n", devices[i], sc);
474 HeapFree(GetProcessHeap(), 0, buffers);
475 continue;
478 /* check that there's at least one channel in this device before
479 * we claim it as usable */
480 for(j = 0; j < buffers->mNumberBuffers; ++j)
481 if(buffers->mBuffers[j].mNumberChannels > 0)
482 break;
483 if(j >= buffers->mNumberBuffers){
484 HeapFree(GetProcessHeap(), 0, buffers);
485 continue;
488 HeapFree(GetProcessHeap(), 0, buffers);
490 size = sizeof(name);
491 addr.mSelector = kAudioObjectPropertyName;
492 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
493 &size, &name);
494 if(sc != noErr){
495 WARN("Unable to get _Name property for device %lu: %lx\n",
496 devices[i], sc);
497 continue;
500 len = CFStringGetLength(name) + 1;
501 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
502 if(!(*ids)[*num]){
503 CFRelease(name);
504 HeapFree(GetProcessHeap(), 0, devices);
505 for(j = 0; j < *num; ++j)
506 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
507 HeapFree(GetProcessHeap(), 0, *ids);
508 HeapFree(GetProcessHeap(), 0, *guids);
509 return E_OUTOFMEMORY;
511 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
512 ((*ids)[*num])[len - 1] = 0;
513 CFRelease(name);
515 get_device_guid(flow, devices[i], &(*guids)[*num]);
517 if(*def_index == (UINT)-1 && devices[i] == default_id)
518 *def_index = *num;
520 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
521 (unsigned int)devices[i], (*def_index == *num) ? " (default)" : "");
523 (*num)++;
526 if(*def_index == (UINT)-1)
527 *def_index = 0;
529 HeapFree(GetProcessHeap(), 0, devices);
531 return S_OK;
534 static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow)
536 HKEY devices_key;
537 UINT i = 0;
538 WCHAR key_name[256];
539 DWORD key_name_size;
541 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
542 ERR("No devices in registry?\n");
543 return FALSE;
546 while(1){
547 HKEY key;
548 DWORD size, type;
549 GUID reg_guid;
551 key_name_size = sizeof(key_name);
552 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
553 NULL, NULL, NULL) != ERROR_SUCCESS)
554 break;
556 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
557 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
558 continue;
561 size = sizeof(reg_guid);
562 if(RegQueryValueExW(key, guidW, 0, &type,
563 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
564 if(IsEqualGUID(&reg_guid, guid)){
565 RegCloseKey(key);
566 RegCloseKey(devices_key);
568 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
570 if(key_name[0] == '0')
571 *flow = eRender;
572 else if(key_name[0] == '1')
573 *flow = eCapture;
574 else{
575 ERR("Unknown device type: %c\n", key_name[0]);
576 return FALSE;
579 *id = strtoulW(key_name + 2, NULL, 10);
581 return TRUE;
585 RegCloseKey(key);
587 ++i;
590 RegCloseKey(devices_key);
592 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
594 return FALSE;
597 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
599 ACImpl *This;
600 AudioDeviceID adevid;
601 EDataFlow dataflow;
603 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
605 if(!get_deviceid_by_guid(guid, &adevid, &dataflow))
606 return AUDCLNT_E_DEVICE_INVALIDATED;
608 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
609 if(!This)
610 return E_OUTOFMEMORY;
612 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
613 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
614 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
615 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
616 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
617 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
619 This->dataflow = dataflow;
621 if(dataflow == eRender)
622 This->scope = kAudioDevicePropertyScopeOutput;
623 else if(dataflow == eCapture)
624 This->scope = kAudioDevicePropertyScopeInput;
625 else{
626 HeapFree(GetProcessHeap(), 0, This);
627 return E_INVALIDARG;
630 This->lock = 0;
632 This->parent = dev;
633 IMMDevice_AddRef(This->parent);
635 list_init(&This->avail_buffers);
636 list_init(&This->queued_buffers);
637 list_init(&This->queued_bufinfos);
639 This->adevid = adevid;
641 *out = &This->IAudioClient_iface;
642 IAudioClient_AddRef(&This->IAudioClient_iface);
644 return S_OK;
647 /* current position from start of stream */
648 #define BUFPOS_ABSOLUTE 1
649 /* current position from start of this buffer */
650 #define BUFPOS_RELATIVE 2
652 static UINT64 get_current_aqbuffer_position(ACImpl *This, int mode)
654 struct list *head;
655 QueuedBufInfo *bufinfo;
656 UINT64 ret;
658 head = list_head(&This->queued_bufinfos);
659 if(!head){
660 TRACE("No buffers queued\n");
661 if(mode == BUFPOS_ABSOLUTE)
662 return This->written_frames;
663 return 0;
665 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
667 if(This->playing == StatePlaying){
668 AudioTimeStamp tstamp;
669 OSStatus sc;
671 /* AudioQueueGetCurrentTime() is brain damaged. The returned
672 * mSampleTime member jumps backwards seemingly at random, so
673 * we record the highest sampletime and use that during these
674 * anomalies.
676 * It also behaves poorly when the queue is paused, jumping
677 * forwards during the pause and backwards again after resuming.
678 * So we record the sampletime when the queue is paused and use
679 * that. */
680 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
681 if(sc != noErr){
682 if(sc != kAudioQueueErr_InvalidRunState)
683 WARN("Unable to get current time: %lx\n", sc);
684 return 0;
687 if(!(tstamp.mFlags & kAudioTimeStampSampleTimeValid)){
688 FIXME("SampleTime not valid: %lx\n", tstamp.mFlags);
689 return 0;
692 if(tstamp.mSampleTime > This->highest_sampletime)
693 This->highest_sampletime = tstamp.mSampleTime;
696 while(This->highest_sampletime > bufinfo->start_sampletime + bufinfo->len_frames){
697 This->inbuf_frames -= bufinfo->len_frames;
698 list_remove(&bufinfo->entry);
699 HeapFree(GetProcessHeap(), 0, bufinfo);
701 head = list_head(&This->queued_bufinfos);
702 if(!head){
703 TRACE("No buffers queued\n");
704 if(mode == BUFPOS_ABSOLUTE)
705 return This->written_frames;
706 return 0;
708 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
711 if(This->highest_sampletime < bufinfo->start_sampletime)
712 ret = 0;
713 else
714 ret = This->highest_sampletime - bufinfo->start_sampletime;
716 if(mode == BUFPOS_ABSOLUTE){
717 ret = This->written_frames - (bufinfo->len_frames - ret);
718 while((head = list_next(&This->queued_bufinfos, &bufinfo->entry))){
719 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
720 ret -= bufinfo->len_frames;
724 TRACE("%llu frames (%s)\n", ret,
725 mode == BUFPOS_ABSOLUTE ? "absolute" : "relative");
727 return ret;
730 static void avail_update(ACImpl *This)
732 AQBuffer *buf, *next;
734 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
735 if(buf->used)
736 break;
737 if(This->dataflow == eCapture)
738 This->inbuf_frames += buf->buf->mAudioDataByteSize / This->fmt->nBlockAlign;
739 list_remove(&buf->entry);
740 list_add_tail(&This->avail_buffers, &buf->entry);
744 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
745 REFIID riid, void **ppv)
747 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
749 if(!ppv)
750 return E_POINTER;
751 *ppv = NULL;
752 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
753 *ppv = iface;
754 if(*ppv){
755 IUnknown_AddRef((IUnknown*)*ppv);
756 return S_OK;
758 WARN("Unknown interface %s\n", debugstr_guid(riid));
759 return E_NOINTERFACE;
762 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
764 ACImpl *This = impl_from_IAudioClient(iface);
765 ULONG ref;
766 ref = InterlockedIncrement(&This->ref);
767 TRACE("(%p) Refcount now %u\n", This, ref);
768 return ref;
771 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
773 ACImpl *This = impl_from_IAudioClient(iface);
774 ULONG ref;
775 ref = InterlockedDecrement(&This->ref);
776 TRACE("(%p) Refcount now %u\n", This, ref);
777 if(!ref){
778 if(This->aqueue){
779 AQBuffer *buf, *next;
780 QueuedBufInfo *bufinfo, *bufinfo2;
782 if(This->public_buffer){
783 buf = This->public_buffer->mUserData;
784 list_add_tail(&This->avail_buffers, &buf->entry);
787 IAudioClient_Stop(iface);
788 AudioQueueStop(This->aqueue, 1);
790 /* Stopped synchronously, all buffers returned. */
791 list_move_tail(&This->avail_buffers, &This->queued_buffers);
792 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->avail_buffers, AQBuffer, entry){
793 AudioQueueFreeBuffer(This->aqueue, buf->buf);
794 HeapFree(GetProcessHeap(), 0, buf);
797 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
798 QueuedBufInfo, entry)
799 HeapFree(GetProcessHeap(), 0, bufinfo);
801 AudioQueueDispose(This->aqueue, 1);
803 if(This->session){
804 EnterCriticalSection(&g_sessions_lock);
805 list_remove(&This->entry);
806 LeaveCriticalSection(&g_sessions_lock);
808 HeapFree(GetProcessHeap(), 0, This->vols);
809 CoTaskMemFree(This->fmt);
810 IMMDevice_Release(This->parent);
811 HeapFree(GetProcessHeap(), 0, This);
813 return ref;
816 static void dump_fmt(const WAVEFORMATEX *fmt)
818 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
819 switch(fmt->wFormatTag){
820 case WAVE_FORMAT_PCM:
821 TRACE("WAVE_FORMAT_PCM");
822 break;
823 case WAVE_FORMAT_IEEE_FLOAT:
824 TRACE("WAVE_FORMAT_IEEE_FLOAT");
825 break;
826 case WAVE_FORMAT_EXTENSIBLE:
827 TRACE("WAVE_FORMAT_EXTENSIBLE");
828 break;
829 default:
830 TRACE("Unknown");
831 break;
833 TRACE(")\n");
835 TRACE("nChannels: %u\n", fmt->nChannels);
836 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
837 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
838 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
839 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
840 TRACE("cbSize: %u\n", fmt->cbSize);
842 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
843 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
844 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
845 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
846 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
850 static DWORD get_channel_mask(unsigned int channels)
852 switch(channels){
853 case 0:
854 return 0;
855 case 1:
856 return KSAUDIO_SPEAKER_MONO;
857 case 2:
858 return KSAUDIO_SPEAKER_STEREO;
859 case 3:
860 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
861 case 4:
862 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
863 case 5:
864 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
865 case 6:
866 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
867 case 7:
868 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
869 case 8:
870 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
872 FIXME("Unknown speaker configuration: %u\n", channels);
873 return 0;
876 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
878 WAVEFORMATEX *ret;
879 size_t size;
881 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
882 size = sizeof(WAVEFORMATEXTENSIBLE);
883 else
884 size = sizeof(WAVEFORMATEX);
886 ret = CoTaskMemAlloc(size);
887 if(!ret)
888 return NULL;
890 memcpy(ret, fmt, size);
892 ret->cbSize = size - sizeof(WAVEFORMATEX);
894 return ret;
897 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
898 const WAVEFORMATEX *fmt)
900 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
902 desc->mFormatFlags = 0;
904 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
905 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
906 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
907 desc->mFormatID = kAudioFormatLinearPCM;
908 if(fmt->wBitsPerSample > 8)
909 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
910 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
911 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
912 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
913 desc->mFormatID = kAudioFormatLinearPCM;
914 desc->mFormatFlags = kAudioFormatFlagIsFloat;
915 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
916 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
917 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
918 desc->mFormatID = kAudioFormatULaw;
919 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
920 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
921 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
922 desc->mFormatID = kAudioFormatALaw;
923 }else
924 return AUDCLNT_E_UNSUPPORTED_FORMAT;
926 desc->mSampleRate = fmt->nSamplesPerSec;
927 desc->mBytesPerPacket = fmt->nBlockAlign;
928 desc->mFramesPerPacket = 1;
929 desc->mBytesPerFrame = fmt->nBlockAlign;
930 desc->mChannelsPerFrame = fmt->nChannels;
931 desc->mBitsPerChannel = fmt->wBitsPerSample;
932 desc->mReserved = 0;
934 return S_OK;
937 /* We can't use debug printing or {Enter,Leave}CriticalSection from
938 * OSX callback threads. We may use OSSpinLock.
939 * OSSpinLock is not a recursive lock, so don't call
940 * synchronized functions while holding the lock. */
941 static void ca_out_buffer_cb(void *user, AudioQueueRef aqueue,
942 AudioQueueBufferRef buffer)
944 AQBuffer *buf = buffer->mUserData;
946 buf->used = FALSE;
949 static void ca_in_buffer_cb(void *user, AudioQueueRef aqueue,
950 AudioQueueBufferRef buffer, const AudioTimeStamp *start,
951 UInt32 ndesc, const AudioStreamPacketDescription *descs)
953 AQBuffer *buf = buffer->mUserData;
955 buf->used = FALSE;
956 /* let's update inbuf_frames synchronously without OSAddAtomic */
959 static HRESULT ca_setup_aqueue(AudioDeviceID did, EDataFlow flow,
960 const WAVEFORMATEX *fmt, void *user, AudioQueueRef *aqueue)
962 AudioStreamBasicDescription desc;
963 AudioObjectPropertyAddress addr;
964 CFStringRef uid;
965 OSStatus sc;
966 HRESULT hr;
967 UInt32 size;
969 addr.mScope = kAudioObjectPropertyScopeGlobal;
970 addr.mElement = 0;
971 addr.mSelector = kAudioDevicePropertyDeviceUID;
973 size = sizeof(uid);
974 sc = AudioObjectGetPropertyData(did, &addr, 0, NULL, &size, &uid);
975 if(sc != noErr){
976 WARN("Unable to get _DeviceUID property: %lx\n", sc);
977 return osstatus_to_hresult(sc);
980 hr = ca_get_audiodesc(&desc, fmt);
981 if(FAILED(hr)){
982 CFRelease(uid);
983 return hr;
986 if(flow == eRender)
987 sc = AudioQueueNewOutput(&desc, ca_out_buffer_cb, user, NULL, NULL, 0,
988 aqueue);
989 else if(flow == eCapture)
990 sc = AudioQueueNewInput(&desc, ca_in_buffer_cb, user, NULL, NULL, 0,
991 aqueue);
992 else{
993 CFRelease(uid);
994 return E_UNEXPECTED;
996 if(sc != noErr){
997 WARN("Unable to create AudioQueue: %lx\n", sc);
998 CFRelease(uid);
999 return osstatus_to_hresult(sc);
1002 sc = AudioQueueSetProperty(*aqueue, kAudioQueueProperty_CurrentDevice,
1003 &uid, sizeof(uid));
1004 if(sc != noErr){
1005 WARN("Unable to change AQueue device: %lx\n", sc);
1006 CFRelease(uid);
1007 return osstatus_to_hresult(sc);
1010 CFRelease(uid);
1012 return S_OK;
1015 static void session_init_vols(AudioSession *session, UINT channels)
1017 if(session->channel_count < channels){
1018 UINT i;
1020 if(session->channel_vols)
1021 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1022 session->channel_vols, sizeof(float) * channels);
1023 else
1024 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1025 sizeof(float) * channels);
1026 if(!session->channel_vols)
1027 return;
1029 for(i = session->channel_count; i < channels; ++i)
1030 session->channel_vols[i] = 1.f;
1032 session->channel_count = channels;
1036 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1037 UINT num_channels)
1039 AudioSession *ret;
1041 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1042 if(!ret)
1043 return NULL;
1045 memcpy(&ret->guid, guid, sizeof(GUID));
1047 ret->device = device;
1049 list_init(&ret->clients);
1051 list_add_head(&g_sessions, &ret->entry);
1053 InitializeCriticalSection(&ret->lock);
1054 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1056 session_init_vols(ret, num_channels);
1058 ret->master_vol = 1.f;
1060 return ret;
1063 /* if channels == 0, then this will return or create a session with
1064 * matching dataflow and GUID. otherwise, channels must also match */
1065 static HRESULT get_audio_session(const GUID *sessionguid,
1066 IMMDevice *device, UINT channels, AudioSession **out)
1068 AudioSession *session;
1070 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1071 *out = create_session(&GUID_NULL, device, channels);
1072 if(!*out)
1073 return E_OUTOFMEMORY;
1075 return S_OK;
1078 *out = NULL;
1079 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1080 if(session->device == device &&
1081 IsEqualGUID(sessionguid, &session->guid)){
1082 session_init_vols(session, channels);
1083 *out = session;
1084 break;
1088 if(!*out){
1089 *out = create_session(sessionguid, device, channels);
1090 if(!*out)
1091 return E_OUTOFMEMORY;
1094 return S_OK;
1097 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1098 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1099 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1100 const GUID *sessionguid)
1102 ACImpl *This = impl_from_IAudioClient(iface);
1103 HRESULT hr;
1104 OSStatus sc;
1105 int i;
1107 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1108 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1110 if(!fmt)
1111 return E_POINTER;
1113 dump_fmt(fmt);
1115 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1116 return AUDCLNT_E_NOT_INITIALIZED;
1118 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1119 AUDCLNT_STREAMFLAGS_LOOPBACK |
1120 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1121 AUDCLNT_STREAMFLAGS_NOPERSIST |
1122 AUDCLNT_STREAMFLAGS_RATEADJUST |
1123 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1124 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1125 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1126 TRACE("Unknown flags: %08x\n", flags);
1127 return E_INVALIDARG;
1130 if(mode == AUDCLNT_SHAREMODE_SHARED){
1131 period = DefaultPeriod;
1132 if( duration < 3 * period)
1133 duration = 3 * period;
1134 }else{
1135 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1136 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1137 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1138 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1141 if(!period)
1142 period = DefaultPeriod; /* not minimum */
1143 if(period < MinimumPeriod || period > 5000000)
1144 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1145 if(duration > 20000000) /* the smaller the period, the lower this limit */
1146 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1147 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1148 if(duration != period)
1149 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1150 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1151 return AUDCLNT_E_DEVICE_IN_USE;
1152 }else{
1153 if( duration < 8 * period)
1154 duration = 8 * period; /* may grow above 2s */
1158 OSSpinLockLock(&This->lock);
1160 if(This->aqueue){
1161 OSSpinLockUnlock(&This->lock);
1162 return AUDCLNT_E_ALREADY_INITIALIZED;
1165 hr = ca_setup_aqueue(This->adevid, This->dataflow, fmt, This, &This->aqueue);
1166 if(FAILED(hr)){
1167 OSSpinLockUnlock(&This->lock);
1168 return hr;
1171 This->fmt = clone_format(fmt);
1172 if(!This->fmt){
1173 AudioQueueDispose(This->aqueue, 1);
1174 This->aqueue = NULL;
1175 OSSpinLockUnlock(&This->lock);
1176 return E_OUTOFMEMORY;
1179 This->period_ms = period / 10000;
1181 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1183 if(This->dataflow == eCapture){
1184 int i;
1185 UInt32 bsize = ceil((This->bufsize_frames / (double)CAPTURE_BUFFERS) *
1186 This->fmt->nBlockAlign);
1187 for(i = 0; i < CAPTURE_BUFFERS; ++i){
1188 AQBuffer *buf;
1190 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1191 if(!buf){
1192 AudioQueueDispose(This->aqueue, 1);
1193 This->aqueue = NULL;
1194 CoTaskMemFree(This->fmt);
1195 This->fmt = NULL;
1196 OSSpinLockUnlock(&This->lock);
1197 return E_OUTOFMEMORY;
1200 sc = AudioQueueAllocateBuffer(This->aqueue, bsize, &buf->buf);
1201 if(sc != noErr){
1202 AudioQueueDispose(This->aqueue, 1);
1203 This->aqueue = NULL;
1204 CoTaskMemFree(This->fmt);
1205 This->fmt = NULL;
1206 OSSpinLockUnlock(&This->lock);
1207 WARN("Couldn't allocate buffer: %lx\n", sc);
1208 return osstatus_to_hresult(sc);
1211 buf->buf->mUserData = buf;
1212 buf->used = TRUE;
1213 sc = AudioQueueEnqueueBuffer(This->aqueue, buf->buf, 0, NULL);
1214 if(sc != noErr){
1215 ERR("Couldn't enqueue buffer: %lx\n", sc);
1216 break;
1218 list_add_tail(&This->queued_buffers, &buf->entry);
1222 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1223 if(!This->vols){
1224 AudioQueueDispose(This->aqueue, 1);
1225 This->aqueue = NULL;
1226 CoTaskMemFree(This->fmt);
1227 This->fmt = NULL;
1228 OSSpinLockUnlock(&This->lock);
1229 return E_OUTOFMEMORY;
1232 for(i = 0; i < fmt->nChannels; ++i)
1233 This->vols[i] = 1.f;
1235 This->share = mode;
1236 This->flags = flags;
1238 EnterCriticalSection(&g_sessions_lock);
1240 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1241 &This->session);
1242 if(FAILED(hr)){
1243 LeaveCriticalSection(&g_sessions_lock);
1244 AudioQueueDispose(This->aqueue, 1);
1245 This->aqueue = NULL;
1246 CoTaskMemFree(This->fmt);
1247 This->fmt = NULL;
1248 HeapFree(GetProcessHeap(), 0, This->vols);
1249 This->vols = NULL;
1250 OSSpinLockUnlock(&This->lock);
1251 return E_INVALIDARG;
1254 list_add_tail(&This->session->clients, &This->entry);
1256 LeaveCriticalSection(&g_sessions_lock);
1258 ca_setvol(This, -1);
1260 OSSpinLockUnlock(&This->lock);
1262 return S_OK;
1265 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1266 UINT32 *frames)
1268 ACImpl *This = impl_from_IAudioClient(iface);
1270 TRACE("(%p)->(%p)\n", This, frames);
1272 if(!frames)
1273 return E_POINTER;
1275 OSSpinLockLock(&This->lock);
1277 if(!This->aqueue){
1278 OSSpinLockUnlock(&This->lock);
1279 return AUDCLNT_E_NOT_INITIALIZED;
1282 *frames = This->bufsize_frames;
1284 OSSpinLockUnlock(&This->lock);
1286 return S_OK;
1289 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1291 AudioObjectPropertyAddress addr;
1292 AudioStreamID *ids;
1293 UInt32 size;
1294 OSStatus sc;
1295 int nstreams, i;
1297 addr.mScope = This->scope;
1298 addr.mElement = 0;
1299 addr.mSelector = kAudioDevicePropertyStreams;
1301 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1302 &size);
1303 if(sc != noErr){
1304 WARN("Unable to get size for _Streams property: %lx\n", sc);
1305 return osstatus_to_hresult(sc);
1308 ids = HeapAlloc(GetProcessHeap(), 0, size);
1309 if(!ids)
1310 return E_OUTOFMEMORY;
1312 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1313 if(sc != noErr){
1314 WARN("Unable to get _Streams property: %lx\n", sc);
1315 HeapFree(GetProcessHeap(), 0, ids);
1316 return osstatus_to_hresult(sc);
1319 nstreams = size / sizeof(AudioStreamID);
1320 *max = 0;
1322 addr.mSelector = kAudioStreamPropertyLatency;
1323 for(i = 0; i < nstreams; ++i){
1324 UInt32 latency;
1326 size = sizeof(latency);
1327 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1328 &size, &latency);
1329 if(sc != noErr){
1330 WARN("Unable to get _Latency property: %lx\n", sc);
1331 continue;
1334 if(latency > *max)
1335 *max = latency;
1338 HeapFree(GetProcessHeap(), 0, ids);
1340 return S_OK;
1343 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1344 REFERENCE_TIME *out)
1346 ACImpl *This = impl_from_IAudioClient(iface);
1347 UInt32 latency, stream_latency, size;
1348 AudioObjectPropertyAddress addr;
1349 OSStatus sc;
1350 HRESULT hr;
1352 TRACE("(%p)->(%p)\n", This, out);
1354 if(!out)
1355 return E_POINTER;
1357 OSSpinLockLock(&This->lock);
1359 if(!This->aqueue){
1360 OSSpinLockUnlock(&This->lock);
1361 return AUDCLNT_E_NOT_INITIALIZED;
1364 addr.mScope = This->scope;
1365 addr.mSelector = kAudioDevicePropertyLatency;
1366 addr.mElement = 0;
1368 size = sizeof(latency);
1369 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1370 &size, &latency);
1371 if(sc != noErr){
1372 WARN("Couldn't get _Latency property: %lx\n", sc);
1373 OSSpinLockUnlock(&This->lock);
1374 return osstatus_to_hresult(sc);
1377 hr = ca_get_max_stream_latency(This, &stream_latency);
1378 if(FAILED(hr)){
1379 OSSpinLockUnlock(&This->lock);
1380 return hr;
1383 latency += stream_latency;
1384 /* pretend we process audio in Period chunks, so max latency includes
1385 * the period time */
1386 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1387 + This->period_ms * 10000;
1389 OSSpinLockUnlock(&This->lock);
1391 return S_OK;
1394 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1395 UINT32 *numpad)
1397 if(!This->aqueue)
1398 return AUDCLNT_E_NOT_INITIALIZED;
1400 avail_update(This);
1402 if(This->dataflow == eRender){
1403 UINT64 bufpos;
1404 bufpos = get_current_aqbuffer_position(This, BUFPOS_RELATIVE);
1405 *numpad = This->inbuf_frames - bufpos;
1406 }else
1407 *numpad = This->inbuf_frames;
1409 return S_OK;
1412 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1413 UINT32 *numpad)
1415 ACImpl *This = impl_from_IAudioClient(iface);
1416 HRESULT hr;
1418 TRACE("(%p)->(%p)\n", This, numpad);
1420 if(!numpad)
1421 return E_POINTER;
1423 OSSpinLockLock(&This->lock);
1425 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1427 OSSpinLockUnlock(&This->lock);
1429 return hr;
1432 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1433 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1434 WAVEFORMATEX **outpwfx)
1436 ACImpl *This = impl_from_IAudioClient(iface);
1437 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1438 AudioQueueRef aqueue;
1439 HRESULT hr;
1441 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1443 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1444 return E_POINTER;
1446 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1447 return E_INVALIDARG;
1449 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1450 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1451 return E_INVALIDARG;
1453 dump_fmt(pwfx);
1455 if(outpwfx){
1456 *outpwfx = NULL;
1457 if(mode != AUDCLNT_SHAREMODE_SHARED)
1458 outpwfx = NULL;
1461 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1462 if(pwfx->nAvgBytesPerSec == 0 ||
1463 pwfx->nBlockAlign == 0 ||
1464 fmtex->Samples.wValidBitsPerSample > pwfx->wBitsPerSample)
1465 return E_INVALIDARG;
1466 if(fmtex->Samples.wValidBitsPerSample < pwfx->wBitsPerSample)
1467 goto unsupported;
1468 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1469 if(fmtex->dwChannelMask == 0 ||
1470 fmtex->dwChannelMask & SPEAKER_RESERVED)
1471 goto unsupported;
1475 if(pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample / 8 ||
1476 pwfx->nAvgBytesPerSec != pwfx->nBlockAlign * pwfx->nSamplesPerSec)
1477 goto unsupported;
1479 if(pwfx->nChannels == 0)
1480 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1482 OSSpinLockLock(&This->lock);
1484 hr = ca_setup_aqueue(This->adevid, This->dataflow, pwfx, NULL, &aqueue);
1485 if(SUCCEEDED(hr)){
1486 AudioQueueDispose(aqueue, 1);
1487 OSSpinLockUnlock(&This->lock);
1488 TRACE("returning %08x\n", S_OK);
1489 return S_OK;
1491 OSSpinLockUnlock(&This->lock);
1492 if(hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
1493 TRACE("returning %08x\n", hr);
1494 return hr;
1497 unsupported:
1498 if(outpwfx){
1499 hr = IAudioClient_GetMixFormat(&This->IAudioClient_iface, outpwfx);
1500 if(FAILED(hr))
1501 return hr;
1502 return S_FALSE;
1505 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1508 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1509 WAVEFORMATEX **pwfx)
1511 ACImpl *This = impl_from_IAudioClient(iface);
1512 WAVEFORMATEXTENSIBLE *fmt;
1513 OSStatus sc;
1514 UInt32 size;
1515 Float64 rate;
1516 AudioBufferList *buffers;
1517 AudioObjectPropertyAddress addr;
1518 int i;
1520 TRACE("(%p)->(%p)\n", This, pwfx);
1522 if(!pwfx)
1523 return E_POINTER;
1524 *pwfx = NULL;
1526 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1527 if(!fmt)
1528 return E_OUTOFMEMORY;
1530 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1532 addr.mScope = This->scope;
1533 addr.mElement = 0;
1534 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1536 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1537 if(sc != noErr){
1538 CoTaskMemFree(fmt);
1539 WARN("Unable to get size for _StreamConfiguration property: %lx\n", sc);
1540 return osstatus_to_hresult(sc);
1543 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1544 if(!buffers){
1545 CoTaskMemFree(fmt);
1546 return E_OUTOFMEMORY;
1549 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1550 &size, buffers);
1551 if(sc != noErr){
1552 CoTaskMemFree(fmt);
1553 HeapFree(GetProcessHeap(), 0, buffers);
1554 WARN("Unable to get _StreamConfiguration property: %lx\n", sc);
1555 return osstatus_to_hresult(sc);
1558 fmt->Format.nChannels = 0;
1559 for(i = 0; i < buffers->mNumberBuffers; ++i)
1560 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1562 HeapFree(GetProcessHeap(), 0, buffers);
1564 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1566 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1567 size = sizeof(Float64);
1568 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1569 if(sc != noErr){
1570 CoTaskMemFree(fmt);
1571 WARN("Unable to get _NominalSampleRate property: %lx\n", sc);
1572 return osstatus_to_hresult(sc);
1574 fmt->Format.nSamplesPerSec = rate;
1576 fmt->Format.wBitsPerSample = 32;
1577 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1579 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1580 fmt->Format.nChannels) / 8;
1581 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1582 fmt->Format.nBlockAlign;
1584 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1585 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1587 *pwfx = (WAVEFORMATEX*)fmt;
1588 dump_fmt(*pwfx);
1590 return S_OK;
1593 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1594 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1596 ACImpl *This = impl_from_IAudioClient(iface);
1598 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1600 if(!defperiod && !minperiod)
1601 return E_POINTER;
1603 if(defperiod)
1604 *defperiod = DefaultPeriod;
1605 if(minperiod)
1606 *minperiod = MinimumPeriod;
1608 return S_OK;
1611 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1613 ACImpl *This = user;
1615 if(This->event)
1616 SetEvent(This->event);
1619 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1621 ACImpl *This = impl_from_IAudioClient(iface);
1622 OSStatus sc;
1624 TRACE("(%p)\n", This);
1626 OSSpinLockLock(&This->lock);
1628 if(!This->aqueue){
1629 OSSpinLockUnlock(&This->lock);
1630 return AUDCLNT_E_NOT_INITIALIZED;
1633 if(This->playing != StateStopped){
1634 OSSpinLockUnlock(&This->lock);
1635 return AUDCLNT_E_NOT_STOPPED;
1638 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1639 OSSpinLockUnlock(&This->lock);
1640 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1643 if(This->event)
1644 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
1645 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
1646 This->timer = NULL;
1647 OSSpinLockUnlock(&This->lock);
1648 WARN("Unable to create timer: %u\n", GetLastError());
1649 return E_OUTOFMEMORY;
1652 if(This->dataflow == eCapture){
1653 UINT32 frames; /* enqueue packets */
1654 AudioCaptureClient_GetNextPacket(This, &frames);
1657 This->playing = StateInTransition;
1659 sc = AudioQueueStart(This->aqueue, NULL);
1660 if(sc != noErr){
1661 OSSpinLockUnlock(&This->lock);
1662 WARN("Unable to start audio queue: %lx\n", sc);
1663 return osstatus_to_hresult(sc);
1666 This->playing = StatePlaying;
1668 OSSpinLockUnlock(&This->lock);
1670 return S_OK;
1673 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1675 ACImpl *This = impl_from_IAudioClient(iface);
1676 AudioTimeStamp tstamp;
1677 OSStatus sc;
1678 HANDLE event = NULL;
1679 BOOL wait = FALSE;
1681 TRACE("(%p)\n", This);
1683 OSSpinLockLock(&This->lock);
1685 if(!This->aqueue){
1686 OSSpinLockUnlock(&This->lock);
1687 return AUDCLNT_E_NOT_INITIALIZED;
1690 if(This->playing == StateStopped){
1691 OSSpinLockUnlock(&This->lock);
1692 return S_FALSE;
1695 if(This->playing == StateInTransition){
1696 OSSpinLockUnlock(&This->lock);
1697 return S_OK;
1700 if(This->timer){
1701 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1702 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1703 This->timer = NULL;
1704 if(wait)
1705 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1706 wait = wait && GetLastError() == ERROR_IO_PENDING;
1709 This->playing = StateInTransition;
1711 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
1712 if(sc == noErr){
1713 if(tstamp.mFlags & kAudioTimeStampSampleTimeValid){
1714 if(tstamp.mSampleTime > This->highest_sampletime)
1715 This->highest_sampletime = tstamp.mSampleTime;
1716 }else
1717 WARN("Returned tstamp mSampleTime not valid: %lx\n", tstamp.mFlags);
1718 }else
1719 WARN("GetCurrentTime failed: %lx\n", sc);
1721 /* Mac OS bug? Our capture callback is no more called past AQStop */
1722 sc = AudioQueuePause(This->aqueue);
1723 if(sc != noErr){
1724 OSSpinLockUnlock(&This->lock);
1725 WARN("Unable to pause audio queue: %lx\n", sc);
1726 return osstatus_to_hresult(sc);
1729 This->playing = StateStopped;
1731 OSSpinLockUnlock(&This->lock);
1733 if(event && wait)
1734 WaitForSingleObject(event, INFINITE);
1735 CloseHandle(event);
1737 return S_OK;
1740 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1742 ACImpl *This = impl_from_IAudioClient(iface);
1743 OSStatus sc;
1744 QueuedBufInfo *bufinfo, *bufinfo2;
1745 AQBuffer *buf;
1747 TRACE("(%p)\n", This);
1749 OSSpinLockLock(&This->lock);
1751 if(!This->aqueue){
1752 OSSpinLockUnlock(&This->lock);
1753 return AUDCLNT_E_NOT_INITIALIZED;
1756 if(This->playing != StateStopped){
1757 OSSpinLockUnlock(&This->lock);
1758 return AUDCLNT_E_NOT_STOPPED;
1761 if(This->getbuf_last){
1762 OSSpinLockUnlock(&This->lock);
1763 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1766 avail_update(This); /* going to skip over inbuf_frames */
1768 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
1769 QueuedBufInfo, entry){
1770 list_remove(&bufinfo->entry);
1771 HeapFree(GetProcessHeap(), 0, bufinfo);
1774 sc = AudioQueueReset(This->aqueue);
1775 if(sc != noErr){
1776 OSSpinLockUnlock(&This->lock);
1777 WARN("Unable to reset audio queue: %lx\n", sc);
1778 return osstatus_to_hresult(sc);
1781 /* AQReset is synchronous */
1782 list_move_tail(&This->avail_buffers, &This->queued_buffers);
1784 if(This->dataflow == eRender){
1785 This->written_frames = 0;
1786 }else{
1787 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry)
1788 buf->buf->mAudioDataByteSize = 0;
1789 This->written_frames += This->inbuf_frames;
1791 This->inbuf_frames = 0;
1793 OSSpinLockUnlock(&This->lock);
1795 return S_OK;
1798 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1799 HANDLE event)
1801 ACImpl *This = impl_from_IAudioClient(iface);
1803 TRACE("(%p)->(%p)\n", This, event);
1805 if(!event)
1806 return E_INVALIDARG;
1808 OSSpinLockLock(&This->lock);
1810 if(!This->aqueue){
1811 OSSpinLockUnlock(&This->lock);
1812 return AUDCLNT_E_NOT_INITIALIZED;
1815 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1816 OSSpinLockUnlock(&This->lock);
1817 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1820 if (This->event){
1821 OSSpinLockUnlock(&This->lock);
1822 FIXME("called twice\n");
1823 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1826 This->event = event;
1828 OSSpinLockUnlock(&This->lock);
1830 return S_OK;
1833 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1834 void **ppv)
1836 ACImpl *This = impl_from_IAudioClient(iface);
1838 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1840 if(!ppv)
1841 return E_POINTER;
1842 *ppv = NULL;
1844 OSSpinLockLock(&This->lock);
1846 if(!This->aqueue){
1847 OSSpinLockUnlock(&This->lock);
1848 return AUDCLNT_E_NOT_INITIALIZED;
1851 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1852 if(This->dataflow != eRender){
1853 OSSpinLockUnlock(&This->lock);
1854 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1856 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1857 *ppv = &This->IAudioRenderClient_iface;
1858 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1859 if(This->dataflow != eCapture){
1860 OSSpinLockUnlock(&This->lock);
1861 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1863 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1864 *ppv = &This->IAudioCaptureClient_iface;
1865 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1866 IAudioClock_AddRef(&This->IAudioClock_iface);
1867 *ppv = &This->IAudioClock_iface;
1868 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1869 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1870 *ppv = &This->IAudioStreamVolume_iface;
1871 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1872 if(!This->session_wrapper){
1873 This->session_wrapper = AudioSessionWrapper_Create(This);
1874 if(!This->session_wrapper){
1875 OSSpinLockUnlock(&This->lock);
1876 return E_OUTOFMEMORY;
1878 }else
1879 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1881 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1882 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1883 if(!This->session_wrapper){
1884 This->session_wrapper = AudioSessionWrapper_Create(This);
1885 if(!This->session_wrapper){
1886 OSSpinLockUnlock(&This->lock);
1887 return E_OUTOFMEMORY;
1889 }else
1890 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1892 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1893 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1894 if(!This->session_wrapper){
1895 This->session_wrapper = AudioSessionWrapper_Create(This);
1896 if(!This->session_wrapper){
1897 OSSpinLockUnlock(&This->lock);
1898 return E_OUTOFMEMORY;
1900 }else
1901 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1903 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1906 if(*ppv){
1907 OSSpinLockUnlock(&This->lock);
1908 return S_OK;
1911 OSSpinLockUnlock(&This->lock);
1913 FIXME("stub %s\n", debugstr_guid(riid));
1914 return E_NOINTERFACE;
1917 static const IAudioClientVtbl AudioClient_Vtbl =
1919 AudioClient_QueryInterface,
1920 AudioClient_AddRef,
1921 AudioClient_Release,
1922 AudioClient_Initialize,
1923 AudioClient_GetBufferSize,
1924 AudioClient_GetStreamLatency,
1925 AudioClient_GetCurrentPadding,
1926 AudioClient_IsFormatSupported,
1927 AudioClient_GetMixFormat,
1928 AudioClient_GetDevicePeriod,
1929 AudioClient_Start,
1930 AudioClient_Stop,
1931 AudioClient_Reset,
1932 AudioClient_SetEventHandle,
1933 AudioClient_GetService
1936 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1937 IAudioRenderClient *iface, REFIID riid, void **ppv)
1939 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1941 if(!ppv)
1942 return E_POINTER;
1943 *ppv = NULL;
1945 if(IsEqualIID(riid, &IID_IUnknown) ||
1946 IsEqualIID(riid, &IID_IAudioRenderClient))
1947 *ppv = iface;
1948 if(*ppv){
1949 IUnknown_AddRef((IUnknown*)*ppv);
1950 return S_OK;
1953 WARN("Unknown interface %s\n", debugstr_guid(riid));
1954 return E_NOINTERFACE;
1957 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1959 ACImpl *This = impl_from_IAudioRenderClient(iface);
1960 return AudioClient_AddRef(&This->IAudioClient_iface);
1963 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1965 ACImpl *This = impl_from_IAudioRenderClient(iface);
1966 return AudioClient_Release(&This->IAudioClient_iface);
1969 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1970 UINT32 frames, BYTE **data)
1972 ACImpl *This = impl_from_IAudioRenderClient(iface);
1973 AQBuffer *buf;
1974 UINT32 pad, bytes;
1975 HRESULT hr;
1976 OSStatus sc;
1978 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1980 if(!data)
1981 return E_POINTER;
1982 *data = NULL;
1984 OSSpinLockLock(&This->lock);
1986 if(This->getbuf_last){
1987 OSSpinLockUnlock(&This->lock);
1988 return AUDCLNT_E_OUT_OF_ORDER;
1991 if(!frames){
1992 OSSpinLockUnlock(&This->lock);
1993 return S_OK;
1996 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
1997 if(FAILED(hr)){
1998 OSSpinLockUnlock(&This->lock);
1999 return hr;
2002 if(pad + frames > This->bufsize_frames){
2003 OSSpinLockUnlock(&This->lock);
2004 return AUDCLNT_E_BUFFER_TOO_LARGE;
2007 bytes = frames * This->fmt->nBlockAlign;
2008 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry){
2009 if(buf->buf->mAudioDataBytesCapacity >= bytes){
2010 This->public_buffer = buf->buf;
2011 list_remove(&buf->entry);
2012 break;
2016 if(&buf->entry == &This->avail_buffers){
2017 sc = AudioQueueAllocateBuffer(This->aqueue, bytes,
2018 &This->public_buffer);
2019 if(sc != noErr){
2020 This->public_buffer = NULL;
2021 OSSpinLockUnlock(&This->lock);
2022 WARN("Unable to allocate buffer: %lx\n", sc);
2023 return E_OUTOFMEMORY;
2025 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
2026 if(!buf){
2027 AudioQueueFreeBuffer(This->aqueue, This->public_buffer);
2028 This->public_buffer = NULL;
2029 OSSpinLockUnlock(&This->lock);
2030 return E_OUTOFMEMORY;
2032 buf->used = FALSE;
2033 buf->buf = This->public_buffer;
2034 This->public_buffer->mUserData = buf;
2037 This->getbuf_last = frames;
2038 *data = This->public_buffer->mAudioData;
2040 OSSpinLockUnlock(&This->lock);
2042 return S_OK;
2045 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2046 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
2048 ACImpl *This = impl_from_IAudioRenderClient(iface);
2049 AQBuffer *buf;
2050 AudioTimeStamp start_time, req_time = {0}, *passed_time = NULL;
2051 OSStatus sc;
2053 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
2055 OSSpinLockLock(&This->lock);
2057 if(!frames){
2058 This->getbuf_last = 0;
2059 if(This->public_buffer){
2060 buf = This->public_buffer->mUserData;
2061 list_add_head(&This->avail_buffers, &buf->entry);
2062 This->public_buffer = NULL;
2064 OSSpinLockUnlock(&This->lock);
2065 return S_OK;
2068 if(!This->getbuf_last){
2069 OSSpinLockUnlock(&This->lock);
2070 return AUDCLNT_E_OUT_OF_ORDER;
2073 if(frames > This->getbuf_last){
2074 OSSpinLockUnlock(&This->lock);
2075 return AUDCLNT_E_INVALID_SIZE;
2078 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2079 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
2080 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
2081 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
2082 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
2083 This->fmt->wBitsPerSample == 8)
2084 memset(This->public_buffer->mAudioData, 128,
2085 frames * This->fmt->nBlockAlign);
2086 else
2087 memset(This->public_buffer->mAudioData, 0,
2088 frames * This->fmt->nBlockAlign);
2091 This->public_buffer->mAudioDataByteSize = frames * This->fmt->nBlockAlign;
2093 buf = This->public_buffer->mUserData;
2094 buf->used = TRUE;
2096 if(list_empty(&This->queued_bufinfos)){
2097 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &req_time, NULL);
2098 if(sc == noErr)
2099 passed_time = &req_time;
2100 else
2101 TRACE("AudioQueueGetCurrentTime failed: %lx\n", sc);
2102 }else{
2103 req_time.mSampleTime = This->next_sampletime;
2104 req_time.mFlags = kAudioTimeStampSampleTimeValid;
2105 passed_time = &req_time;
2108 sc = AudioQueueEnqueueBufferWithParameters(This->aqueue,
2109 This->public_buffer, 0, NULL, 0, 0, 0, NULL, passed_time,
2110 &start_time);
2111 if(sc != noErr){
2112 OSSpinLockUnlock(&This->lock);
2113 ERR("Unable to enqueue buffer: %lx\n", sc);
2114 return AUDCLNT_E_DEVICE_INVALIDATED;
2116 list_add_tail(&This->queued_buffers, &buf->entry);
2118 if(start_time.mFlags & kAudioTimeStampSampleTimeValid){
2119 QueuedBufInfo *bufinfo;
2121 bufinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*bufinfo));
2122 bufinfo->start_sampletime = start_time.mSampleTime;
2123 bufinfo->start_pos = This->written_frames;
2124 bufinfo->len_frames = frames;
2126 list_add_tail(&This->queued_bufinfos, &bufinfo->entry);
2128 This->next_sampletime = start_time.mSampleTime + bufinfo->len_frames;
2129 }else
2130 WARN("Start time didn't contain valid SampleTime member\n");
2132 if(This->playing == StateStopped)
2133 AudioQueuePrime(This->aqueue, 0, NULL);
2135 This->public_buffer = NULL;
2136 This->getbuf_last = 0;
2137 This->written_frames += frames;
2138 This->inbuf_frames += frames;
2140 OSSpinLockUnlock(&This->lock);
2142 return S_OK;
2145 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2146 AudioRenderClient_QueryInterface,
2147 AudioRenderClient_AddRef,
2148 AudioRenderClient_Release,
2149 AudioRenderClient_GetBuffer,
2150 AudioRenderClient_ReleaseBuffer
2153 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2154 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2156 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2158 if(!ppv)
2159 return E_POINTER;
2160 *ppv = NULL;
2162 if(IsEqualIID(riid, &IID_IUnknown) ||
2163 IsEqualIID(riid, &IID_IAudioCaptureClient))
2164 *ppv = iface;
2165 if(*ppv){
2166 IUnknown_AddRef((IUnknown*)*ppv);
2167 return S_OK;
2170 WARN("Unknown interface %s\n", debugstr_guid(riid));
2171 return E_NOINTERFACE;
2174 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2176 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2177 return IAudioClient_AddRef(&This->IAudioClient_iface);
2180 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2182 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2183 return IAudioClient_Release(&This->IAudioClient_iface);
2186 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames)
2188 AQBuffer *buf;
2189 OSStatus sc;
2191 avail_update(This); /* once, not inside loop */
2193 for(;;){
2194 if(!This->public_buffer){
2195 struct list *head = list_head(&This->avail_buffers);
2197 if(!head){
2198 *frames = 0;
2199 return S_OK;
2201 buf = LIST_ENTRY(head, AQBuffer, entry);
2202 This->public_buffer = buf->buf;
2203 list_remove(&buf->entry);
2204 }else
2205 buf = This->public_buffer->mUserData;
2206 *frames = This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
2207 if(*frames)
2208 return S_OK;
2210 WARN("empty packet\n");
2211 buf->used = TRUE;
2212 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2213 if(sc != noErr){
2214 ERR("Unable to enqueue buffer: %lx\n", sc);
2215 /* Release will free This->public_buffer */
2216 return AUDCLNT_E_DEVICE_INVALIDATED;
2217 }else
2218 list_add_tail(&This->queued_buffers, &buf->entry);
2219 This->public_buffer = NULL;
2223 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2224 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2225 UINT64 *qpcpos)
2227 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2228 HRESULT hr;
2230 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2231 devpos, qpcpos);
2233 if(!data || !frames || !flags)
2234 return E_POINTER;
2236 OSSpinLockLock(&This->lock);
2238 if(This->getbuf_last){
2239 OSSpinLockUnlock(&This->lock);
2240 return AUDCLNT_E_OUT_OF_ORDER;
2243 hr = AudioCaptureClient_GetNextPacket(This, frames);
2244 if(FAILED(hr)){
2245 OSSpinLockUnlock(&This->lock);
2246 return hr;
2249 if((This->getbuf_last = *frames)){
2250 *flags = 0;
2251 *data = This->public_buffer->mAudioData;
2253 if(devpos)
2254 *devpos = This->written_frames;
2255 if(qpcpos){ /* fixme: qpc of recording time */
2256 LARGE_INTEGER stamp, freq;
2257 QueryPerformanceCounter(&stamp);
2258 QueryPerformanceFrequency(&freq);
2259 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2262 OSSpinLockUnlock(&This->lock);
2264 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2267 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2268 IAudioCaptureClient *iface, UINT32 done)
2270 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2271 AQBuffer *buf;
2272 OSStatus sc;
2274 TRACE("(%p)->(%u)\n", This, done);
2276 OSSpinLockLock(&This->lock);
2278 if(!done){
2279 This->getbuf_last = 0;
2280 OSSpinLockUnlock(&This->lock);
2281 return S_OK;
2284 if(!This->getbuf_last){
2285 OSSpinLockUnlock(&This->lock);
2286 return AUDCLNT_E_OUT_OF_ORDER;
2289 if(This->getbuf_last != done){
2290 OSSpinLockUnlock(&This->lock);
2291 return AUDCLNT_E_INVALID_SIZE;
2294 This->written_frames += done;
2295 This->inbuf_frames -= done;
2296 This->getbuf_last = 0;
2298 buf = This->public_buffer->mUserData;
2299 buf->used = TRUE;
2300 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2301 if(sc != noErr){
2302 OSSpinLockUnlock(&This->lock);
2303 /* fixme: can't zero public_buffer or we lose memory, but then
2304 * GetBuffer will see that packet again and again. */
2305 ERR("Unable to enqueue buffer: %lx\n", sc);
2306 return AUDCLNT_E_DEVICE_INVALIDATED;
2307 }else
2308 list_add_tail(&This->queued_buffers, &buf->entry);
2309 This->public_buffer = NULL;
2311 OSSpinLockUnlock(&This->lock);
2313 return S_OK;
2316 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2317 IAudioCaptureClient *iface, UINT32 *frames)
2319 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2320 HRESULT hr;
2322 TRACE("(%p)->(%p)\n", This, frames);
2324 if(!frames)
2325 return E_POINTER;
2327 OSSpinLockLock(&This->lock);
2329 hr = AudioCaptureClient_GetNextPacket(This, frames);
2331 OSSpinLockUnlock(&This->lock);
2333 return hr;
2336 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2338 AudioCaptureClient_QueryInterface,
2339 AudioCaptureClient_AddRef,
2340 AudioCaptureClient_Release,
2341 AudioCaptureClient_GetBuffer,
2342 AudioCaptureClient_ReleaseBuffer,
2343 AudioCaptureClient_GetNextPacketSize
2346 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2347 REFIID riid, void **ppv)
2349 ACImpl *This = impl_from_IAudioClock(iface);
2351 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2353 if(!ppv)
2354 return E_POINTER;
2355 *ppv = NULL;
2357 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2358 *ppv = iface;
2359 else if(IsEqualIID(riid, &IID_IAudioClock2))
2360 *ppv = &This->IAudioClock2_iface;
2361 if(*ppv){
2362 IUnknown_AddRef((IUnknown*)*ppv);
2363 return S_OK;
2366 WARN("Unknown interface %s\n", debugstr_guid(riid));
2367 return E_NOINTERFACE;
2370 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2372 ACImpl *This = impl_from_IAudioClock(iface);
2373 return IAudioClient_AddRef(&This->IAudioClient_iface);
2376 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2378 ACImpl *This = impl_from_IAudioClock(iface);
2379 return IAudioClient_Release(&This->IAudioClient_iface);
2382 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2384 ACImpl *This = impl_from_IAudioClock(iface);
2386 TRACE("(%p)->(%p)\n", This, freq);
2388 *freq = This->fmt->nSamplesPerSec;
2390 return S_OK;
2393 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2394 UINT64 *pos, UINT64 *qpctime)
2396 avail_update(This);
2398 if(This->dataflow == eRender)
2399 *pos = get_current_aqbuffer_position(This, BUFPOS_ABSOLUTE);
2400 else
2401 *pos = This->inbuf_frames + This->written_frames;
2403 if(qpctime){
2404 LARGE_INTEGER stamp, freq;
2405 QueryPerformanceCounter(&stamp);
2406 QueryPerformanceFrequency(&freq);
2407 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2410 return S_OK;
2413 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2414 UINT64 *qpctime)
2416 ACImpl *This = impl_from_IAudioClock(iface);
2417 HRESULT hr;
2419 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2421 if(!pos)
2422 return E_POINTER;
2424 OSSpinLockLock(&This->lock);
2426 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2428 OSSpinLockUnlock(&This->lock);
2430 return hr;
2433 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2434 DWORD *chars)
2436 ACImpl *This = impl_from_IAudioClock(iface);
2438 TRACE("(%p)->(%p)\n", This, chars);
2440 if(!chars)
2441 return E_POINTER;
2443 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2445 return S_OK;
2448 static const IAudioClockVtbl AudioClock_Vtbl =
2450 AudioClock_QueryInterface,
2451 AudioClock_AddRef,
2452 AudioClock_Release,
2453 AudioClock_GetFrequency,
2454 AudioClock_GetPosition,
2455 AudioClock_GetCharacteristics
2458 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2459 REFIID riid, void **ppv)
2461 ACImpl *This = impl_from_IAudioClock2(iface);
2462 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2465 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2467 ACImpl *This = impl_from_IAudioClock2(iface);
2468 return IAudioClient_AddRef(&This->IAudioClient_iface);
2471 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2473 ACImpl *This = impl_from_IAudioClock2(iface);
2474 return IAudioClient_Release(&This->IAudioClient_iface);
2477 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2478 UINT64 *pos, UINT64 *qpctime)
2480 ACImpl *This = impl_from_IAudioClock2(iface);
2482 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2484 return E_NOTIMPL;
2487 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2489 AudioClock2_QueryInterface,
2490 AudioClock2_AddRef,
2491 AudioClock2_Release,
2492 AudioClock2_GetDevicePosition
2495 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2497 AudioSessionWrapper *ret;
2499 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2500 sizeof(AudioSessionWrapper));
2501 if(!ret)
2502 return NULL;
2504 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2505 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2506 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2508 ret->ref = 1;
2510 ret->client = client;
2511 if(client){
2512 ret->session = client->session;
2513 AudioClient_AddRef(&client->IAudioClient_iface);
2516 return ret;
2519 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2520 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2522 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2524 if(!ppv)
2525 return E_POINTER;
2526 *ppv = NULL;
2528 if(IsEqualIID(riid, &IID_IUnknown) ||
2529 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2530 IsEqualIID(riid, &IID_IAudioSessionControl2))
2531 *ppv = iface;
2532 if(*ppv){
2533 IUnknown_AddRef((IUnknown*)*ppv);
2534 return S_OK;
2537 WARN("Unknown interface %s\n", debugstr_guid(riid));
2538 return E_NOINTERFACE;
2541 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2543 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2544 ULONG ref;
2545 ref = InterlockedIncrement(&This->ref);
2546 TRACE("(%p) Refcount now %u\n", This, ref);
2547 return ref;
2550 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2552 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2553 ULONG ref;
2554 ref = InterlockedDecrement(&This->ref);
2555 TRACE("(%p) Refcount now %u\n", This, ref);
2556 if(!ref){
2557 if(This->client){
2558 OSSpinLockLock(&This->client->lock);
2559 This->client->session_wrapper = NULL;
2560 OSSpinLockUnlock(&This->client->lock);
2561 AudioClient_Release(&This->client->IAudioClient_iface);
2563 HeapFree(GetProcessHeap(), 0, This);
2565 return ref;
2568 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2569 AudioSessionState *state)
2571 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2572 ACImpl *client;
2574 TRACE("(%p)->(%p)\n", This, state);
2576 if(!state)
2577 return NULL_PTR_ERR;
2579 EnterCriticalSection(&g_sessions_lock);
2581 if(list_empty(&This->session->clients)){
2582 *state = AudioSessionStateExpired;
2583 LeaveCriticalSection(&g_sessions_lock);
2584 return S_OK;
2587 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2588 OSSpinLockLock(&client->lock);
2589 if(client->playing == StatePlaying ||
2590 client->playing == StateInTransition){
2591 *state = AudioSessionStateActive;
2592 OSSpinLockUnlock(&client->lock);
2593 LeaveCriticalSection(&g_sessions_lock);
2594 return S_OK;
2596 OSSpinLockUnlock(&client->lock);
2599 LeaveCriticalSection(&g_sessions_lock);
2601 *state = AudioSessionStateInactive;
2603 return S_OK;
2606 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2607 IAudioSessionControl2 *iface, WCHAR **name)
2609 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2611 FIXME("(%p)->(%p) - stub\n", This, name);
2613 return E_NOTIMPL;
2616 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2617 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2619 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2621 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2623 return E_NOTIMPL;
2626 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2627 IAudioSessionControl2 *iface, WCHAR **path)
2629 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2631 FIXME("(%p)->(%p) - stub\n", This, path);
2633 return E_NOTIMPL;
2636 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2637 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2639 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2641 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2643 return E_NOTIMPL;
2646 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2647 IAudioSessionControl2 *iface, GUID *group)
2649 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2651 FIXME("(%p)->(%p) - stub\n", This, group);
2653 return E_NOTIMPL;
2656 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2657 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2659 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2661 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2662 debugstr_guid(session));
2664 return E_NOTIMPL;
2667 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2668 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2670 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2672 FIXME("(%p)->(%p) - stub\n", This, events);
2674 return S_OK;
2677 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2678 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2680 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2682 FIXME("(%p)->(%p) - stub\n", This, events);
2684 return S_OK;
2687 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2688 IAudioSessionControl2 *iface, WCHAR **id)
2690 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2692 FIXME("(%p)->(%p) - stub\n", This, id);
2694 return E_NOTIMPL;
2697 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2698 IAudioSessionControl2 *iface, WCHAR **id)
2700 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2702 FIXME("(%p)->(%p) - stub\n", This, id);
2704 return E_NOTIMPL;
2707 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2708 IAudioSessionControl2 *iface, DWORD *pid)
2710 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2712 TRACE("(%p)->(%p)\n", This, pid);
2714 if(!pid)
2715 return E_POINTER;
2717 *pid = GetCurrentProcessId();
2719 return S_OK;
2722 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2723 IAudioSessionControl2 *iface)
2725 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2727 TRACE("(%p)\n", This);
2729 return S_FALSE;
2732 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2733 IAudioSessionControl2 *iface, BOOL optout)
2735 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2737 TRACE("(%p)->(%d)\n", This, optout);
2739 return S_OK;
2742 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2744 AudioSessionControl_QueryInterface,
2745 AudioSessionControl_AddRef,
2746 AudioSessionControl_Release,
2747 AudioSessionControl_GetState,
2748 AudioSessionControl_GetDisplayName,
2749 AudioSessionControl_SetDisplayName,
2750 AudioSessionControl_GetIconPath,
2751 AudioSessionControl_SetIconPath,
2752 AudioSessionControl_GetGroupingParam,
2753 AudioSessionControl_SetGroupingParam,
2754 AudioSessionControl_RegisterAudioSessionNotification,
2755 AudioSessionControl_UnregisterAudioSessionNotification,
2756 AudioSessionControl_GetSessionIdentifier,
2757 AudioSessionControl_GetSessionInstanceIdentifier,
2758 AudioSessionControl_GetProcessId,
2759 AudioSessionControl_IsSystemSoundsSession,
2760 AudioSessionControl_SetDuckingPreference
2763 /* index == -1 means set all channels, otherwise sets only the given channel */
2764 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2766 float level;
2767 OSStatus sc;
2769 if(index == (UINT32)-1){
2770 HRESULT ret = S_OK;
2771 UINT32 i;
2772 for(i = 0; i < This->fmt->nChannels; ++i){
2773 HRESULT hr;
2774 hr = ca_setvol(This, i);
2775 if(FAILED(hr))
2776 ret = hr;
2778 return ret;
2781 if(This->session->mute)
2782 level = 0;
2783 else
2784 level = This->session->master_vol *
2785 This->session->channel_vols[index] * This->vols[index];
2787 sc = AudioQueueSetParameter(This->aqueue, kAudioQueueParam_Volume, level);
2788 if(sc != noErr)
2789 WARN("Setting _Volume property failed: %lx\n", sc);
2791 return S_OK;
2794 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
2796 HRESULT ret = S_OK;
2797 ACImpl *client;
2799 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2800 HRESULT hr;
2801 hr = ca_setvol(client, index);
2802 if(FAILED(hr))
2803 ret = hr;
2806 return ret;
2809 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2810 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2812 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2814 if(!ppv)
2815 return E_POINTER;
2816 *ppv = NULL;
2818 if(IsEqualIID(riid, &IID_IUnknown) ||
2819 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2820 *ppv = iface;
2821 if(*ppv){
2822 IUnknown_AddRef((IUnknown*)*ppv);
2823 return S_OK;
2826 WARN("Unknown interface %s\n", debugstr_guid(riid));
2827 return E_NOINTERFACE;
2830 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2832 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2833 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2836 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2838 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2839 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2842 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2843 ISimpleAudioVolume *iface, float level, const GUID *context)
2845 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2846 AudioSession *session = This->session;
2847 HRESULT ret;
2849 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2851 if(level < 0.f || level > 1.f)
2852 return E_INVALIDARG;
2854 if(context)
2855 FIXME("Notifications not supported yet\n");
2857 EnterCriticalSection(&session->lock);
2859 session->master_vol = level;
2861 ret = ca_session_setvol(session, -1);
2863 LeaveCriticalSection(&session->lock);
2865 return ret;
2868 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2869 ISimpleAudioVolume *iface, float *level)
2871 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2872 AudioSession *session = This->session;
2874 TRACE("(%p)->(%p)\n", session, level);
2876 if(!level)
2877 return NULL_PTR_ERR;
2879 *level = session->master_vol;
2881 return S_OK;
2884 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2885 BOOL mute, const GUID *context)
2887 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2888 AudioSession *session = This->session;
2890 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2892 if(context)
2893 FIXME("Notifications not supported yet\n");
2895 EnterCriticalSection(&session->lock);
2897 session->mute = mute;
2899 ca_session_setvol(session, -1);
2901 LeaveCriticalSection(&session->lock);
2903 return S_OK;
2906 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2907 BOOL *mute)
2909 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2910 AudioSession *session = This->session;
2912 TRACE("(%p)->(%p)\n", session, mute);
2914 if(!mute)
2915 return NULL_PTR_ERR;
2917 *mute = session->mute;
2919 return S_OK;
2922 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2924 SimpleAudioVolume_QueryInterface,
2925 SimpleAudioVolume_AddRef,
2926 SimpleAudioVolume_Release,
2927 SimpleAudioVolume_SetMasterVolume,
2928 SimpleAudioVolume_GetMasterVolume,
2929 SimpleAudioVolume_SetMute,
2930 SimpleAudioVolume_GetMute
2933 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2934 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2936 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2938 if(!ppv)
2939 return E_POINTER;
2940 *ppv = NULL;
2942 if(IsEqualIID(riid, &IID_IUnknown) ||
2943 IsEqualIID(riid, &IID_IAudioStreamVolume))
2944 *ppv = iface;
2945 if(*ppv){
2946 IUnknown_AddRef((IUnknown*)*ppv);
2947 return S_OK;
2950 WARN("Unknown interface %s\n", debugstr_guid(riid));
2951 return E_NOINTERFACE;
2954 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2956 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2957 return IAudioClient_AddRef(&This->IAudioClient_iface);
2960 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2962 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2963 return IAudioClient_Release(&This->IAudioClient_iface);
2966 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2967 IAudioStreamVolume *iface, UINT32 *out)
2969 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2971 TRACE("(%p)->(%p)\n", This, out);
2973 if(!out)
2974 return E_POINTER;
2976 *out = This->fmt->nChannels;
2978 return S_OK;
2981 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2982 IAudioStreamVolume *iface, UINT32 index, float level)
2984 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2985 HRESULT ret;
2987 TRACE("(%p)->(%d, %f)\n", This, index, level);
2989 if(level < 0.f || level > 1.f)
2990 return E_INVALIDARG;
2992 if(index >= This->fmt->nChannels)
2993 return E_INVALIDARG;
2995 OSSpinLockLock(&This->lock);
2997 This->vols[index] = level;
2999 WARN("AudioQueue doesn't support per-channel volume control\n");
3000 ret = ca_setvol(This, index);
3002 OSSpinLockUnlock(&This->lock);
3004 return ret;
3007 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3008 IAudioStreamVolume *iface, UINT32 index, float *level)
3010 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3012 TRACE("(%p)->(%d, %p)\n", This, index, level);
3014 if(!level)
3015 return E_POINTER;
3017 if(index >= This->fmt->nChannels)
3018 return E_INVALIDARG;
3020 *level = This->vols[index];
3022 return S_OK;
3025 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3026 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3028 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3029 int i;
3030 HRESULT ret;
3032 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3034 if(!levels)
3035 return E_POINTER;
3037 if(count != This->fmt->nChannels)
3038 return E_INVALIDARG;
3040 OSSpinLockLock(&This->lock);
3042 for(i = 0; i < count; ++i)
3043 This->vols[i] = levels[i];
3045 ret = ca_setvol(This, -1);
3047 OSSpinLockUnlock(&This->lock);
3049 return ret;
3052 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3053 IAudioStreamVolume *iface, UINT32 count, float *levels)
3055 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3056 int i;
3058 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3060 if(!levels)
3061 return E_POINTER;
3063 if(count != This->fmt->nChannels)
3064 return E_INVALIDARG;
3066 OSSpinLockLock(&This->lock);
3068 for(i = 0; i < count; ++i)
3069 levels[i] = This->vols[i];
3071 OSSpinLockUnlock(&This->lock);
3073 return S_OK;
3076 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3078 AudioStreamVolume_QueryInterface,
3079 AudioStreamVolume_AddRef,
3080 AudioStreamVolume_Release,
3081 AudioStreamVolume_GetChannelCount,
3082 AudioStreamVolume_SetChannelVolume,
3083 AudioStreamVolume_GetChannelVolume,
3084 AudioStreamVolume_SetAllVolumes,
3085 AudioStreamVolume_GetAllVolumes
3088 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3089 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3091 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3093 if(!ppv)
3094 return E_POINTER;
3095 *ppv = NULL;
3097 if(IsEqualIID(riid, &IID_IUnknown) ||
3098 IsEqualIID(riid, &IID_IChannelAudioVolume))
3099 *ppv = iface;
3100 if(*ppv){
3101 IUnknown_AddRef((IUnknown*)*ppv);
3102 return S_OK;
3105 WARN("Unknown interface %s\n", debugstr_guid(riid));
3106 return E_NOINTERFACE;
3109 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3111 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3112 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3115 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3117 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3118 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3121 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3122 IChannelAudioVolume *iface, UINT32 *out)
3124 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3125 AudioSession *session = This->session;
3127 TRACE("(%p)->(%p)\n", session, out);
3129 if(!out)
3130 return NULL_PTR_ERR;
3132 *out = session->channel_count;
3134 return S_OK;
3137 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3138 IChannelAudioVolume *iface, UINT32 index, float level,
3139 const GUID *context)
3141 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3142 AudioSession *session = This->session;
3143 HRESULT ret;
3145 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3146 wine_dbgstr_guid(context));
3148 if(level < 0.f || level > 1.f)
3149 return E_INVALIDARG;
3151 if(index >= session->channel_count)
3152 return E_INVALIDARG;
3154 if(context)
3155 FIXME("Notifications not supported yet\n");
3157 EnterCriticalSection(&session->lock);
3159 session->channel_vols[index] = level;
3161 WARN("AudioQueue doesn't support per-channel volume control\n");
3162 ret = ca_session_setvol(session, index);
3164 LeaveCriticalSection(&session->lock);
3166 return ret;
3169 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3170 IChannelAudioVolume *iface, UINT32 index, float *level)
3172 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3173 AudioSession *session = This->session;
3175 TRACE("(%p)->(%d, %p)\n", session, index, level);
3177 if(!level)
3178 return NULL_PTR_ERR;
3180 if(index >= session->channel_count)
3181 return E_INVALIDARG;
3183 *level = session->channel_vols[index];
3185 return S_OK;
3188 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3189 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3190 const GUID *context)
3192 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3193 AudioSession *session = This->session;
3194 int i;
3195 HRESULT ret;
3197 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3198 wine_dbgstr_guid(context));
3200 if(!levels)
3201 return NULL_PTR_ERR;
3203 if(count != session->channel_count)
3204 return E_INVALIDARG;
3206 if(context)
3207 FIXME("Notifications not supported yet\n");
3209 EnterCriticalSection(&session->lock);
3211 for(i = 0; i < count; ++i)
3212 session->channel_vols[i] = levels[i];
3214 ret = ca_session_setvol(session, -1);
3216 LeaveCriticalSection(&session->lock);
3218 return ret;
3221 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3222 IChannelAudioVolume *iface, UINT32 count, float *levels)
3224 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3225 AudioSession *session = This->session;
3226 int i;
3228 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3230 if(!levels)
3231 return NULL_PTR_ERR;
3233 if(count != session->channel_count)
3234 return E_INVALIDARG;
3236 for(i = 0; i < count; ++i)
3237 levels[i] = session->channel_vols[i];
3239 return S_OK;
3242 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3244 ChannelAudioVolume_QueryInterface,
3245 ChannelAudioVolume_AddRef,
3246 ChannelAudioVolume_Release,
3247 ChannelAudioVolume_GetChannelCount,
3248 ChannelAudioVolume_SetChannelVolume,
3249 ChannelAudioVolume_GetChannelVolume,
3250 ChannelAudioVolume_SetAllVolumes,
3251 ChannelAudioVolume_GetAllVolumes
3254 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3255 REFIID riid, void **ppv)
3257 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3259 if(!ppv)
3260 return E_POINTER;
3261 *ppv = NULL;
3263 if(IsEqualIID(riid, &IID_IUnknown) ||
3264 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3265 IsEqualIID(riid, &IID_IAudioSessionManager2))
3266 *ppv = iface;
3267 if(*ppv){
3268 IUnknown_AddRef((IUnknown*)*ppv);
3269 return S_OK;
3272 WARN("Unknown interface %s\n", debugstr_guid(riid));
3273 return E_NOINTERFACE;
3276 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3278 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3279 ULONG ref;
3280 ref = InterlockedIncrement(&This->ref);
3281 TRACE("(%p) Refcount now %u\n", This, ref);
3282 return ref;
3285 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3287 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3288 ULONG ref;
3289 ref = InterlockedDecrement(&This->ref);
3290 TRACE("(%p) Refcount now %u\n", This, ref);
3291 if(!ref)
3292 HeapFree(GetProcessHeap(), 0, This);
3293 return ref;
3296 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3297 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3298 IAudioSessionControl **out)
3300 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3301 AudioSession *session;
3302 AudioSessionWrapper *wrapper;
3303 HRESULT hr;
3305 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3306 flags, out);
3308 hr = get_audio_session(session_guid, This->device, 0, &session);
3309 if(FAILED(hr))
3310 return hr;
3312 wrapper = AudioSessionWrapper_Create(NULL);
3313 if(!wrapper)
3314 return E_OUTOFMEMORY;
3316 wrapper->session = session;
3318 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3320 return S_OK;
3323 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3324 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3325 ISimpleAudioVolume **out)
3327 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3328 AudioSession *session;
3329 AudioSessionWrapper *wrapper;
3330 HRESULT hr;
3332 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3333 flags, out);
3335 hr = get_audio_session(session_guid, This->device, 0, &session);
3336 if(FAILED(hr))
3337 return hr;
3339 wrapper = AudioSessionWrapper_Create(NULL);
3340 if(!wrapper)
3341 return E_OUTOFMEMORY;
3343 wrapper->session = session;
3345 *out = &wrapper->ISimpleAudioVolume_iface;
3347 return S_OK;
3350 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3351 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3353 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3354 FIXME("(%p)->(%p) - stub\n", This, out);
3355 return E_NOTIMPL;
3358 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3359 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3361 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3362 FIXME("(%p)->(%p) - stub\n", This, notification);
3363 return E_NOTIMPL;
3366 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3367 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3369 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3370 FIXME("(%p)->(%p) - stub\n", This, notification);
3371 return E_NOTIMPL;
3374 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3375 IAudioSessionManager2 *iface, const WCHAR *session_id,
3376 IAudioVolumeDuckNotification *notification)
3378 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3379 FIXME("(%p)->(%p) - stub\n", This, notification);
3380 return E_NOTIMPL;
3383 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3384 IAudioSessionManager2 *iface,
3385 IAudioVolumeDuckNotification *notification)
3387 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3388 FIXME("(%p)->(%p) - stub\n", This, notification);
3389 return E_NOTIMPL;
3392 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3394 AudioSessionManager_QueryInterface,
3395 AudioSessionManager_AddRef,
3396 AudioSessionManager_Release,
3397 AudioSessionManager_GetAudioSessionControl,
3398 AudioSessionManager_GetSimpleAudioVolume,
3399 AudioSessionManager_GetSessionEnumerator,
3400 AudioSessionManager_RegisterSessionNotification,
3401 AudioSessionManager_UnregisterSessionNotification,
3402 AudioSessionManager_RegisterDuckNotification,
3403 AudioSessionManager_UnregisterDuckNotification
3406 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3407 IAudioSessionManager2 **out)
3409 SessionMgr *This;
3411 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3412 if(!This)
3413 return E_OUTOFMEMORY;
3415 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3416 This->device = device;
3417 This->ref = 1;
3419 *out = &This->IAudioSessionManager2_iface;
3421 return S_OK;