msvcp90: Added num_put<short> implementation.
[wine/multimedia.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobda0123b7bed96123ecb34b8f99278cdbf2e59039
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>
59 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
61 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
63 #define CAPTURE_BUFFERS 5
65 static const REFERENCE_TIME DefaultPeriod = 200000;
66 static const REFERENCE_TIME MinimumPeriod = 100000;
68 typedef struct _QueuedBufInfo {
69 Float64 start_sampletime;
70 UINT64 start_pos;
71 UINT32 len_frames;
72 struct list entry;
73 } QueuedBufInfo;
75 typedef struct _AQBuffer {
76 AudioQueueBufferRef buf;
77 struct list entry;
78 BOOL used;
79 } AQBuffer;
81 struct ACImpl;
82 typedef struct ACImpl ACImpl;
84 typedef struct _AudioSession {
85 GUID guid;
86 struct list clients;
88 IMMDevice *device;
90 float master_vol;
91 UINT32 channel_count;
92 float *channel_vols;
93 BOOL mute;
95 CRITICAL_SECTION lock;
97 struct list entry;
98 } AudioSession;
100 typedef struct _AudioSessionWrapper {
101 IAudioSessionControl2 IAudioSessionControl2_iface;
102 IChannelAudioVolume IChannelAudioVolume_iface;
103 ISimpleAudioVolume ISimpleAudioVolume_iface;
105 LONG ref;
107 ACImpl *client;
108 AudioSession *session;
109 } AudioSessionWrapper;
111 struct ACImpl {
112 IAudioClient IAudioClient_iface;
113 IAudioRenderClient IAudioRenderClient_iface;
114 IAudioCaptureClient IAudioCaptureClient_iface;
115 IAudioClock IAudioClock_iface;
116 IAudioClock2 IAudioClock2_iface;
117 IAudioStreamVolume IAudioStreamVolume_iface;
119 LONG ref;
121 IMMDevice *parent;
123 WAVEFORMATEX *fmt;
125 EDataFlow dataflow;
126 DWORD flags;
127 AUDCLNT_SHAREMODE share;
128 HANDLE event;
129 float *vols;
131 AudioDeviceID adevid;
132 AudioQueueRef aqueue;
133 AudioObjectPropertyScope scope;
134 HANDLE timer;
135 UINT32 period_ms, bufsize_frames, inbuf_frames;
136 UINT64 last_time, written_frames;
137 AudioQueueBufferRef public_buffer;
138 UINT32 getbuf_last;
139 int playing;
141 Float64 highest_sampletime, next_sampletime;
143 AudioSession *session;
144 AudioSessionWrapper *session_wrapper;
146 struct list entry;
148 struct list avail_buffers;
149 struct list queued_buffers; /* either in avail, queued or public_buffer */
150 struct list queued_bufinfos;
152 OSSpinLock lock;
155 enum PlayingStates {
156 StateStopped = 0,
157 StatePlaying,
158 StateInTransition
161 static const IAudioClientVtbl AudioClient_Vtbl;
162 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
163 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
164 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
165 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
166 static const IAudioClockVtbl AudioClock_Vtbl;
167 static const IAudioClock2Vtbl AudioClock2_Vtbl;
168 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
169 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
170 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
172 typedef struct _SessionMgr {
173 IAudioSessionManager2 IAudioSessionManager2_iface;
175 LONG ref;
177 IMMDevice *device;
178 } SessionMgr;
180 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
181 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
182 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v',0};
183 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
184 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
185 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v','\\','d','e','v','i','c','e','s',0};
186 static const WCHAR guidW[] = {'g','u','i','d',0};
188 static HANDLE g_timer_q;
190 static CRITICAL_SECTION g_sessions_lock;
191 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
193 0, 0, &g_sessions_lock,
194 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
195 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
197 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
198 static struct list g_sessions = LIST_INIT(g_sessions);
200 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames);
201 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
202 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
204 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
209 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
214 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
219 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
221 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
224 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
226 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
229 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
231 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
234 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
236 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
239 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
241 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
244 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
246 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
249 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
251 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
254 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
256 switch (reason)
258 case DLL_PROCESS_ATTACH:
259 g_timer_q = CreateTimerQueue();
260 if(!g_timer_q)
261 return FALSE;
262 break;
264 case DLL_PROCESS_DETACH:
265 DeleteCriticalSection(&g_sessions_lock);
266 break;
268 return TRUE;
271 /* From <dlls/mmdevapi/mmdevapi.h> */
272 enum DriverPriority {
273 Priority_Unavailable = 0,
274 Priority_Low,
275 Priority_Neutral,
276 Priority_Preferred
279 int WINAPI AUDDRV_GetPriority(void)
281 return Priority_Neutral;
284 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
285 GUID *guid)
287 HKEY key;
288 BOOL opened = FALSE;
289 LONG lr;
291 if(!drv_key){
292 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
293 NULL, &drv_key, NULL);
294 if(lr != ERROR_SUCCESS){
295 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
296 return;
298 opened = TRUE;
301 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
302 NULL, &key, NULL);
303 if(lr != ERROR_SUCCESS){
304 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
305 goto exit;
308 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
309 sizeof(GUID));
310 if(lr != ERROR_SUCCESS)
311 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
313 RegCloseKey(key);
314 exit:
315 if(opened)
316 RegCloseKey(drv_key);
319 static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid)
321 HKEY key = NULL, dev_key;
322 DWORD type, size = sizeof(*guid);
323 WCHAR key_name[256];
325 static const WCHAR key_fmt[] = {'%','u',0};
327 if(flow == eCapture)
328 key_name[0] = '1';
329 else
330 key_name[0] = '0';
331 key_name[1] = ',';
333 sprintfW(key_name + 2, key_fmt, device);
335 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
336 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
337 if(RegQueryValueExW(dev_key, guidW, 0, &type,
338 (BYTE*)guid, &size) == ERROR_SUCCESS){
339 if(type == REG_BINARY){
340 RegCloseKey(dev_key);
341 RegCloseKey(key);
342 return;
344 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
345 wine_dbgstr_w(key_name), type);
347 RegCloseKey(dev_key);
351 CoCreateGuid(guid);
353 set_device_guid(flow, key, key_name, guid);
355 if(key)
356 RegCloseKey(key);
359 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
360 GUID **guids, UINT *num, UINT *def_index)
362 UInt32 devsize, size;
363 AudioDeviceID *devices;
364 AudioDeviceID default_id;
365 AudioObjectPropertyAddress addr;
366 OSStatus sc;
367 int i, ndevices;
369 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
371 addr.mScope = kAudioObjectPropertyScopeGlobal;
372 addr.mElement = kAudioObjectPropertyElementMaster;
373 if(flow == eRender)
374 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
375 else if(flow == eCapture)
376 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
377 else
378 return E_INVALIDARG;
380 size = sizeof(default_id);
381 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
382 NULL, &size, &default_id);
383 if(sc != noErr){
384 WARN("Getting _DefaultInputDevice property failed: %lx\n", sc);
385 default_id = -1;
388 addr.mSelector = kAudioHardwarePropertyDevices;
389 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
390 NULL, &devsize);
391 if(sc != noErr){
392 WARN("Getting _Devices property size failed: %lx\n", sc);
393 return E_FAIL;
396 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
397 if(!devices)
398 return E_OUTOFMEMORY;
400 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
401 &devsize, devices);
402 if(sc != noErr){
403 WARN("Getting _Devices property failed: %lx\n", sc);
404 HeapFree(GetProcessHeap(), 0, devices);
405 return E_FAIL;
408 ndevices = devsize / sizeof(AudioDeviceID);
410 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
411 if(!*ids){
412 HeapFree(GetProcessHeap(), 0, devices);
413 return E_OUTOFMEMORY;
416 *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
417 if(!*ids){
418 HeapFree(GetProcessHeap(), 0, *ids);
419 HeapFree(GetProcessHeap(), 0, devices);
420 return E_OUTOFMEMORY;
423 *num = 0;
424 *def_index = (UINT)-1;
425 for(i = 0; i < ndevices; ++i){
426 AudioBufferList *buffers;
427 CFStringRef name;
428 SIZE_T len;
429 int j;
431 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
432 if(flow == eRender)
433 addr.mScope = kAudioDevicePropertyScopeOutput;
434 else
435 addr.mScope = kAudioDevicePropertyScopeInput;
436 addr.mElement = 0;
437 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
438 if(sc != noErr){
439 WARN("Unable to get _StreamConfiguration property size for "
440 "device %lu: %lx\n", devices[i], sc);
441 continue;
444 buffers = HeapAlloc(GetProcessHeap(), 0, size);
445 if(!buffers){
446 HeapFree(GetProcessHeap(), 0, devices);
447 for(j = 0; j < *num; ++j)
448 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
449 HeapFree(GetProcessHeap(), 0, *guids);
450 HeapFree(GetProcessHeap(), 0, *ids);
451 return E_OUTOFMEMORY;
454 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
455 &size, buffers);
456 if(sc != noErr){
457 WARN("Unable to get _StreamConfiguration property for "
458 "device %lu: %lx\n", devices[i], sc);
459 HeapFree(GetProcessHeap(), 0, buffers);
460 continue;
463 /* check that there's at least one channel in this device before
464 * we claim it as usable */
465 for(j = 0; j < buffers->mNumberBuffers; ++j)
466 if(buffers->mBuffers[j].mNumberChannels > 0)
467 break;
468 if(j >= buffers->mNumberBuffers){
469 HeapFree(GetProcessHeap(), 0, buffers);
470 continue;
473 HeapFree(GetProcessHeap(), 0, buffers);
475 size = sizeof(name);
476 addr.mSelector = kAudioObjectPropertyName;
477 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
478 &size, &name);
479 if(sc != noErr){
480 WARN("Unable to get _Name property for device %lu: %lx\n",
481 devices[i], sc);
482 continue;
485 len = CFStringGetLength(name) + 1;
486 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
487 if(!(*ids)[*num]){
488 CFRelease(name);
489 HeapFree(GetProcessHeap(), 0, devices);
490 for(j = 0; j < *num; ++j)
491 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
492 HeapFree(GetProcessHeap(), 0, *ids);
493 HeapFree(GetProcessHeap(), 0, *guids);
494 return E_OUTOFMEMORY;
496 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
497 ((*ids)[*num])[len - 1] = 0;
498 CFRelease(name);
500 get_device_guid(flow, devices[i], &(*guids)[*num]);
502 if(*def_index == (UINT)-1 && devices[i] == default_id)
503 *def_index = *num;
505 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
506 (unsigned int)devices[i], (*def_index == *num) ? " (default)" : "");
508 (*num)++;
511 if(*def_index == (UINT)-1)
512 *def_index = 0;
514 HeapFree(GetProcessHeap(), 0, devices);
516 return S_OK;
519 static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow)
521 HKEY devices_key;
522 UINT i = 0;
523 WCHAR key_name[256];
524 DWORD key_name_size;
526 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
527 ERR("No devices in registry?\n");
528 return FALSE;
531 while(1){
532 HKEY key;
533 DWORD size, type;
534 GUID reg_guid;
536 key_name_size = sizeof(key_name);
537 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
538 NULL, NULL, NULL) != ERROR_SUCCESS)
539 break;
541 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
542 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
543 continue;
546 size = sizeof(reg_guid);
547 if(RegQueryValueExW(key, guidW, 0, &type,
548 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
549 if(IsEqualGUID(&reg_guid, guid)){
550 RegCloseKey(key);
551 RegCloseKey(devices_key);
553 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
555 if(key_name[0] == '0')
556 *flow = eRender;
557 else if(key_name[0] == '1')
558 *flow = eCapture;
559 else{
560 ERR("Unknown device type: %c\n", key_name[0]);
561 return FALSE;
564 *id = strtoulW(key_name + 2, NULL, 10);
566 return TRUE;
570 RegCloseKey(key);
572 ++i;
575 RegCloseKey(devices_key);
577 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
579 return FALSE;
582 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
584 ACImpl *This;
585 AudioDeviceID adevid;
586 EDataFlow dataflow;
588 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
590 if(!get_deviceid_by_guid(guid, &adevid, &dataflow))
591 return AUDCLNT_E_DEVICE_INVALIDATED;
593 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
594 if(!This)
595 return E_OUTOFMEMORY;
597 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
598 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
599 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
600 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
601 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
602 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
604 This->dataflow = dataflow;
606 if(dataflow == eRender)
607 This->scope = kAudioDevicePropertyScopeOutput;
608 else if(dataflow == eCapture)
609 This->scope = kAudioDevicePropertyScopeInput;
610 else{
611 HeapFree(GetProcessHeap(), 0, This);
612 return E_INVALIDARG;
615 This->lock = 0;
617 This->parent = dev;
618 IMMDevice_AddRef(This->parent);
620 list_init(&This->avail_buffers);
621 list_init(&This->queued_buffers);
622 list_init(&This->queued_bufinfos);
624 This->adevid = adevid;
626 *out = &This->IAudioClient_iface;
627 IAudioClient_AddRef(&This->IAudioClient_iface);
629 return S_OK;
632 /* current position from start of stream */
633 #define BUFPOS_ABSOLUTE 1
634 /* current position from start of this buffer */
635 #define BUFPOS_RELATIVE 2
637 static UINT64 get_current_aqbuffer_position(ACImpl *This, int mode)
639 struct list *head;
640 QueuedBufInfo *bufinfo;
641 UINT64 ret;
643 head = list_head(&This->queued_bufinfos);
644 if(!head){
645 TRACE("No buffers queued\n");
646 if(mode == BUFPOS_ABSOLUTE)
647 return This->written_frames;
648 return 0;
650 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
652 if(This->playing == StatePlaying){
653 AudioTimeStamp tstamp;
654 OSStatus sc;
656 /* AudioQueueGetCurrentTime() is brain damaged. The returned
657 * mSampleTime member jumps backwards seemingly at random, so
658 * we record the highest sampletime and use that during these
659 * anomalies.
661 * It also behaves poorly when the queue is paused, jumping
662 * forwards during the pause and backwards again after resuming.
663 * So we record the sampletime when the queue is paused and use
664 * that. */
665 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
666 if(sc != noErr){
667 if(sc != kAudioQueueErr_InvalidRunState)
668 WARN("Unable to get current time: %lx\n", sc);
669 return 0;
672 if(!(tstamp.mFlags & kAudioTimeStampSampleTimeValid)){
673 FIXME("SampleTime not valid: %lx\n", tstamp.mFlags);
674 return 0;
677 if(tstamp.mSampleTime > This->highest_sampletime)
678 This->highest_sampletime = tstamp.mSampleTime;
681 while(This->highest_sampletime > bufinfo->start_sampletime + bufinfo->len_frames){
682 This->inbuf_frames -= bufinfo->len_frames;
683 list_remove(&bufinfo->entry);
684 HeapFree(GetProcessHeap(), 0, bufinfo);
686 head = list_head(&This->queued_bufinfos);
687 if(!head){
688 TRACE("No buffers queued\n");
689 if(mode == BUFPOS_ABSOLUTE)
690 return This->written_frames;
691 return 0;
693 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
696 if(This->highest_sampletime < bufinfo->start_sampletime)
697 ret = 0;
698 else
699 ret = This->highest_sampletime - bufinfo->start_sampletime;
701 if(mode == BUFPOS_ABSOLUTE){
702 ret = This->written_frames - (bufinfo->len_frames - ret);
703 while((head = list_next(&This->queued_bufinfos, &bufinfo->entry))){
704 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
705 ret -= bufinfo->len_frames;
709 TRACE("%llu frames (%s)\n", ret,
710 mode == BUFPOS_ABSOLUTE ? "absolute" : "relative");
712 return ret;
715 static void avail_update(ACImpl *This)
717 AQBuffer *buf, *next;
719 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
720 if(buf->used)
721 break;
722 if(This->dataflow == eCapture)
723 This->inbuf_frames += buf->buf->mAudioDataByteSize / This->fmt->nBlockAlign;
724 list_remove(&buf->entry);
725 list_add_tail(&This->avail_buffers, &buf->entry);
729 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
730 REFIID riid, void **ppv)
732 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
734 if(!ppv)
735 return E_POINTER;
736 *ppv = NULL;
737 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
738 *ppv = iface;
739 if(*ppv){
740 IUnknown_AddRef((IUnknown*)*ppv);
741 return S_OK;
743 WARN("Unknown interface %s\n", debugstr_guid(riid));
744 return E_NOINTERFACE;
747 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
749 ACImpl *This = impl_from_IAudioClient(iface);
750 ULONG ref;
751 ref = InterlockedIncrement(&This->ref);
752 TRACE("(%p) Refcount now %u\n", This, ref);
753 return ref;
756 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
758 ACImpl *This = impl_from_IAudioClient(iface);
759 ULONG ref;
760 ref = InterlockedDecrement(&This->ref);
761 TRACE("(%p) Refcount now %u\n", This, ref);
762 if(!ref){
763 if(This->aqueue){
764 AQBuffer *buf, *next;
765 QueuedBufInfo *bufinfo, *bufinfo2;
767 if(This->public_buffer){
768 buf = This->public_buffer->mUserData;
769 list_add_tail(&This->avail_buffers, &buf->entry);
772 IAudioClient_Stop(iface);
773 AudioQueueStop(This->aqueue, 1);
775 /* Stopped synchronously, all buffers returned. */
776 list_move_tail(&This->avail_buffers, &This->queued_buffers);
777 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->avail_buffers, AQBuffer, entry){
778 AudioQueueFreeBuffer(This->aqueue, buf->buf);
779 HeapFree(GetProcessHeap(), 0, buf);
782 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
783 QueuedBufInfo, entry)
784 HeapFree(GetProcessHeap(), 0, bufinfo);
786 AudioQueueDispose(This->aqueue, 1);
788 if(This->session){
789 EnterCriticalSection(&g_sessions_lock);
790 list_remove(&This->entry);
791 LeaveCriticalSection(&g_sessions_lock);
793 HeapFree(GetProcessHeap(), 0, This->vols);
794 CoTaskMemFree(This->fmt);
795 IMMDevice_Release(This->parent);
796 HeapFree(GetProcessHeap(), 0, This);
798 return ref;
801 static void dump_fmt(const WAVEFORMATEX *fmt)
803 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
804 switch(fmt->wFormatTag){
805 case WAVE_FORMAT_PCM:
806 TRACE("WAVE_FORMAT_PCM");
807 break;
808 case WAVE_FORMAT_IEEE_FLOAT:
809 TRACE("WAVE_FORMAT_IEEE_FLOAT");
810 break;
811 case WAVE_FORMAT_EXTENSIBLE:
812 TRACE("WAVE_FORMAT_EXTENSIBLE");
813 break;
814 default:
815 TRACE("Unknown");
816 break;
818 TRACE(")\n");
820 TRACE("nChannels: %u\n", fmt->nChannels);
821 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
822 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
823 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
824 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
825 TRACE("cbSize: %u\n", fmt->cbSize);
827 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
828 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
829 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
830 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
831 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
835 static DWORD get_channel_mask(unsigned int channels)
837 switch(channels){
838 case 0:
839 return 0;
840 case 1:
841 return KSAUDIO_SPEAKER_MONO;
842 case 2:
843 return KSAUDIO_SPEAKER_STEREO;
844 case 3:
845 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
846 case 4:
847 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
848 case 5:
849 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
850 case 6:
851 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
852 case 7:
853 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
854 case 8:
855 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
857 FIXME("Unknown speaker configuration: %u\n", channels);
858 return 0;
861 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
863 WAVEFORMATEX *ret;
864 size_t size;
866 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
867 size = sizeof(WAVEFORMATEXTENSIBLE);
868 else
869 size = sizeof(WAVEFORMATEX);
871 ret = CoTaskMemAlloc(size);
872 if(!ret)
873 return NULL;
875 memcpy(ret, fmt, size);
877 ret->cbSize = size - sizeof(WAVEFORMATEX);
879 return ret;
882 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
883 const WAVEFORMATEX *fmt)
885 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
887 desc->mFormatFlags = 0;
889 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
890 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
891 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
892 desc->mFormatID = kAudioFormatLinearPCM;
893 if(fmt->wBitsPerSample > 8)
894 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
895 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
896 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
897 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
898 desc->mFormatID = kAudioFormatLinearPCM;
899 desc->mFormatFlags = kAudioFormatFlagIsFloat;
900 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
901 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
902 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
903 desc->mFormatID = kAudioFormatULaw;
904 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
905 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
906 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
907 desc->mFormatID = kAudioFormatALaw;
908 }else
909 return AUDCLNT_E_UNSUPPORTED_FORMAT;
911 desc->mSampleRate = fmt->nSamplesPerSec;
912 desc->mBytesPerPacket = fmt->nBlockAlign;
913 desc->mFramesPerPacket = 1;
914 desc->mBytesPerFrame = fmt->nBlockAlign;
915 desc->mChannelsPerFrame = fmt->nChannels;
916 desc->mBitsPerChannel = fmt->wBitsPerSample;
917 desc->mReserved = 0;
919 return S_OK;
922 /* We can't use debug printing or {Enter,Leave}CriticalSection from
923 * OSX callback threads. We may use OSSpinLock.
924 * OSSpinLock is not a recursive lock, so don't call
925 * synchronized functions while holding the lock. */
926 static void ca_out_buffer_cb(void *user, AudioQueueRef aqueue,
927 AudioQueueBufferRef buffer)
929 AQBuffer *buf = buffer->mUserData;
931 buf->used = FALSE;
934 static void ca_in_buffer_cb(void *user, AudioQueueRef aqueue,
935 AudioQueueBufferRef buffer, const AudioTimeStamp *start,
936 UInt32 ndesc, const AudioStreamPacketDescription *descs)
938 AQBuffer *buf = buffer->mUserData;
940 buf->used = FALSE;
941 /* let's update inbuf_frames synchronously without OSAddAtomic */
944 static HRESULT ca_setup_aqueue(AudioDeviceID did, EDataFlow flow,
945 const WAVEFORMATEX *fmt, void *user, AudioQueueRef *aqueue)
947 AudioStreamBasicDescription desc;
948 AudioObjectPropertyAddress addr;
949 CFStringRef uid;
950 OSStatus sc;
951 HRESULT hr;
952 UInt32 size;
954 addr.mScope = kAudioObjectPropertyScopeGlobal;
955 addr.mElement = 0;
956 addr.mSelector = kAudioDevicePropertyDeviceUID;
958 size = sizeof(uid);
959 sc = AudioObjectGetPropertyData(did, &addr, 0, NULL, &size, &uid);
960 if(sc != noErr){
961 WARN("Unable to get _DeviceUID property: %lx\n", sc);
962 return E_FAIL;
965 hr = ca_get_audiodesc(&desc, fmt);
966 if(FAILED(hr)){
967 CFRelease(uid);
968 return hr;
971 if(flow == eRender)
972 sc = AudioQueueNewOutput(&desc, ca_out_buffer_cb, user, NULL, NULL, 0,
973 aqueue);
974 else if(flow == eCapture)
975 sc = AudioQueueNewInput(&desc, ca_in_buffer_cb, user, NULL, NULL, 0,
976 aqueue);
977 else{
978 CFRelease(uid);
979 return E_UNEXPECTED;
981 if(sc != noErr){
982 WARN("Unable to create AudioQueue: %lx\n", sc);
983 CFRelease(uid);
984 return E_FAIL;
987 sc = AudioQueueSetProperty(*aqueue, kAudioQueueProperty_CurrentDevice,
988 &uid, sizeof(uid));
989 if(sc != noErr){
990 CFRelease(uid);
991 return E_FAIL;
994 CFRelease(uid);
996 return S_OK;
999 static void session_init_vols(AudioSession *session, UINT channels)
1001 if(session->channel_count < channels){
1002 UINT i;
1004 if(session->channel_vols)
1005 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1006 session->channel_vols, sizeof(float) * channels);
1007 else
1008 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1009 sizeof(float) * channels);
1010 if(!session->channel_vols)
1011 return;
1013 for(i = session->channel_count; i < channels; ++i)
1014 session->channel_vols[i] = 1.f;
1016 session->channel_count = channels;
1020 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1021 UINT num_channels)
1023 AudioSession *ret;
1025 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1026 if(!ret)
1027 return NULL;
1029 memcpy(&ret->guid, guid, sizeof(GUID));
1031 ret->device = device;
1033 list_init(&ret->clients);
1035 list_add_head(&g_sessions, &ret->entry);
1037 InitializeCriticalSection(&ret->lock);
1038 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1040 session_init_vols(ret, num_channels);
1042 ret->master_vol = 1.f;
1044 return ret;
1047 /* if channels == 0, then this will return or create a session with
1048 * matching dataflow and GUID. otherwise, channels must also match */
1049 static HRESULT get_audio_session(const GUID *sessionguid,
1050 IMMDevice *device, UINT channels, AudioSession **out)
1052 AudioSession *session;
1054 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1055 *out = create_session(&GUID_NULL, device, channels);
1056 if(!*out)
1057 return E_OUTOFMEMORY;
1059 return S_OK;
1062 *out = NULL;
1063 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1064 if(session->device == device &&
1065 IsEqualGUID(sessionguid, &session->guid)){
1066 session_init_vols(session, channels);
1067 *out = session;
1068 break;
1072 if(!*out){
1073 *out = create_session(sessionguid, device, channels);
1074 if(!*out)
1075 return E_OUTOFMEMORY;
1078 return S_OK;
1081 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1082 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1083 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1084 const GUID *sessionguid)
1086 ACImpl *This = impl_from_IAudioClient(iface);
1087 HRESULT hr;
1088 OSStatus sc;
1089 int i;
1091 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1092 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1094 if(!fmt)
1095 return E_POINTER;
1097 dump_fmt(fmt);
1099 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1100 return AUDCLNT_E_NOT_INITIALIZED;
1102 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1103 AUDCLNT_STREAMFLAGS_LOOPBACK |
1104 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1105 AUDCLNT_STREAMFLAGS_NOPERSIST |
1106 AUDCLNT_STREAMFLAGS_RATEADJUST |
1107 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1108 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1109 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1110 TRACE("Unknown flags: %08x\n", flags);
1111 return E_INVALIDARG;
1114 if(mode == AUDCLNT_SHAREMODE_SHARED){
1115 period = DefaultPeriod;
1116 if( duration < 3 * period)
1117 duration = 3 * period;
1118 }else{
1119 if(!period)
1120 period = DefaultPeriod; /* not minimum */
1121 if(period < MinimumPeriod || period > 5000000)
1122 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1123 if(duration > 20000000) /* the smaller the period, the lower this limit */
1124 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1125 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1126 if(duration != period)
1127 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1128 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1129 return AUDCLNT_E_DEVICE_IN_USE;
1130 }else{
1131 if( duration < 8 * period)
1132 duration = 8 * period; /* may grow above 2s */
1136 OSSpinLockLock(&This->lock);
1138 if(This->aqueue){
1139 OSSpinLockUnlock(&This->lock);
1140 return AUDCLNT_E_ALREADY_INITIALIZED;
1143 hr = ca_setup_aqueue(This->adevid, This->dataflow, fmt, This, &This->aqueue);
1144 if(FAILED(hr)){
1145 OSSpinLockUnlock(&This->lock);
1146 return hr;
1149 This->fmt = clone_format(fmt);
1150 if(!This->fmt){
1151 AudioQueueDispose(This->aqueue, 1);
1152 This->aqueue = NULL;
1153 OSSpinLockUnlock(&This->lock);
1154 return E_OUTOFMEMORY;
1157 This->period_ms = period / 10000;
1159 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1161 if(This->dataflow == eCapture){
1162 int i;
1163 UInt32 bsize = ceil((This->bufsize_frames / (double)CAPTURE_BUFFERS) *
1164 This->fmt->nBlockAlign);
1165 for(i = 0; i < CAPTURE_BUFFERS; ++i){
1166 AQBuffer *buf;
1168 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1169 if(!buf){
1170 AudioQueueDispose(This->aqueue, 1);
1171 This->aqueue = NULL;
1172 CoTaskMemFree(This->fmt);
1173 This->fmt = NULL;
1174 OSSpinLockUnlock(&This->lock);
1175 return E_OUTOFMEMORY;
1178 sc = AudioQueueAllocateBuffer(This->aqueue, bsize, &buf->buf);
1179 if(sc != noErr){
1180 AudioQueueDispose(This->aqueue, 1);
1181 This->aqueue = NULL;
1182 CoTaskMemFree(This->fmt);
1183 This->fmt = NULL;
1184 OSSpinLockUnlock(&This->lock);
1185 WARN("Couldn't allocate buffer: %lx\n", sc);
1186 return E_FAIL;
1189 buf->buf->mUserData = buf;
1190 buf->used = TRUE;
1191 sc = AudioQueueEnqueueBuffer(This->aqueue, buf->buf, 0, NULL);
1192 if(sc != noErr){
1193 ERR("Couldn't enqueue buffer: %lx\n", sc);
1194 break;
1196 list_add_tail(&This->queued_buffers, &buf->entry);
1200 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1201 if(!This->vols){
1202 AudioQueueDispose(This->aqueue, 1);
1203 This->aqueue = NULL;
1204 CoTaskMemFree(This->fmt);
1205 This->fmt = NULL;
1206 OSSpinLockUnlock(&This->lock);
1207 return E_OUTOFMEMORY;
1210 for(i = 0; i < fmt->nChannels; ++i)
1211 This->vols[i] = 1.f;
1213 This->share = mode;
1214 This->flags = flags;
1216 EnterCriticalSection(&g_sessions_lock);
1218 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1219 &This->session);
1220 if(FAILED(hr)){
1221 LeaveCriticalSection(&g_sessions_lock);
1222 AudioQueueDispose(This->aqueue, 1);
1223 This->aqueue = NULL;
1224 CoTaskMemFree(This->fmt);
1225 This->fmt = NULL;
1226 HeapFree(GetProcessHeap(), 0, This->vols);
1227 This->vols = NULL;
1228 OSSpinLockUnlock(&This->lock);
1229 return E_INVALIDARG;
1232 list_add_tail(&This->session->clients, &This->entry);
1234 LeaveCriticalSection(&g_sessions_lock);
1236 ca_setvol(This, -1);
1238 OSSpinLockUnlock(&This->lock);
1240 return S_OK;
1243 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1244 UINT32 *frames)
1246 ACImpl *This = impl_from_IAudioClient(iface);
1248 TRACE("(%p)->(%p)\n", This, frames);
1250 if(!frames)
1251 return E_POINTER;
1253 OSSpinLockLock(&This->lock);
1255 if(!This->aqueue){
1256 OSSpinLockUnlock(&This->lock);
1257 return AUDCLNT_E_NOT_INITIALIZED;
1260 *frames = This->bufsize_frames;
1262 OSSpinLockUnlock(&This->lock);
1264 return S_OK;
1267 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1269 AudioObjectPropertyAddress addr;
1270 AudioStreamID *ids;
1271 UInt32 size;
1272 OSStatus sc;
1273 int nstreams, i;
1275 addr.mScope = This->scope;
1276 addr.mElement = 0;
1277 addr.mSelector = kAudioDevicePropertyStreams;
1279 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1280 &size);
1281 if(sc != noErr){
1282 WARN("Unable to get size for _Streams property: %lx\n", sc);
1283 return E_FAIL;
1286 ids = HeapAlloc(GetProcessHeap(), 0, size);
1287 if(!ids)
1288 return E_OUTOFMEMORY;
1290 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1291 if(sc != noErr){
1292 WARN("Unable to get _Streams property: %lx\n", sc);
1293 HeapFree(GetProcessHeap(), 0, ids);
1294 return E_FAIL;
1297 nstreams = size / sizeof(AudioStreamID);
1298 *max = 0;
1300 addr.mSelector = kAudioStreamPropertyLatency;
1301 for(i = 0; i < nstreams; ++i){
1302 UInt32 latency;
1304 size = sizeof(latency);
1305 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1306 &size, &latency);
1307 if(sc != noErr){
1308 WARN("Unable to get _Latency property: %lx\n", sc);
1309 continue;
1312 if(latency > *max)
1313 *max = latency;
1316 HeapFree(GetProcessHeap(), 0, ids);
1318 return S_OK;
1321 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1322 REFERENCE_TIME *out)
1324 ACImpl *This = impl_from_IAudioClient(iface);
1325 UInt32 latency, stream_latency, size;
1326 AudioObjectPropertyAddress addr;
1327 OSStatus sc;
1328 HRESULT hr;
1330 TRACE("(%p)->(%p)\n", This, out);
1332 if(!out)
1333 return E_POINTER;
1335 OSSpinLockLock(&This->lock);
1337 if(!This->aqueue){
1338 OSSpinLockUnlock(&This->lock);
1339 return AUDCLNT_E_NOT_INITIALIZED;
1342 addr.mScope = This->scope;
1343 addr.mSelector = kAudioDevicePropertyLatency;
1344 addr.mElement = 0;
1346 size = sizeof(latency);
1347 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1348 &size, &latency);
1349 if(sc != noErr){
1350 WARN("Couldn't get _Latency property: %lx\n", sc);
1351 OSSpinLockUnlock(&This->lock);
1352 return E_FAIL;
1355 hr = ca_get_max_stream_latency(This, &stream_latency);
1356 if(FAILED(hr)){
1357 OSSpinLockUnlock(&This->lock);
1358 return hr;
1361 latency += stream_latency;
1362 /* pretend we process audio in Period chunks, so max latency includes
1363 * the period time */
1364 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1365 + This->period_ms * 10000;
1367 OSSpinLockUnlock(&This->lock);
1369 return S_OK;
1372 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1373 UINT32 *numpad)
1375 if(!This->aqueue)
1376 return AUDCLNT_E_NOT_INITIALIZED;
1378 avail_update(This);
1380 if(This->dataflow == eRender){
1381 UINT64 bufpos;
1382 bufpos = get_current_aqbuffer_position(This, BUFPOS_RELATIVE);
1383 *numpad = This->inbuf_frames - bufpos;
1384 }else
1385 *numpad = This->inbuf_frames;
1387 return S_OK;
1390 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1391 UINT32 *numpad)
1393 ACImpl *This = impl_from_IAudioClient(iface);
1394 HRESULT hr;
1396 TRACE("(%p)->(%p)\n", This, numpad);
1398 if(!numpad)
1399 return E_POINTER;
1401 OSSpinLockLock(&This->lock);
1403 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1405 OSSpinLockUnlock(&This->lock);
1407 return hr;
1410 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1411 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1412 WAVEFORMATEX **outpwfx)
1414 ACImpl *This = impl_from_IAudioClient(iface);
1415 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1416 AudioQueueRef aqueue;
1417 HRESULT hr;
1419 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1421 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1422 return E_POINTER;
1424 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1425 return E_INVALIDARG;
1427 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1428 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1429 return E_INVALIDARG;
1431 dump_fmt(pwfx);
1433 if(outpwfx)
1434 *outpwfx = NULL;
1436 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1437 fmtex->dwChannelMask != 0 &&
1438 fmtex->dwChannelMask != get_channel_mask(pwfx->nChannels))
1439 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1441 OSSpinLockLock(&This->lock);
1443 hr = ca_setup_aqueue(This->adevid, This->dataflow, pwfx, NULL, &aqueue);
1444 if(SUCCEEDED(hr)){
1445 AudioQueueDispose(aqueue, 1);
1446 OSSpinLockUnlock(&This->lock);
1447 TRACE("returning %08x\n", S_OK);
1448 return S_OK;
1451 OSSpinLockUnlock(&This->lock);
1453 TRACE("returning %08x\n", AUDCLNT_E_UNSUPPORTED_FORMAT);
1454 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1457 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1458 WAVEFORMATEX **pwfx)
1460 ACImpl *This = impl_from_IAudioClient(iface);
1461 WAVEFORMATEXTENSIBLE *fmt;
1462 OSStatus sc;
1463 UInt32 size;
1464 Float64 rate;
1465 AudioBufferList *buffers;
1466 AudioObjectPropertyAddress addr;
1467 int i;
1469 TRACE("(%p)->(%p)\n", This, pwfx);
1471 if(!pwfx)
1472 return E_POINTER;
1473 *pwfx = NULL;
1475 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1476 if(!fmt)
1477 return E_OUTOFMEMORY;
1479 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1481 addr.mScope = This->scope;
1482 addr.mElement = 0;
1483 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1485 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1486 if(sc != noErr){
1487 CoTaskMemFree(fmt);
1488 WARN("Unable to get size for _StreamConfiguration property: %lx\n", sc);
1489 return E_FAIL;
1492 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1493 if(!buffers){
1494 CoTaskMemFree(fmt);
1495 return E_OUTOFMEMORY;
1498 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1499 &size, buffers);
1500 if(sc != noErr){
1501 CoTaskMemFree(fmt);
1502 HeapFree(GetProcessHeap(), 0, buffers);
1503 WARN("Unable to get _StreamConfiguration property: %lx\n", sc);
1504 return E_FAIL;
1507 fmt->Format.nChannels = 0;
1508 for(i = 0; i < buffers->mNumberBuffers; ++i)
1509 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1511 HeapFree(GetProcessHeap(), 0, buffers);
1513 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1515 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1516 size = sizeof(Float64);
1517 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1518 if(sc != noErr){
1519 CoTaskMemFree(fmt);
1520 WARN("Unable to get _NominalSampleRate property: %lx\n", sc);
1521 return E_FAIL;
1523 fmt->Format.nSamplesPerSec = rate;
1525 fmt->Format.wBitsPerSample = 32;
1526 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1528 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1529 fmt->Format.nChannels) / 8;
1530 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1531 fmt->Format.nBlockAlign;
1533 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1534 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1536 *pwfx = (WAVEFORMATEX*)fmt;
1537 dump_fmt(*pwfx);
1539 return S_OK;
1542 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1543 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1545 ACImpl *This = impl_from_IAudioClient(iface);
1547 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1549 if(!defperiod && !minperiod)
1550 return E_POINTER;
1552 if(defperiod)
1553 *defperiod = DefaultPeriod;
1554 if(minperiod)
1555 *minperiod = MinimumPeriod;
1557 return S_OK;
1560 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1562 ACImpl *This = user;
1564 if(This->event)
1565 SetEvent(This->event);
1568 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1570 ACImpl *This = impl_from_IAudioClient(iface);
1571 OSStatus sc;
1573 TRACE("(%p)\n", This);
1575 OSSpinLockLock(&This->lock);
1577 if(!This->aqueue){
1578 OSSpinLockUnlock(&This->lock);
1579 return AUDCLNT_E_NOT_INITIALIZED;
1582 if(This->playing != StateStopped){
1583 OSSpinLockUnlock(&This->lock);
1584 return AUDCLNT_E_NOT_STOPPED;
1587 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1588 OSSpinLockUnlock(&This->lock);
1589 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1592 if(This->event)
1593 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
1594 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
1595 This->timer = NULL;
1596 OSSpinLockUnlock(&This->lock);
1597 WARN("Unable to create timer: %u\n", GetLastError());
1598 return E_OUTOFMEMORY;
1601 if(This->dataflow == eCapture){
1602 UINT32 frames; /* enqueue packets */
1603 AudioCaptureClient_GetNextPacket(This, &frames);
1606 This->playing = StateInTransition;
1608 sc = AudioQueueStart(This->aqueue, NULL);
1609 if(sc != noErr){
1610 OSSpinLockUnlock(&This->lock);
1611 WARN("Unable to start audio queue: %lx\n", sc);
1612 return E_FAIL;
1615 This->playing = StatePlaying;
1617 OSSpinLockUnlock(&This->lock);
1619 return S_OK;
1622 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1624 ACImpl *This = impl_from_IAudioClient(iface);
1625 AudioTimeStamp tstamp;
1626 OSStatus sc;
1627 HANDLE event = NULL;
1628 BOOL wait = FALSE;
1630 TRACE("(%p)\n", This);
1632 OSSpinLockLock(&This->lock);
1634 if(!This->aqueue){
1635 OSSpinLockUnlock(&This->lock);
1636 return AUDCLNT_E_NOT_INITIALIZED;
1639 if(This->playing == StateStopped){
1640 OSSpinLockUnlock(&This->lock);
1641 return S_FALSE;
1644 if(This->playing == StateInTransition){
1645 OSSpinLockUnlock(&This->lock);
1646 return S_OK;
1649 if(This->timer){
1650 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1651 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1652 This->timer = NULL;
1653 if(wait)
1654 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1655 wait = wait && GetLastError() == ERROR_IO_PENDING;
1658 This->playing = StateInTransition;
1660 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
1661 if(sc == noErr){
1662 if(tstamp.mFlags & kAudioTimeStampSampleTimeValid){
1663 if(tstamp.mSampleTime > This->highest_sampletime)
1664 This->highest_sampletime = tstamp.mSampleTime;
1665 }else
1666 WARN("Returned tstamp mSampleTime not valid: %lx\n", tstamp.mFlags);
1667 }else
1668 WARN("GetCurrentTime failed: %lx\n", sc);
1670 /* Mac OS bug? Our capture callback is no more called past AQStop */
1671 sc = AudioQueuePause(This->aqueue);
1672 if(sc != noErr){
1673 OSSpinLockUnlock(&This->lock);
1674 WARN("Unable to pause audio queue: %lx\n", sc);
1675 return E_FAIL;
1678 This->playing = StateStopped;
1680 OSSpinLockUnlock(&This->lock);
1682 if(event && wait)
1683 WaitForSingleObject(event, INFINITE);
1684 CloseHandle(event);
1686 return S_OK;
1689 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1691 ACImpl *This = impl_from_IAudioClient(iface);
1692 OSStatus sc;
1693 QueuedBufInfo *bufinfo, *bufinfo2;
1694 AQBuffer *buf;
1696 TRACE("(%p)\n", This);
1698 OSSpinLockLock(&This->lock);
1700 if(!This->aqueue){
1701 OSSpinLockUnlock(&This->lock);
1702 return AUDCLNT_E_NOT_INITIALIZED;
1705 if(This->playing != StateStopped){
1706 OSSpinLockUnlock(&This->lock);
1707 return AUDCLNT_E_NOT_STOPPED;
1710 if(This->getbuf_last){
1711 OSSpinLockUnlock(&This->lock);
1712 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1715 avail_update(This); /* going to skip over inbuf_frames */
1717 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
1718 QueuedBufInfo, entry){
1719 list_remove(&bufinfo->entry);
1720 HeapFree(GetProcessHeap(), 0, bufinfo);
1723 sc = AudioQueueReset(This->aqueue);
1724 if(sc != noErr){
1725 OSSpinLockUnlock(&This->lock);
1726 WARN("Unable to reset audio queue: %lx\n", sc);
1727 return E_FAIL;
1730 /* AQReset is synchronous */
1731 list_move_tail(&This->avail_buffers, &This->queued_buffers);
1733 if(This->dataflow == eRender){
1734 This->written_frames = 0;
1735 }else{
1736 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry)
1737 buf->buf->mAudioDataByteSize = 0;
1738 This->written_frames += This->inbuf_frames;
1740 This->inbuf_frames = 0;
1742 OSSpinLockUnlock(&This->lock);
1744 return S_OK;
1747 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1748 HANDLE event)
1750 ACImpl *This = impl_from_IAudioClient(iface);
1752 TRACE("(%p)->(%p)\n", This, event);
1754 if(!event)
1755 return E_INVALIDARG;
1757 OSSpinLockLock(&This->lock);
1759 if(!This->aqueue){
1760 OSSpinLockUnlock(&This->lock);
1761 return AUDCLNT_E_NOT_INITIALIZED;
1764 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1765 OSSpinLockUnlock(&This->lock);
1766 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1769 This->event = event;
1771 OSSpinLockUnlock(&This->lock);
1773 return S_OK;
1776 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1777 void **ppv)
1779 ACImpl *This = impl_from_IAudioClient(iface);
1781 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1783 if(!ppv)
1784 return E_POINTER;
1785 *ppv = NULL;
1787 OSSpinLockLock(&This->lock);
1789 if(!This->aqueue){
1790 OSSpinLockUnlock(&This->lock);
1791 return AUDCLNT_E_NOT_INITIALIZED;
1794 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1795 if(This->dataflow != eRender){
1796 OSSpinLockUnlock(&This->lock);
1797 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1799 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1800 *ppv = &This->IAudioRenderClient_iface;
1801 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1802 if(This->dataflow != eCapture){
1803 OSSpinLockUnlock(&This->lock);
1804 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1806 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1807 *ppv = &This->IAudioCaptureClient_iface;
1808 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1809 IAudioClock_AddRef(&This->IAudioClock_iface);
1810 *ppv = &This->IAudioClock_iface;
1811 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1812 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1813 *ppv = &This->IAudioStreamVolume_iface;
1814 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1815 if(!This->session_wrapper){
1816 This->session_wrapper = AudioSessionWrapper_Create(This);
1817 if(!This->session_wrapper){
1818 OSSpinLockUnlock(&This->lock);
1819 return E_OUTOFMEMORY;
1821 }else
1822 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1824 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1825 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1826 if(!This->session_wrapper){
1827 This->session_wrapper = AudioSessionWrapper_Create(This);
1828 if(!This->session_wrapper){
1829 OSSpinLockUnlock(&This->lock);
1830 return E_OUTOFMEMORY;
1832 }else
1833 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1835 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1836 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1837 if(!This->session_wrapper){
1838 This->session_wrapper = AudioSessionWrapper_Create(This);
1839 if(!This->session_wrapper){
1840 OSSpinLockUnlock(&This->lock);
1841 return E_OUTOFMEMORY;
1843 }else
1844 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1846 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1849 if(*ppv){
1850 OSSpinLockUnlock(&This->lock);
1851 return S_OK;
1854 OSSpinLockUnlock(&This->lock);
1856 FIXME("stub %s\n", debugstr_guid(riid));
1857 return E_NOINTERFACE;
1860 static const IAudioClientVtbl AudioClient_Vtbl =
1862 AudioClient_QueryInterface,
1863 AudioClient_AddRef,
1864 AudioClient_Release,
1865 AudioClient_Initialize,
1866 AudioClient_GetBufferSize,
1867 AudioClient_GetStreamLatency,
1868 AudioClient_GetCurrentPadding,
1869 AudioClient_IsFormatSupported,
1870 AudioClient_GetMixFormat,
1871 AudioClient_GetDevicePeriod,
1872 AudioClient_Start,
1873 AudioClient_Stop,
1874 AudioClient_Reset,
1875 AudioClient_SetEventHandle,
1876 AudioClient_GetService
1879 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1880 IAudioRenderClient *iface, REFIID riid, void **ppv)
1882 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1884 if(!ppv)
1885 return E_POINTER;
1886 *ppv = NULL;
1888 if(IsEqualIID(riid, &IID_IUnknown) ||
1889 IsEqualIID(riid, &IID_IAudioRenderClient))
1890 *ppv = iface;
1891 if(*ppv){
1892 IUnknown_AddRef((IUnknown*)*ppv);
1893 return S_OK;
1896 WARN("Unknown interface %s\n", debugstr_guid(riid));
1897 return E_NOINTERFACE;
1900 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1902 ACImpl *This = impl_from_IAudioRenderClient(iface);
1903 return AudioClient_AddRef(&This->IAudioClient_iface);
1906 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1908 ACImpl *This = impl_from_IAudioRenderClient(iface);
1909 return AudioClient_Release(&This->IAudioClient_iface);
1912 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1913 UINT32 frames, BYTE **data)
1915 ACImpl *This = impl_from_IAudioRenderClient(iface);
1916 AQBuffer *buf;
1917 UINT32 pad, bytes;
1918 HRESULT hr;
1919 OSStatus sc;
1921 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1923 if(!data)
1924 return E_POINTER;
1925 *data = NULL;
1927 OSSpinLockLock(&This->lock);
1929 if(This->getbuf_last){
1930 OSSpinLockUnlock(&This->lock);
1931 return AUDCLNT_E_OUT_OF_ORDER;
1934 if(!frames){
1935 OSSpinLockUnlock(&This->lock);
1936 return S_OK;
1939 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
1940 if(FAILED(hr)){
1941 OSSpinLockUnlock(&This->lock);
1942 return hr;
1945 if(pad + frames > This->bufsize_frames){
1946 OSSpinLockUnlock(&This->lock);
1947 return AUDCLNT_E_BUFFER_TOO_LARGE;
1950 bytes = frames * This->fmt->nBlockAlign;
1951 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry){
1952 if(buf->buf->mAudioDataBytesCapacity >= bytes){
1953 This->public_buffer = buf->buf;
1954 list_remove(&buf->entry);
1955 break;
1959 if(&buf->entry == &This->avail_buffers){
1960 sc = AudioQueueAllocateBuffer(This->aqueue, bytes,
1961 &This->public_buffer);
1962 if(sc != noErr){
1963 This->public_buffer = NULL;
1964 OSSpinLockUnlock(&This->lock);
1965 WARN("Unable to allocate buffer: %lx\n", sc);
1966 return E_OUTOFMEMORY;
1968 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1969 if(!buf){
1970 AudioQueueFreeBuffer(This->aqueue, This->public_buffer);
1971 This->public_buffer = NULL;
1972 OSSpinLockUnlock(&This->lock);
1973 return E_OUTOFMEMORY;
1975 buf->used = FALSE;
1976 buf->buf = This->public_buffer;
1977 This->public_buffer->mUserData = buf;
1980 This->getbuf_last = frames;
1981 *data = This->public_buffer->mAudioData;
1983 OSSpinLockUnlock(&This->lock);
1985 return S_OK;
1988 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1989 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
1991 ACImpl *This = impl_from_IAudioRenderClient(iface);
1992 AQBuffer *buf;
1993 AudioTimeStamp start_time, req_time = {0}, *passed_time = NULL;
1994 OSStatus sc;
1996 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
1998 OSSpinLockLock(&This->lock);
2000 if(!frames){
2001 This->getbuf_last = 0;
2002 if(This->public_buffer){
2003 buf = This->public_buffer->mUserData;
2004 list_add_head(&This->avail_buffers, &buf->entry);
2005 This->public_buffer = NULL;
2007 OSSpinLockUnlock(&This->lock);
2008 return S_OK;
2011 if(!This->getbuf_last){
2012 OSSpinLockUnlock(&This->lock);
2013 return AUDCLNT_E_OUT_OF_ORDER;
2016 if(frames > This->getbuf_last){
2017 OSSpinLockUnlock(&This->lock);
2018 return AUDCLNT_E_INVALID_SIZE;
2021 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2022 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
2023 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
2024 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
2025 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
2026 This->fmt->wBitsPerSample == 8)
2027 memset(This->public_buffer->mAudioData, 128,
2028 frames * This->fmt->nBlockAlign);
2029 else
2030 memset(This->public_buffer->mAudioData, 0,
2031 frames * This->fmt->nBlockAlign);
2034 This->public_buffer->mAudioDataByteSize = frames * This->fmt->nBlockAlign;
2036 buf = This->public_buffer->mUserData;
2037 buf->used = TRUE;
2039 if(list_empty(&This->queued_bufinfos)){
2040 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &req_time, NULL);
2041 if(sc == noErr)
2042 passed_time = &req_time;
2043 else
2044 TRACE("AudioQueueGetCurrentTime failed: %lx\n", sc);
2045 }else{
2046 req_time.mSampleTime = This->next_sampletime;
2047 req_time.mFlags = kAudioTimeStampSampleTimeValid;
2048 passed_time = &req_time;
2051 sc = AudioQueueEnqueueBufferWithParameters(This->aqueue,
2052 This->public_buffer, 0, NULL, 0, 0, 0, NULL, passed_time,
2053 &start_time);
2054 if(sc != noErr){
2055 OSSpinLockUnlock(&This->lock);
2056 ERR("Unable to enqueue buffer: %lx\n", sc);
2057 return AUDCLNT_E_DEVICE_INVALIDATED;
2059 list_add_tail(&This->queued_buffers, &buf->entry);
2061 if(start_time.mFlags & kAudioTimeStampSampleTimeValid){
2062 QueuedBufInfo *bufinfo;
2064 bufinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*bufinfo));
2065 bufinfo->start_sampletime = start_time.mSampleTime;
2066 bufinfo->start_pos = This->written_frames;
2067 bufinfo->len_frames = frames;
2069 list_add_tail(&This->queued_bufinfos, &bufinfo->entry);
2071 This->next_sampletime = start_time.mSampleTime + bufinfo->len_frames;
2072 }else
2073 WARN("Start time didn't contain valid SampleTime member\n");
2075 if(This->playing == StateStopped)
2076 AudioQueuePrime(This->aqueue, 0, NULL);
2078 This->public_buffer = NULL;
2079 This->getbuf_last = 0;
2080 This->written_frames += frames;
2081 This->inbuf_frames += frames;
2083 OSSpinLockUnlock(&This->lock);
2085 return S_OK;
2088 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2089 AudioRenderClient_QueryInterface,
2090 AudioRenderClient_AddRef,
2091 AudioRenderClient_Release,
2092 AudioRenderClient_GetBuffer,
2093 AudioRenderClient_ReleaseBuffer
2096 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2097 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2099 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2101 if(!ppv)
2102 return E_POINTER;
2103 *ppv = NULL;
2105 if(IsEqualIID(riid, &IID_IUnknown) ||
2106 IsEqualIID(riid, &IID_IAudioCaptureClient))
2107 *ppv = iface;
2108 if(*ppv){
2109 IUnknown_AddRef((IUnknown*)*ppv);
2110 return S_OK;
2113 WARN("Unknown interface %s\n", debugstr_guid(riid));
2114 return E_NOINTERFACE;
2117 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2119 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2120 return IAudioClient_AddRef(&This->IAudioClient_iface);
2123 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2125 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2126 return IAudioClient_Release(&This->IAudioClient_iface);
2129 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames)
2131 AQBuffer *buf;
2132 OSStatus sc;
2134 avail_update(This); /* once, not inside loop */
2136 for(;;){
2137 if(!This->public_buffer){
2138 struct list *head = list_head(&This->avail_buffers);
2140 if(!head){
2141 *frames = 0;
2142 return S_OK;
2144 buf = LIST_ENTRY(head, AQBuffer, entry);
2145 This->public_buffer = buf->buf;
2146 list_remove(&buf->entry);
2147 }else
2148 buf = This->public_buffer->mUserData;
2149 *frames = This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
2150 if(*frames)
2151 return S_OK;
2153 WARN("empty packet\n");
2154 buf->used = TRUE;
2155 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2156 if(sc != noErr){
2157 ERR("Unable to enqueue buffer: %lx\n", sc);
2158 /* Release will free This->public_buffer */
2159 return AUDCLNT_E_DEVICE_INVALIDATED;
2160 }else
2161 list_add_tail(&This->queued_buffers, &buf->entry);
2162 This->public_buffer = NULL;
2166 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2167 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2168 UINT64 *qpcpos)
2170 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2171 HRESULT hr;
2173 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2174 devpos, qpcpos);
2176 if(!data || !frames || !flags)
2177 return E_POINTER;
2179 OSSpinLockLock(&This->lock);
2181 if(This->getbuf_last){
2182 OSSpinLockUnlock(&This->lock);
2183 return AUDCLNT_E_OUT_OF_ORDER;
2186 hr = AudioCaptureClient_GetNextPacket(This, frames);
2187 if(FAILED(hr)){
2188 OSSpinLockUnlock(&This->lock);
2189 return hr;
2192 if((This->getbuf_last = *frames)){
2193 *flags = 0;
2194 *data = This->public_buffer->mAudioData;
2196 if(devpos)
2197 *devpos = This->written_frames;
2198 if(qpcpos){ /* fixme: qpc of recording time */
2199 LARGE_INTEGER stamp, freq;
2200 QueryPerformanceCounter(&stamp);
2201 QueryPerformanceFrequency(&freq);
2202 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2205 OSSpinLockUnlock(&This->lock);
2207 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2210 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2211 IAudioCaptureClient *iface, UINT32 done)
2213 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2214 AQBuffer *buf;
2215 OSStatus sc;
2217 TRACE("(%p)->(%u)\n", This, done);
2219 OSSpinLockLock(&This->lock);
2221 if(!done){
2222 This->getbuf_last = 0;
2223 OSSpinLockUnlock(&This->lock);
2224 return S_OK;
2227 if(!This->getbuf_last){
2228 OSSpinLockUnlock(&This->lock);
2229 return AUDCLNT_E_OUT_OF_ORDER;
2232 if(This->getbuf_last != done){
2233 OSSpinLockUnlock(&This->lock);
2234 return AUDCLNT_E_INVALID_SIZE;
2237 This->written_frames += done;
2238 This->inbuf_frames -= done;
2239 This->getbuf_last = 0;
2241 buf = This->public_buffer->mUserData;
2242 buf->used = TRUE;
2243 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2244 if(sc != noErr){
2245 OSSpinLockUnlock(&This->lock);
2246 /* fixme: can't zero public_buffer or we lose memory, but then
2247 * GetBuffer will see that packet again and again. */
2248 ERR("Unable to enqueue buffer: %lx\n", sc);
2249 return AUDCLNT_E_DEVICE_INVALIDATED;
2250 }else
2251 list_add_tail(&This->queued_buffers, &buf->entry);
2252 This->public_buffer = NULL;
2254 OSSpinLockUnlock(&This->lock);
2256 return S_OK;
2259 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2260 IAudioCaptureClient *iface, UINT32 *frames)
2262 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2263 HRESULT hr;
2265 TRACE("(%p)->(%p)\n", This, frames);
2267 if(!frames)
2268 return E_POINTER;
2270 OSSpinLockLock(&This->lock);
2272 hr = AudioCaptureClient_GetNextPacket(This, frames);
2274 OSSpinLockUnlock(&This->lock);
2276 return hr;
2279 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2281 AudioCaptureClient_QueryInterface,
2282 AudioCaptureClient_AddRef,
2283 AudioCaptureClient_Release,
2284 AudioCaptureClient_GetBuffer,
2285 AudioCaptureClient_ReleaseBuffer,
2286 AudioCaptureClient_GetNextPacketSize
2289 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2290 REFIID riid, void **ppv)
2292 ACImpl *This = impl_from_IAudioClock(iface);
2294 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2296 if(!ppv)
2297 return E_POINTER;
2298 *ppv = NULL;
2300 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2301 *ppv = iface;
2302 else if(IsEqualIID(riid, &IID_IAudioClock2))
2303 *ppv = &This->IAudioClock2_iface;
2304 if(*ppv){
2305 IUnknown_AddRef((IUnknown*)*ppv);
2306 return S_OK;
2309 WARN("Unknown interface %s\n", debugstr_guid(riid));
2310 return E_NOINTERFACE;
2313 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2315 ACImpl *This = impl_from_IAudioClock(iface);
2316 return IAudioClient_AddRef(&This->IAudioClient_iface);
2319 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2321 ACImpl *This = impl_from_IAudioClock(iface);
2322 return IAudioClient_Release(&This->IAudioClient_iface);
2325 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2327 ACImpl *This = impl_from_IAudioClock(iface);
2329 TRACE("(%p)->(%p)\n", This, freq);
2331 *freq = This->fmt->nSamplesPerSec;
2333 return S_OK;
2336 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2337 UINT64 *pos, UINT64 *qpctime)
2339 avail_update(This);
2341 if(This->dataflow == eRender)
2342 *pos = get_current_aqbuffer_position(This, BUFPOS_ABSOLUTE);
2343 else
2344 *pos = This->inbuf_frames + This->written_frames;
2346 if(qpctime){
2347 LARGE_INTEGER stamp, freq;
2348 QueryPerformanceCounter(&stamp);
2349 QueryPerformanceFrequency(&freq);
2350 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2353 return S_OK;
2356 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2357 UINT64 *qpctime)
2359 ACImpl *This = impl_from_IAudioClock(iface);
2360 HRESULT hr;
2362 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2364 if(!pos)
2365 return E_POINTER;
2367 OSSpinLockLock(&This->lock);
2369 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2371 OSSpinLockUnlock(&This->lock);
2373 return hr;
2376 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2377 DWORD *chars)
2379 ACImpl *This = impl_from_IAudioClock(iface);
2381 TRACE("(%p)->(%p)\n", This, chars);
2383 if(!chars)
2384 return E_POINTER;
2386 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2388 return S_OK;
2391 static const IAudioClockVtbl AudioClock_Vtbl =
2393 AudioClock_QueryInterface,
2394 AudioClock_AddRef,
2395 AudioClock_Release,
2396 AudioClock_GetFrequency,
2397 AudioClock_GetPosition,
2398 AudioClock_GetCharacteristics
2401 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2402 REFIID riid, void **ppv)
2404 ACImpl *This = impl_from_IAudioClock2(iface);
2405 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2408 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2410 ACImpl *This = impl_from_IAudioClock2(iface);
2411 return IAudioClient_AddRef(&This->IAudioClient_iface);
2414 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2416 ACImpl *This = impl_from_IAudioClock2(iface);
2417 return IAudioClient_Release(&This->IAudioClient_iface);
2420 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2421 UINT64 *pos, UINT64 *qpctime)
2423 ACImpl *This = impl_from_IAudioClock2(iface);
2425 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2427 return E_NOTIMPL;
2430 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2432 AudioClock2_QueryInterface,
2433 AudioClock2_AddRef,
2434 AudioClock2_Release,
2435 AudioClock2_GetDevicePosition
2438 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2440 AudioSessionWrapper *ret;
2442 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2443 sizeof(AudioSessionWrapper));
2444 if(!ret)
2445 return NULL;
2447 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2448 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2449 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2451 ret->ref = 1;
2453 ret->client = client;
2454 if(client){
2455 ret->session = client->session;
2456 AudioClient_AddRef(&client->IAudioClient_iface);
2459 return ret;
2462 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2463 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2465 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2467 if(!ppv)
2468 return E_POINTER;
2469 *ppv = NULL;
2471 if(IsEqualIID(riid, &IID_IUnknown) ||
2472 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2473 IsEqualIID(riid, &IID_IAudioSessionControl2))
2474 *ppv = iface;
2475 if(*ppv){
2476 IUnknown_AddRef((IUnknown*)*ppv);
2477 return S_OK;
2480 WARN("Unknown interface %s\n", debugstr_guid(riid));
2481 return E_NOINTERFACE;
2484 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2486 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2487 ULONG ref;
2488 ref = InterlockedIncrement(&This->ref);
2489 TRACE("(%p) Refcount now %u\n", This, ref);
2490 return ref;
2493 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2495 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2496 ULONG ref;
2497 ref = InterlockedDecrement(&This->ref);
2498 TRACE("(%p) Refcount now %u\n", This, ref);
2499 if(!ref){
2500 if(This->client){
2501 OSSpinLockLock(&This->client->lock);
2502 This->client->session_wrapper = NULL;
2503 OSSpinLockUnlock(&This->client->lock);
2504 AudioClient_Release(&This->client->IAudioClient_iface);
2506 HeapFree(GetProcessHeap(), 0, This);
2508 return ref;
2511 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2512 AudioSessionState *state)
2514 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2515 ACImpl *client;
2517 TRACE("(%p)->(%p)\n", This, state);
2519 if(!state)
2520 return NULL_PTR_ERR;
2522 EnterCriticalSection(&g_sessions_lock);
2524 if(list_empty(&This->session->clients)){
2525 *state = AudioSessionStateExpired;
2526 LeaveCriticalSection(&g_sessions_lock);
2527 return S_OK;
2530 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2531 OSSpinLockLock(&client->lock);
2532 if(client->playing == StatePlaying ||
2533 client->playing == StateInTransition){
2534 *state = AudioSessionStateActive;
2535 OSSpinLockUnlock(&client->lock);
2536 LeaveCriticalSection(&g_sessions_lock);
2537 return S_OK;
2539 OSSpinLockUnlock(&client->lock);
2542 LeaveCriticalSection(&g_sessions_lock);
2544 *state = AudioSessionStateInactive;
2546 return S_OK;
2549 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2550 IAudioSessionControl2 *iface, WCHAR **name)
2552 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2554 FIXME("(%p)->(%p) - stub\n", This, name);
2556 return E_NOTIMPL;
2559 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2560 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2562 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2564 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2566 return E_NOTIMPL;
2569 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2570 IAudioSessionControl2 *iface, WCHAR **path)
2572 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2574 FIXME("(%p)->(%p) - stub\n", This, path);
2576 return E_NOTIMPL;
2579 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2580 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2582 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2584 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2586 return E_NOTIMPL;
2589 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2590 IAudioSessionControl2 *iface, GUID *group)
2592 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2594 FIXME("(%p)->(%p) - stub\n", This, group);
2596 return E_NOTIMPL;
2599 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2600 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2602 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2604 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2605 debugstr_guid(session));
2607 return E_NOTIMPL;
2610 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2611 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2613 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2615 FIXME("(%p)->(%p) - stub\n", This, events);
2617 return S_OK;
2620 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2621 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2623 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2625 FIXME("(%p)->(%p) - stub\n", This, events);
2627 return S_OK;
2630 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2631 IAudioSessionControl2 *iface, WCHAR **id)
2633 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2635 FIXME("(%p)->(%p) - stub\n", This, id);
2637 return E_NOTIMPL;
2640 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2641 IAudioSessionControl2 *iface, WCHAR **id)
2643 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2645 FIXME("(%p)->(%p) - stub\n", This, id);
2647 return E_NOTIMPL;
2650 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2651 IAudioSessionControl2 *iface, DWORD *pid)
2653 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2655 TRACE("(%p)->(%p)\n", This, pid);
2657 if(!pid)
2658 return E_POINTER;
2660 *pid = GetCurrentProcessId();
2662 return S_OK;
2665 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2666 IAudioSessionControl2 *iface)
2668 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2670 TRACE("(%p)\n", This);
2672 return S_FALSE;
2675 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2676 IAudioSessionControl2 *iface, BOOL optout)
2678 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2680 TRACE("(%p)->(%d)\n", This, optout);
2682 return S_OK;
2685 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2687 AudioSessionControl_QueryInterface,
2688 AudioSessionControl_AddRef,
2689 AudioSessionControl_Release,
2690 AudioSessionControl_GetState,
2691 AudioSessionControl_GetDisplayName,
2692 AudioSessionControl_SetDisplayName,
2693 AudioSessionControl_GetIconPath,
2694 AudioSessionControl_SetIconPath,
2695 AudioSessionControl_GetGroupingParam,
2696 AudioSessionControl_SetGroupingParam,
2697 AudioSessionControl_RegisterAudioSessionNotification,
2698 AudioSessionControl_UnregisterAudioSessionNotification,
2699 AudioSessionControl_GetSessionIdentifier,
2700 AudioSessionControl_GetSessionInstanceIdentifier,
2701 AudioSessionControl_GetProcessId,
2702 AudioSessionControl_IsSystemSoundsSession,
2703 AudioSessionControl_SetDuckingPreference
2706 /* index == -1 means set all channels, otherwise sets only the given channel */
2707 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2709 float level;
2710 OSStatus sc;
2712 if(index == (UINT32)-1){
2713 HRESULT ret = S_OK;
2714 UINT32 i;
2715 for(i = 0; i < This->fmt->nChannels; ++i){
2716 HRESULT hr;
2717 hr = ca_setvol(This, i);
2718 if(FAILED(hr))
2719 ret = hr;
2721 return ret;
2724 if(This->session->mute)
2725 level = 0;
2726 else
2727 level = This->session->master_vol *
2728 This->session->channel_vols[index] * This->vols[index];
2730 sc = AudioQueueSetParameter(This->aqueue, kAudioQueueParam_Volume, level);
2731 if(sc != noErr)
2732 WARN("Setting _Volume property failed: %lx\n", sc);
2734 return S_OK;
2737 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
2739 HRESULT ret = S_OK;
2740 ACImpl *client;
2742 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2743 HRESULT hr;
2744 hr = ca_setvol(client, index);
2745 if(FAILED(hr))
2746 ret = hr;
2749 return ret;
2752 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2753 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2755 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2757 if(!ppv)
2758 return E_POINTER;
2759 *ppv = NULL;
2761 if(IsEqualIID(riid, &IID_IUnknown) ||
2762 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2763 *ppv = iface;
2764 if(*ppv){
2765 IUnknown_AddRef((IUnknown*)*ppv);
2766 return S_OK;
2769 WARN("Unknown interface %s\n", debugstr_guid(riid));
2770 return E_NOINTERFACE;
2773 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2775 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2776 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2779 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2781 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2782 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2785 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2786 ISimpleAudioVolume *iface, float level, const GUID *context)
2788 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2789 AudioSession *session = This->session;
2790 HRESULT ret;
2792 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2794 if(level < 0.f || level > 1.f)
2795 return E_INVALIDARG;
2797 if(context)
2798 FIXME("Notifications not supported yet\n");
2800 EnterCriticalSection(&session->lock);
2802 session->master_vol = level;
2804 ret = ca_session_setvol(session, -1);
2806 LeaveCriticalSection(&session->lock);
2808 return ret;
2811 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2812 ISimpleAudioVolume *iface, float *level)
2814 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2815 AudioSession *session = This->session;
2817 TRACE("(%p)->(%p)\n", session, level);
2819 if(!level)
2820 return NULL_PTR_ERR;
2822 *level = session->master_vol;
2824 return S_OK;
2827 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2828 BOOL mute, const GUID *context)
2830 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2831 AudioSession *session = This->session;
2833 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2835 if(context)
2836 FIXME("Notifications not supported yet\n");
2838 EnterCriticalSection(&session->lock);
2840 session->mute = mute;
2842 ca_session_setvol(session, -1);
2844 LeaveCriticalSection(&session->lock);
2846 return S_OK;
2849 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2850 BOOL *mute)
2852 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2853 AudioSession *session = This->session;
2855 TRACE("(%p)->(%p)\n", session, mute);
2857 if(!mute)
2858 return NULL_PTR_ERR;
2860 *mute = session->mute;
2862 return S_OK;
2865 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2867 SimpleAudioVolume_QueryInterface,
2868 SimpleAudioVolume_AddRef,
2869 SimpleAudioVolume_Release,
2870 SimpleAudioVolume_SetMasterVolume,
2871 SimpleAudioVolume_GetMasterVolume,
2872 SimpleAudioVolume_SetMute,
2873 SimpleAudioVolume_GetMute
2876 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2877 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2879 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2881 if(!ppv)
2882 return E_POINTER;
2883 *ppv = NULL;
2885 if(IsEqualIID(riid, &IID_IUnknown) ||
2886 IsEqualIID(riid, &IID_IAudioStreamVolume))
2887 *ppv = iface;
2888 if(*ppv){
2889 IUnknown_AddRef((IUnknown*)*ppv);
2890 return S_OK;
2893 WARN("Unknown interface %s\n", debugstr_guid(riid));
2894 return E_NOINTERFACE;
2897 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2899 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2900 return IAudioClient_AddRef(&This->IAudioClient_iface);
2903 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2905 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2906 return IAudioClient_Release(&This->IAudioClient_iface);
2909 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2910 IAudioStreamVolume *iface, UINT32 *out)
2912 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2914 TRACE("(%p)->(%p)\n", This, out);
2916 if(!out)
2917 return E_POINTER;
2919 *out = This->fmt->nChannels;
2921 return S_OK;
2924 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2925 IAudioStreamVolume *iface, UINT32 index, float level)
2927 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2928 HRESULT ret;
2930 TRACE("(%p)->(%d, %f)\n", This, index, level);
2932 if(level < 0.f || level > 1.f)
2933 return E_INVALIDARG;
2935 if(index >= This->fmt->nChannels)
2936 return E_INVALIDARG;
2938 OSSpinLockLock(&This->lock);
2940 This->vols[index] = level;
2942 WARN("AudioQueue doesn't support per-channel volume control\n");
2943 ret = ca_setvol(This, index);
2945 OSSpinLockUnlock(&This->lock);
2947 return ret;
2950 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2951 IAudioStreamVolume *iface, UINT32 index, float *level)
2953 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2955 TRACE("(%p)->(%d, %p)\n", This, index, level);
2957 if(!level)
2958 return E_POINTER;
2960 if(index >= This->fmt->nChannels)
2961 return E_INVALIDARG;
2963 *level = This->vols[index];
2965 return S_OK;
2968 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2969 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2971 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2972 int i;
2973 HRESULT ret;
2975 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2977 if(!levels)
2978 return E_POINTER;
2980 if(count != This->fmt->nChannels)
2981 return E_INVALIDARG;
2983 OSSpinLockLock(&This->lock);
2985 for(i = 0; i < count; ++i)
2986 This->vols[i] = levels[i];
2988 ret = ca_setvol(This, -1);
2990 OSSpinLockUnlock(&This->lock);
2992 return ret;
2995 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2996 IAudioStreamVolume *iface, UINT32 count, float *levels)
2998 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2999 int i;
3001 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3003 if(!levels)
3004 return E_POINTER;
3006 if(count != This->fmt->nChannels)
3007 return E_INVALIDARG;
3009 OSSpinLockLock(&This->lock);
3011 for(i = 0; i < count; ++i)
3012 levels[i] = This->vols[i];
3014 OSSpinLockUnlock(&This->lock);
3016 return S_OK;
3019 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3021 AudioStreamVolume_QueryInterface,
3022 AudioStreamVolume_AddRef,
3023 AudioStreamVolume_Release,
3024 AudioStreamVolume_GetChannelCount,
3025 AudioStreamVolume_SetChannelVolume,
3026 AudioStreamVolume_GetChannelVolume,
3027 AudioStreamVolume_SetAllVolumes,
3028 AudioStreamVolume_GetAllVolumes
3031 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3032 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3034 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3036 if(!ppv)
3037 return E_POINTER;
3038 *ppv = NULL;
3040 if(IsEqualIID(riid, &IID_IUnknown) ||
3041 IsEqualIID(riid, &IID_IChannelAudioVolume))
3042 *ppv = iface;
3043 if(*ppv){
3044 IUnknown_AddRef((IUnknown*)*ppv);
3045 return S_OK;
3048 WARN("Unknown interface %s\n", debugstr_guid(riid));
3049 return E_NOINTERFACE;
3052 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3054 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3055 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3058 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3060 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3061 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3064 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3065 IChannelAudioVolume *iface, UINT32 *out)
3067 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3068 AudioSession *session = This->session;
3070 TRACE("(%p)->(%p)\n", session, out);
3072 if(!out)
3073 return NULL_PTR_ERR;
3075 *out = session->channel_count;
3077 return S_OK;
3080 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3081 IChannelAudioVolume *iface, UINT32 index, float level,
3082 const GUID *context)
3084 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3085 AudioSession *session = This->session;
3086 HRESULT ret;
3088 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3089 wine_dbgstr_guid(context));
3091 if(level < 0.f || level > 1.f)
3092 return E_INVALIDARG;
3094 if(index >= session->channel_count)
3095 return E_INVALIDARG;
3097 if(context)
3098 FIXME("Notifications not supported yet\n");
3100 EnterCriticalSection(&session->lock);
3102 session->channel_vols[index] = level;
3104 WARN("AudioQueue doesn't support per-channel volume control\n");
3105 ret = ca_session_setvol(session, index);
3107 LeaveCriticalSection(&session->lock);
3109 return ret;
3112 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3113 IChannelAudioVolume *iface, UINT32 index, float *level)
3115 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3116 AudioSession *session = This->session;
3118 TRACE("(%p)->(%d, %p)\n", session, index, level);
3120 if(!level)
3121 return NULL_PTR_ERR;
3123 if(index >= session->channel_count)
3124 return E_INVALIDARG;
3126 *level = session->channel_vols[index];
3128 return S_OK;
3131 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3132 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3133 const GUID *context)
3135 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3136 AudioSession *session = This->session;
3137 int i;
3138 HRESULT ret;
3140 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3141 wine_dbgstr_guid(context));
3143 if(!levels)
3144 return NULL_PTR_ERR;
3146 if(count != session->channel_count)
3147 return E_INVALIDARG;
3149 if(context)
3150 FIXME("Notifications not supported yet\n");
3152 EnterCriticalSection(&session->lock);
3154 for(i = 0; i < count; ++i)
3155 session->channel_vols[i] = levels[i];
3157 ret = ca_session_setvol(session, -1);
3159 LeaveCriticalSection(&session->lock);
3161 return ret;
3164 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3165 IChannelAudioVolume *iface, UINT32 count, float *levels)
3167 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3168 AudioSession *session = This->session;
3169 int i;
3171 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3173 if(!levels)
3174 return NULL_PTR_ERR;
3176 if(count != session->channel_count)
3177 return E_INVALIDARG;
3179 for(i = 0; i < count; ++i)
3180 levels[i] = session->channel_vols[i];
3182 return S_OK;
3185 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3187 ChannelAudioVolume_QueryInterface,
3188 ChannelAudioVolume_AddRef,
3189 ChannelAudioVolume_Release,
3190 ChannelAudioVolume_GetChannelCount,
3191 ChannelAudioVolume_SetChannelVolume,
3192 ChannelAudioVolume_GetChannelVolume,
3193 ChannelAudioVolume_SetAllVolumes,
3194 ChannelAudioVolume_GetAllVolumes
3197 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3198 REFIID riid, void **ppv)
3200 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3202 if(!ppv)
3203 return E_POINTER;
3204 *ppv = NULL;
3206 if(IsEqualIID(riid, &IID_IUnknown) ||
3207 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3208 IsEqualIID(riid, &IID_IAudioSessionManager2))
3209 *ppv = iface;
3210 if(*ppv){
3211 IUnknown_AddRef((IUnknown*)*ppv);
3212 return S_OK;
3215 WARN("Unknown interface %s\n", debugstr_guid(riid));
3216 return E_NOINTERFACE;
3219 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3221 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3222 ULONG ref;
3223 ref = InterlockedIncrement(&This->ref);
3224 TRACE("(%p) Refcount now %u\n", This, ref);
3225 return ref;
3228 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3230 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3231 ULONG ref;
3232 ref = InterlockedDecrement(&This->ref);
3233 TRACE("(%p) Refcount now %u\n", This, ref);
3234 if(!ref)
3235 HeapFree(GetProcessHeap(), 0, This);
3236 return ref;
3239 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3240 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3241 IAudioSessionControl **out)
3243 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3244 AudioSession *session;
3245 AudioSessionWrapper *wrapper;
3246 HRESULT hr;
3248 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3249 flags, out);
3251 hr = get_audio_session(session_guid, This->device, 0, &session);
3252 if(FAILED(hr))
3253 return hr;
3255 wrapper = AudioSessionWrapper_Create(NULL);
3256 if(!wrapper)
3257 return E_OUTOFMEMORY;
3259 wrapper->session = session;
3261 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3263 return S_OK;
3266 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3267 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3268 ISimpleAudioVolume **out)
3270 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3271 AudioSession *session;
3272 AudioSessionWrapper *wrapper;
3273 HRESULT hr;
3275 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3276 flags, out);
3278 hr = get_audio_session(session_guid, This->device, 0, &session);
3279 if(FAILED(hr))
3280 return hr;
3282 wrapper = AudioSessionWrapper_Create(NULL);
3283 if(!wrapper)
3284 return E_OUTOFMEMORY;
3286 wrapper->session = session;
3288 *out = &wrapper->ISimpleAudioVolume_iface;
3290 return S_OK;
3293 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3294 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3296 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3297 FIXME("(%p)->(%p) - stub\n", This, out);
3298 return E_NOTIMPL;
3301 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3302 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3304 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3305 FIXME("(%p)->(%p) - stub\n", This, notification);
3306 return E_NOTIMPL;
3309 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3310 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3312 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3313 FIXME("(%p)->(%p) - stub\n", This, notification);
3314 return E_NOTIMPL;
3317 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3318 IAudioSessionManager2 *iface, const WCHAR *session_id,
3319 IAudioVolumeDuckNotification *notification)
3321 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3322 FIXME("(%p)->(%p) - stub\n", This, notification);
3323 return E_NOTIMPL;
3326 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3327 IAudioSessionManager2 *iface,
3328 IAudioVolumeDuckNotification *notification)
3330 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3331 FIXME("(%p)->(%p) - stub\n", This, notification);
3332 return E_NOTIMPL;
3335 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3337 AudioSessionManager_QueryInterface,
3338 AudioSessionManager_AddRef,
3339 AudioSessionManager_Release,
3340 AudioSessionManager_GetAudioSessionControl,
3341 AudioSessionManager_GetSimpleAudioVolume,
3342 AudioSessionManager_GetSessionEnumerator,
3343 AudioSessionManager_RegisterSessionNotification,
3344 AudioSessionManager_UnregisterSessionNotification,
3345 AudioSessionManager_RegisterDuckNotification,
3346 AudioSessionManager_UnregisterDuckNotification
3349 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3350 IAudioSessionManager2 **out)
3352 SessionMgr *This;
3354 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3355 if(!This)
3356 return E_OUTOFMEMORY;
3358 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3359 This->device = device;
3360 This->ref = 1;
3362 *out = &This->IAudioSessionManager2_iface;
3364 return S_OK;