mmdevapi: SHAREDMODE_EXCLUSIVE + EVENTCALLBACK is too ... exclusive for now.
[wine/multimedia.git] / dlls / winecoreaudio.drv / mmdevdrv.c
bloba648429e60794d7f7e8743696192305231e34650
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 } AQBuffer;
80 struct ACImpl;
81 typedef struct ACImpl ACImpl;
83 typedef struct _AudioSession {
84 GUID guid;
85 struct list clients;
87 IMMDevice *device;
89 float master_vol;
90 UINT32 channel_count;
91 float *channel_vols;
92 BOOL mute;
94 CRITICAL_SECTION lock;
96 struct list entry;
97 } AudioSession;
99 typedef struct _AudioSessionWrapper {
100 IAudioSessionControl2 IAudioSessionControl2_iface;
101 IChannelAudioVolume IChannelAudioVolume_iface;
102 ISimpleAudioVolume ISimpleAudioVolume_iface;
104 LONG ref;
106 ACImpl *client;
107 AudioSession *session;
108 } AudioSessionWrapper;
110 struct ACImpl {
111 IAudioClient IAudioClient_iface;
112 IAudioRenderClient IAudioRenderClient_iface;
113 IAudioCaptureClient IAudioCaptureClient_iface;
114 IAudioClock IAudioClock_iface;
115 IAudioClock2 IAudioClock2_iface;
116 IAudioStreamVolume IAudioStreamVolume_iface;
118 LONG ref;
120 IMMDevice *parent;
122 WAVEFORMATEX *fmt;
124 EDataFlow dataflow;
125 DWORD flags;
126 AUDCLNT_SHAREMODE share;
127 HANDLE event;
128 float *vols;
130 AudioDeviceID adevid;
131 AudioQueueRef aqueue;
132 AudioObjectPropertyScope scope;
133 HANDLE timer;
134 UINT32 period_ms, bufsize_frames, inbuf_frames;
135 UINT64 last_time, written_frames;
136 AudioQueueBufferRef public_buffer;
137 UINT32 getbuf_last;
138 int playing;
140 Float64 highest_sampletime;
142 AudioSession *session;
143 AudioSessionWrapper *session_wrapper;
145 struct list entry;
147 struct list avail_buffers;
148 struct list queued_bufinfos;
150 /* We can't use debug printing or {Enter,Leave}CriticalSection from
151 * OSX callback threads, so we use OSX's OSSpinLock for synchronization
152 * instead. OSSpinLock is not a recursive lock, so don't call
153 * synchronized functions while holding the lock. */
154 OSSpinLock lock;
157 enum PlayingStates {
158 StateStopped = 0,
159 StatePlaying,
160 StateInTransition
163 static const IAudioClientVtbl AudioClient_Vtbl;
164 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
165 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
166 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
167 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
168 static const IAudioClockVtbl AudioClock_Vtbl;
169 static const IAudioClock2Vtbl AudioClock2_Vtbl;
170 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
171 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
172 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
174 typedef struct _SessionMgr {
175 IAudioSessionManager2 IAudioSessionManager2_iface;
177 LONG ref;
179 IMMDevice *device;
180 } SessionMgr;
182 static HANDLE g_timer_q;
184 static CRITICAL_SECTION g_sessions_lock;
185 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
187 0, 0, &g_sessions_lock,
188 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
189 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
191 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
192 static struct list g_sessions = LIST_INIT(g_sessions);
194 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This, UINT64 *pos,
195 UINT64 *qpctime);
196 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
197 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
199 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
201 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
204 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
209 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
214 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
216 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
219 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
221 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
224 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
226 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
229 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
231 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
234 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
236 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
239 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
241 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
244 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
246 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
249 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
251 switch (reason)
253 case DLL_PROCESS_ATTACH:
254 g_timer_q = CreateTimerQueue();
255 if(!g_timer_q)
256 return FALSE;
257 break;
259 case DLL_PROCESS_DETACH:
260 DeleteCriticalSection(&g_sessions_lock);
261 break;
263 return TRUE;
266 /* From <dlls/mmdevapi/mmdevapi.h> */
267 enum DriverPriority {
268 Priority_Unavailable = 0,
269 Priority_Low,
270 Priority_Neutral,
271 Priority_Preferred
274 int WINAPI AUDDRV_GetPriority(void)
276 return Priority_Neutral;
279 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
280 AudioDeviceID ***keys, UINT *num, UINT *def_index)
282 UInt32 devsize, size;
283 AudioDeviceID *devices;
284 AudioDeviceID default_id;
285 AudioObjectPropertyAddress addr;
286 OSStatus sc;
287 int i, ndevices;
289 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
291 addr.mScope = kAudioObjectPropertyScopeGlobal;
292 addr.mElement = kAudioObjectPropertyElementMaster;
293 if(flow == eRender)
294 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
295 else if(flow == eCapture)
296 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
297 else
298 return E_INVALIDARG;
300 size = sizeof(default_id);
301 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
302 NULL, &size, &default_id);
303 if(sc != noErr){
304 WARN("Getting _DefaultInputDevice property failed: %lx\n", sc);
305 default_id = -1;
308 addr.mSelector = kAudioHardwarePropertyDevices;
309 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
310 NULL, &devsize);
311 if(sc != noErr){
312 WARN("Getting _Devices property size failed: %lx\n", sc);
313 return E_FAIL;
316 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
317 if(!devices)
318 return E_OUTOFMEMORY;
320 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
321 &devsize, devices);
322 if(sc != noErr){
323 WARN("Getting _Devices property failed: %lx\n", sc);
324 HeapFree(GetProcessHeap(), 0, devices);
325 return E_FAIL;
328 ndevices = devsize / sizeof(AudioDeviceID);
330 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
331 if(!*ids){
332 HeapFree(GetProcessHeap(), 0, devices);
333 return E_OUTOFMEMORY;
336 *keys = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(AudioDeviceID *));
337 if(!*ids){
338 HeapFree(GetProcessHeap(), 0, *ids);
339 HeapFree(GetProcessHeap(), 0, devices);
340 return E_OUTOFMEMORY;
343 *num = 0;
344 *def_index = (UINT)-1;
345 for(i = 0; i < ndevices; ++i){
346 AudioBufferList *buffers;
347 CFStringRef name;
348 SIZE_T len;
349 int j;
351 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
352 if(flow == eRender)
353 addr.mScope = kAudioDevicePropertyScopeOutput;
354 else
355 addr.mScope = kAudioDevicePropertyScopeInput;
356 addr.mElement = 0;
357 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
358 if(sc != noErr){
359 WARN("Unable to get _StreamConfiguration property size for "
360 "device %lu: %lx\n", devices[i], sc);
361 continue;
364 buffers = HeapAlloc(GetProcessHeap(), 0, size);
365 if(!buffers){
366 HeapFree(GetProcessHeap(), 0, devices);
367 for(j = 0; j < *num; ++j){
368 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
369 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
371 HeapFree(GetProcessHeap(), 0, *keys);
372 HeapFree(GetProcessHeap(), 0, *ids);
373 return E_OUTOFMEMORY;
376 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
377 &size, buffers);
378 if(sc != noErr){
379 WARN("Unable to get _StreamConfiguration property for "
380 "device %lu: %lx\n", devices[i], sc);
381 HeapFree(GetProcessHeap(), 0, buffers);
382 continue;
385 /* check that there's at least one channel in this device before
386 * we claim it as usable */
387 for(j = 0; j < buffers->mNumberBuffers; ++j)
388 if(buffers->mBuffers[j].mNumberChannels > 0)
389 break;
390 if(j >= buffers->mNumberBuffers){
391 HeapFree(GetProcessHeap(), 0, buffers);
392 continue;
395 HeapFree(GetProcessHeap(), 0, buffers);
397 size = sizeof(name);
398 addr.mSelector = kAudioObjectPropertyName;
399 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
400 &size, &name);
401 if(sc != noErr){
402 WARN("Unable to get _Name property for device %lu: %lx\n",
403 devices[i], sc);
404 continue;
407 len = CFStringGetLength(name) + 1;
408 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
409 if(!(*ids)[*num]){
410 CFRelease(name);
411 HeapFree(GetProcessHeap(), 0, devices);
412 for(j = 0; j < *num; ++j){
413 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
414 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
416 HeapFree(GetProcessHeap(), 0, *ids);
417 HeapFree(GetProcessHeap(), 0, *keys);
418 return E_OUTOFMEMORY;
420 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
421 ((*ids)[*num])[len - 1] = 0;
422 CFRelease(name);
424 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID));
425 if(!(*keys)[*num]){
426 HeapFree(GetProcessHeap(), 0, devices);
427 HeapFree(GetProcessHeap(), 0, (*ids)[*num]);
428 for(j = 0; j < *num; ++j){
429 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
430 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
432 HeapFree(GetProcessHeap(), 0, *ids);
433 HeapFree(GetProcessHeap(), 0, *keys);
434 return E_OUTOFMEMORY;
436 *(*keys)[*num] = devices[i];
438 if(*def_index == (UINT)-1 && devices[i] == default_id)
439 *def_index = *num;
441 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
442 (unsigned int)*(*keys)[*num], (*def_index == *num) ? " (default)" : "");
444 (*num)++;
447 if(*def_index == (UINT)-1)
448 *def_index = 0;
450 HeapFree(GetProcessHeap(), 0, devices);
452 return S_OK;
455 HRESULT WINAPI AUDDRV_GetAudioEndpoint(AudioDeviceID *adevid, IMMDevice *dev,
456 EDataFlow dataflow, IAudioClient **out)
458 ACImpl *This;
460 TRACE("%u %p %d %p\n", (unsigned int)*adevid, dev, dataflow, out);
462 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
463 if(!This)
464 return E_OUTOFMEMORY;
466 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
467 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
468 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
469 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
470 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
471 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
473 This->dataflow = dataflow;
475 if(dataflow == eRender)
476 This->scope = kAudioDevicePropertyScopeOutput;
477 else if(dataflow == eCapture)
478 This->scope = kAudioDevicePropertyScopeInput;
479 else{
480 HeapFree(GetProcessHeap(), 0, This);
481 return E_INVALIDARG;
484 This->lock = 0;
486 This->parent = dev;
487 IMMDevice_AddRef(This->parent);
489 list_init(&This->avail_buffers);
490 list_init(&This->queued_bufinfos);
492 This->adevid = *adevid;
494 *out = &This->IAudioClient_iface;
495 IAudioClient_AddRef(&This->IAudioClient_iface);
497 return S_OK;
500 /* current position from start of stream */
501 #define BUFPOS_ABSOLUTE 1
502 /* current position from start of this buffer */
503 #define BUFPOS_RELATIVE 2
505 static UINT64 get_current_aqbuffer_position(ACImpl *This, int mode)
507 struct list *head;
508 QueuedBufInfo *bufinfo;
509 UINT64 ret;
511 head = list_head(&This->queued_bufinfos);
512 if(!head){
513 TRACE("No buffers queued\n");
514 if(mode == BUFPOS_ABSOLUTE)
515 return This->written_frames;
516 return 0;
518 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
520 if(This->playing == StatePlaying){
521 AudioTimeStamp tstamp;
522 OSStatus sc;
524 /* AudioQueueGetCurrentTime() is brain damaged. The returned
525 * mSampleTime member jumps backwards seemingly at random, so
526 * we record the highest sampletime and use that during these
527 * anomalies.
529 * It also behaves poorly when the queue is paused, jumping
530 * forwards during the pause and backwards again after resuming.
531 * So we record the sampletime when the queue is paused and use
532 * that. */
533 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
534 if(sc != noErr){
535 if(sc != kAudioQueueErr_InvalidRunState)
536 WARN("Unable to get current time: %lx\n", sc);
537 return 0;
540 if(!(tstamp.mFlags & kAudioTimeStampSampleTimeValid)){
541 FIXME("SampleTime not valid: %lx\n", tstamp.mFlags);
542 return 0;
545 if(tstamp.mSampleTime > This->highest_sampletime)
546 This->highest_sampletime = tstamp.mSampleTime;
549 while(This->highest_sampletime > bufinfo->start_sampletime + bufinfo->len_frames){
550 This->inbuf_frames -= bufinfo->len_frames;
551 list_remove(&bufinfo->entry);
552 HeapFree(GetProcessHeap(), 0, bufinfo);
554 head = list_head(&This->queued_bufinfos);
555 if(!head){
556 TRACE("No buffers queued\n");
557 if(mode == BUFPOS_ABSOLUTE)
558 return This->written_frames;
559 return 0;
561 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
564 if(This->highest_sampletime < bufinfo->start_sampletime)
565 ret = 0;
566 else
567 ret = This->highest_sampletime - bufinfo->start_sampletime;
569 if(mode == BUFPOS_ABSOLUTE)
570 ret += bufinfo->start_pos;
572 TRACE("%llu frames (%s)\n", ret,
573 mode == BUFPOS_ABSOLUTE ? "absolute" : "relative");
575 return ret;
578 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
579 REFIID riid, void **ppv)
581 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
583 if(!ppv)
584 return E_POINTER;
585 *ppv = NULL;
586 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
587 *ppv = iface;
588 if(*ppv){
589 IUnknown_AddRef((IUnknown*)*ppv);
590 return S_OK;
592 WARN("Unknown interface %s\n", debugstr_guid(riid));
593 return E_NOINTERFACE;
596 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
598 ACImpl *This = impl_from_IAudioClient(iface);
599 ULONG ref;
600 ref = InterlockedIncrement(&This->ref);
601 TRACE("(%p) Refcount now %u\n", This, ref);
602 return ref;
605 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
607 ACImpl *This = impl_from_IAudioClient(iface);
608 ULONG ref;
609 ref = InterlockedDecrement(&This->ref);
610 TRACE("(%p) Refcount now %u\n", This, ref);
611 if(!ref){
612 if(This->aqueue){
613 AQBuffer *buf, *next;
614 QueuedBufInfo *bufinfo, *bufinfo2;
616 if(This->public_buffer){
617 buf = This->public_buffer->mUserData;
618 list_add_tail(&This->avail_buffers, &buf->entry);
621 IAudioClient_Stop(iface);
622 AudioQueueStop(This->aqueue, 1);
624 /* Stopped synchronously, all buffers returned. */
625 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->avail_buffers, AQBuffer, entry){
626 AudioQueueFreeBuffer(This->aqueue, buf->buf);
627 HeapFree(GetProcessHeap(), 0, buf);
630 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
631 QueuedBufInfo, entry)
632 HeapFree(GetProcessHeap(), 0, bufinfo);
634 AudioQueueDispose(This->aqueue, 1);
636 if(This->session){
637 EnterCriticalSection(&g_sessions_lock);
638 list_remove(&This->entry);
639 LeaveCriticalSection(&g_sessions_lock);
641 HeapFree(GetProcessHeap(), 0, This->vols);
642 CoTaskMemFree(This->fmt);
643 IMMDevice_Release(This->parent);
644 HeapFree(GetProcessHeap(), 0, This);
646 return ref;
649 static void dump_fmt(const WAVEFORMATEX *fmt)
651 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
652 switch(fmt->wFormatTag){
653 case WAVE_FORMAT_PCM:
654 TRACE("WAVE_FORMAT_PCM");
655 break;
656 case WAVE_FORMAT_IEEE_FLOAT:
657 TRACE("WAVE_FORMAT_IEEE_FLOAT");
658 break;
659 case WAVE_FORMAT_EXTENSIBLE:
660 TRACE("WAVE_FORMAT_EXTENSIBLE");
661 break;
662 default:
663 TRACE("Unknown");
664 break;
666 TRACE(")\n");
668 TRACE("nChannels: %u\n", fmt->nChannels);
669 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
670 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
671 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
672 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
673 TRACE("cbSize: %u\n", fmt->cbSize);
675 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
676 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
677 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
678 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
679 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
683 static DWORD get_channel_mask(unsigned int channels)
685 switch(channels){
686 case 0:
687 return 0;
688 case 1:
689 return KSAUDIO_SPEAKER_MONO;
690 case 2:
691 return KSAUDIO_SPEAKER_STEREO;
692 case 3:
693 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
694 case 4:
695 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
696 case 5:
697 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
698 case 6:
699 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
700 case 7:
701 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
702 case 8:
703 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
705 FIXME("Unknown speaker configuration: %u\n", channels);
706 return 0;
709 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
711 WAVEFORMATEX *ret;
712 size_t size;
714 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
715 size = sizeof(WAVEFORMATEXTENSIBLE);
716 else
717 size = sizeof(WAVEFORMATEX);
719 ret = CoTaskMemAlloc(size);
720 if(!ret)
721 return NULL;
723 memcpy(ret, fmt, size);
725 ret->cbSize = size - sizeof(WAVEFORMATEX);
727 return ret;
730 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
731 const WAVEFORMATEX *fmt)
733 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
735 desc->mFormatFlags = 0;
737 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
738 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
739 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
740 desc->mFormatID = kAudioFormatLinearPCM;
741 if(fmt->wBitsPerSample > 8)
742 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
743 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
744 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
745 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
746 desc->mFormatID = kAudioFormatLinearPCM;
747 desc->mFormatFlags = kAudioFormatFlagIsFloat;
748 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
749 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
750 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
751 desc->mFormatID = kAudioFormatULaw;
752 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
753 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
754 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
755 desc->mFormatID = kAudioFormatALaw;
756 }else
757 return AUDCLNT_E_UNSUPPORTED_FORMAT;
759 desc->mSampleRate = fmt->nSamplesPerSec;
760 desc->mBytesPerPacket = fmt->nBlockAlign;
761 desc->mFramesPerPacket = 1;
762 desc->mBytesPerFrame = fmt->nBlockAlign;
763 desc->mChannelsPerFrame = fmt->nChannels;
764 desc->mBitsPerChannel = fmt->wBitsPerSample;
765 desc->mReserved = 0;
767 return S_OK;
770 static void ca_out_buffer_cb(void *user, AudioQueueRef aqueue,
771 AudioQueueBufferRef buffer)
773 ACImpl *This = user;
774 AQBuffer *buf = buffer->mUserData;
776 OSSpinLockLock(&This->lock);
777 list_add_tail(&This->avail_buffers, &buf->entry);
778 OSSpinLockUnlock(&This->lock);
781 static void ca_in_buffer_cb(void *user, AudioQueueRef aqueue,
782 AudioQueueBufferRef buffer, const AudioTimeStamp *start,
783 UInt32 ndesc, const AudioStreamPacketDescription *descs)
785 ACImpl *This = user;
786 AQBuffer *buf = buffer->mUserData;
788 OSSpinLockLock(&This->lock);
789 list_add_tail(&This->avail_buffers, &buf->entry);
790 This->inbuf_frames += buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
791 OSSpinLockUnlock(&This->lock);
794 static HRESULT ca_setup_aqueue(AudioDeviceID did, EDataFlow flow,
795 const WAVEFORMATEX *fmt, void *user, AudioQueueRef *aqueue)
797 AudioStreamBasicDescription desc;
798 AudioObjectPropertyAddress addr;
799 CFStringRef uid;
800 OSStatus sc;
801 HRESULT hr;
802 UInt32 size;
804 addr.mScope = kAudioObjectPropertyScopeGlobal;
805 addr.mElement = 0;
806 addr.mSelector = kAudioDevicePropertyDeviceUID;
808 size = sizeof(uid);
809 sc = AudioObjectGetPropertyData(did, &addr, 0, NULL, &size, &uid);
810 if(sc != noErr){
811 WARN("Unable to get _DeviceUID property: %lx\n", sc);
812 return E_FAIL;
815 hr = ca_get_audiodesc(&desc, fmt);
816 if(FAILED(hr)){
817 CFRelease(uid);
818 return hr;
821 if(flow == eRender)
822 sc = AudioQueueNewOutput(&desc, ca_out_buffer_cb, user, NULL, NULL, 0,
823 aqueue);
824 else if(flow == eCapture)
825 sc = AudioQueueNewInput(&desc, ca_in_buffer_cb, user, NULL, NULL, 0,
826 aqueue);
827 else{
828 CFRelease(uid);
829 return E_UNEXPECTED;
831 if(sc != noErr){
832 WARN("Unable to create AudioQueue: %lx\n", sc);
833 CFRelease(uid);
834 return E_FAIL;
837 sc = AudioQueueSetProperty(*aqueue, kAudioQueueProperty_CurrentDevice,
838 &uid, sizeof(uid));
839 if(sc != noErr){
840 CFRelease(uid);
841 return E_FAIL;
844 CFRelease(uid);
846 return S_OK;
849 static void session_init_vols(AudioSession *session, UINT channels)
851 if(session->channel_count < channels){
852 UINT i;
854 if(session->channel_vols)
855 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
856 session->channel_vols, sizeof(float) * channels);
857 else
858 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
859 sizeof(float) * channels);
860 if(!session->channel_vols)
861 return;
863 for(i = session->channel_count; i < channels; ++i)
864 session->channel_vols[i] = 1.f;
866 session->channel_count = channels;
870 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
871 UINT num_channels)
873 AudioSession *ret;
875 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
876 if(!ret)
877 return NULL;
879 memcpy(&ret->guid, guid, sizeof(GUID));
881 ret->device = device;
883 list_init(&ret->clients);
885 list_add_head(&g_sessions, &ret->entry);
887 InitializeCriticalSection(&ret->lock);
888 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
890 session_init_vols(ret, num_channels);
892 ret->master_vol = 1.f;
894 return ret;
897 /* if channels == 0, then this will return or create a session with
898 * matching dataflow and GUID. otherwise, channels must also match */
899 static HRESULT get_audio_session(const GUID *sessionguid,
900 IMMDevice *device, UINT channels, AudioSession **out)
902 AudioSession *session;
904 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
905 *out = create_session(&GUID_NULL, device, channels);
906 if(!*out)
907 return E_OUTOFMEMORY;
909 return S_OK;
912 *out = NULL;
913 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
914 if(session->device == device &&
915 IsEqualGUID(sessionguid, &session->guid)){
916 session_init_vols(session, channels);
917 *out = session;
918 break;
922 if(!*out){
923 *out = create_session(sessionguid, device, channels);
924 if(!*out)
925 return E_OUTOFMEMORY;
928 return S_OK;
931 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
932 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
933 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
934 const GUID *sessionguid)
936 ACImpl *This = impl_from_IAudioClient(iface);
937 HRESULT hr;
938 OSStatus sc;
939 int i;
941 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
942 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
944 if(!fmt)
945 return E_POINTER;
947 dump_fmt(fmt);
949 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
950 return AUDCLNT_E_NOT_INITIALIZED;
952 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
953 AUDCLNT_STREAMFLAGS_LOOPBACK |
954 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
955 AUDCLNT_STREAMFLAGS_NOPERSIST |
956 AUDCLNT_STREAMFLAGS_RATEADJUST |
957 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
958 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
959 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
960 TRACE("Unknown flags: %08x\n", flags);
961 return E_INVALIDARG;
964 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE && flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
965 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
966 return AUDCLNT_E_DEVICE_IN_USE;
969 OSSpinLockLock(&This->lock);
971 if(This->aqueue){
972 OSSpinLockUnlock(&This->lock);
973 return AUDCLNT_E_ALREADY_INITIALIZED;
976 hr = ca_setup_aqueue(This->adevid, This->dataflow, fmt, This, &This->aqueue);
977 if(FAILED(hr)){
978 OSSpinLockUnlock(&This->lock);
979 return hr;
982 This->fmt = clone_format(fmt);
983 if(!This->fmt){
984 AudioQueueDispose(This->aqueue, 1);
985 This->aqueue = NULL;
986 OSSpinLockUnlock(&This->lock);
987 return E_OUTOFMEMORY;
990 if(period){
991 This->period_ms = period / 10000;
992 if(This->period_ms == 0)
993 This->period_ms = 1;
994 }else
995 This->period_ms = MinimumPeriod / 10000;
997 if(!duration)
998 duration = 300000; /* 0.03s */
999 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
1001 if(This->dataflow == eCapture){
1002 int i;
1003 UInt32 bsize = ceil((This->bufsize_frames / (double)CAPTURE_BUFFERS) *
1004 This->fmt->nBlockAlign);
1005 for(i = 0; i < CAPTURE_BUFFERS; ++i){
1006 AQBuffer *buf;
1008 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1009 if(!buf){
1010 AudioQueueDispose(This->aqueue, 1);
1011 This->aqueue = NULL;
1012 CoTaskMemFree(This->fmt);
1013 This->fmt = NULL;
1014 OSSpinLockUnlock(&This->lock);
1015 return E_OUTOFMEMORY;
1018 sc = AudioQueueAllocateBuffer(This->aqueue, bsize, &buf->buf);
1019 if(sc != noErr){
1020 AudioQueueDispose(This->aqueue, 1);
1021 This->aqueue = NULL;
1022 CoTaskMemFree(This->fmt);
1023 This->fmt = NULL;
1024 OSSpinLockUnlock(&This->lock);
1025 WARN("Couldn't allocate buffer: %lx\n", sc);
1026 return E_FAIL;
1029 buf->buf->mUserData = buf;
1031 sc = AudioQueueEnqueueBuffer(This->aqueue, buf->buf, 0, NULL);
1032 if(sc != noErr){
1033 ERR("Couldn't enqueue buffer: %lx\n", sc);
1034 break;
1039 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1040 if(!This->vols){
1041 AudioQueueDispose(This->aqueue, 1);
1042 This->aqueue = NULL;
1043 CoTaskMemFree(This->fmt);
1044 This->fmt = NULL;
1045 OSSpinLockUnlock(&This->lock);
1046 return E_OUTOFMEMORY;
1049 for(i = 0; i < fmt->nChannels; ++i)
1050 This->vols[i] = 1.f;
1052 This->share = mode;
1053 This->flags = flags;
1055 EnterCriticalSection(&g_sessions_lock);
1057 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1058 &This->session);
1059 if(FAILED(hr)){
1060 LeaveCriticalSection(&g_sessions_lock);
1061 AudioQueueDispose(This->aqueue, 1);
1062 This->aqueue = NULL;
1063 CoTaskMemFree(This->fmt);
1064 This->fmt = NULL;
1065 HeapFree(GetProcessHeap(), 0, This->vols);
1066 This->vols = NULL;
1067 OSSpinLockUnlock(&This->lock);
1068 return E_INVALIDARG;
1071 list_add_tail(&This->session->clients, &This->entry);
1073 LeaveCriticalSection(&g_sessions_lock);
1075 ca_setvol(This, -1);
1077 OSSpinLockUnlock(&This->lock);
1079 return S_OK;
1082 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1083 UINT32 *frames)
1085 ACImpl *This = impl_from_IAudioClient(iface);
1087 TRACE("(%p)->(%p)\n", This, frames);
1089 if(!frames)
1090 return E_POINTER;
1092 OSSpinLockLock(&This->lock);
1094 if(!This->aqueue){
1095 OSSpinLockUnlock(&This->lock);
1096 return AUDCLNT_E_NOT_INITIALIZED;
1099 *frames = This->bufsize_frames;
1101 OSSpinLockUnlock(&This->lock);
1103 return S_OK;
1106 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1108 AudioObjectPropertyAddress addr;
1109 AudioStreamID *ids;
1110 UInt32 size;
1111 OSStatus sc;
1112 int nstreams, i;
1114 addr.mScope = This->scope;
1115 addr.mElement = 0;
1116 addr.mSelector = kAudioDevicePropertyStreams;
1118 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1119 &size);
1120 if(sc != noErr){
1121 WARN("Unable to get size for _Streams property: %lx\n", sc);
1122 return E_FAIL;
1125 ids = HeapAlloc(GetProcessHeap(), 0, size);
1126 if(!ids)
1127 return E_OUTOFMEMORY;
1129 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1130 if(sc != noErr){
1131 WARN("Unable to get _Streams property: %lx\n", sc);
1132 HeapFree(GetProcessHeap(), 0, ids);
1133 return E_FAIL;
1136 nstreams = size / sizeof(AudioStreamID);
1137 *max = 0;
1139 addr.mSelector = kAudioStreamPropertyLatency;
1140 for(i = 0; i < nstreams; ++i){
1141 UInt32 latency;
1143 size = sizeof(latency);
1144 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1145 &size, &latency);
1146 if(sc != noErr){
1147 WARN("Unable to get _Latency property: %lx\n", sc);
1148 continue;
1151 if(latency > *max)
1152 *max = latency;
1155 HeapFree(GetProcessHeap(), 0, ids);
1157 return S_OK;
1160 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1161 REFERENCE_TIME *out)
1163 ACImpl *This = impl_from_IAudioClient(iface);
1164 UInt32 latency, stream_latency, size;
1165 AudioObjectPropertyAddress addr;
1166 OSStatus sc;
1167 HRESULT hr;
1169 TRACE("(%p)->(%p)\n", This, out);
1171 if(!out)
1172 return E_POINTER;
1174 OSSpinLockLock(&This->lock);
1176 if(!This->aqueue){
1177 OSSpinLockUnlock(&This->lock);
1178 return AUDCLNT_E_NOT_INITIALIZED;
1181 addr.mScope = This->scope;
1182 addr.mSelector = kAudioDevicePropertyLatency;
1183 addr.mElement = 0;
1185 size = sizeof(latency);
1186 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1187 &size, &latency);
1188 if(sc != noErr){
1189 WARN("Couldn't get _Latency property: %lx\n", sc);
1190 OSSpinLockUnlock(&This->lock);
1191 return E_FAIL;
1194 hr = ca_get_max_stream_latency(This, &stream_latency);
1195 if(FAILED(hr)){
1196 OSSpinLockUnlock(&This->lock);
1197 return hr;
1200 latency += stream_latency;
1201 /* pretend we process audio in Period chunks, so max latency includes
1202 * the period time */
1203 latency += DefaultPeriod;
1204 *out = (latency / (double)This->fmt->nSamplesPerSec) * 10000000;
1206 OSSpinLockUnlock(&This->lock);
1208 return S_OK;
1211 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1212 UINT32 *numpad)
1214 if(!This->aqueue)
1215 return AUDCLNT_E_NOT_INITIALIZED;
1217 if(This->dataflow == eRender){
1218 UINT64 bufpos;
1219 bufpos = get_current_aqbuffer_position(This, BUFPOS_RELATIVE);
1220 *numpad = This->inbuf_frames - bufpos;
1221 }else
1222 *numpad = This->inbuf_frames;
1224 return S_OK;
1227 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1228 UINT32 *numpad)
1230 ACImpl *This = impl_from_IAudioClient(iface);
1231 HRESULT hr;
1233 TRACE("(%p)->(%p)\n", This, numpad);
1235 if(!numpad)
1236 return E_POINTER;
1238 OSSpinLockLock(&This->lock);
1240 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1242 OSSpinLockUnlock(&This->lock);
1244 return hr;
1247 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1248 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1249 WAVEFORMATEX **outpwfx)
1251 ACImpl *This = impl_from_IAudioClient(iface);
1252 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1253 AudioQueueRef aqueue;
1254 HRESULT hr;
1256 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1258 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1259 return E_POINTER;
1261 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1262 return E_INVALIDARG;
1264 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1265 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1266 return E_INVALIDARG;
1268 dump_fmt(pwfx);
1270 if(outpwfx)
1271 *outpwfx = NULL;
1273 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1274 fmtex->dwChannelMask != 0 &&
1275 fmtex->dwChannelMask != get_channel_mask(pwfx->nChannels))
1276 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1278 OSSpinLockLock(&This->lock);
1280 hr = ca_setup_aqueue(This->adevid, This->dataflow, pwfx, NULL, &aqueue);
1281 if(SUCCEEDED(hr)){
1282 AudioQueueDispose(aqueue, 1);
1283 OSSpinLockUnlock(&This->lock);
1284 TRACE("returning %08x\n", S_OK);
1285 return S_OK;
1288 OSSpinLockUnlock(&This->lock);
1290 TRACE("returning %08x\n", AUDCLNT_E_UNSUPPORTED_FORMAT);
1291 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1294 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1295 WAVEFORMATEX **pwfx)
1297 ACImpl *This = impl_from_IAudioClient(iface);
1298 WAVEFORMATEXTENSIBLE *fmt;
1299 OSStatus sc;
1300 UInt32 size;
1301 Float64 rate;
1302 AudioBufferList *buffers;
1303 AudioObjectPropertyAddress addr;
1304 int i;
1306 TRACE("(%p)->(%p)\n", This, pwfx);
1308 if(!pwfx)
1309 return E_POINTER;
1310 *pwfx = NULL;
1312 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1313 if(!fmt)
1314 return E_OUTOFMEMORY;
1316 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1318 addr.mScope = This->scope;
1319 addr.mElement = 0;
1320 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1322 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1323 if(sc != noErr){
1324 CoTaskMemFree(fmt);
1325 WARN("Unable to get size for _StreamConfiguration property: %lx\n", sc);
1326 return E_FAIL;
1329 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1330 if(!buffers){
1331 CoTaskMemFree(fmt);
1332 return E_OUTOFMEMORY;
1335 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1336 &size, buffers);
1337 if(sc != noErr){
1338 CoTaskMemFree(fmt);
1339 HeapFree(GetProcessHeap(), 0, buffers);
1340 WARN("Unable to get _StreamConfiguration property: %lx\n", sc);
1341 return E_FAIL;
1344 fmt->Format.nChannels = 0;
1345 for(i = 0; i < buffers->mNumberBuffers; ++i)
1346 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1348 HeapFree(GetProcessHeap(), 0, buffers);
1350 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1352 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1353 size = sizeof(Float64);
1354 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1355 if(sc != noErr){
1356 CoTaskMemFree(fmt);
1357 WARN("Unable to get _NominalSampleRate property: %lx\n", sc);
1358 return E_FAIL;
1360 fmt->Format.nSamplesPerSec = rate;
1362 fmt->Format.wBitsPerSample = 32;
1363 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1365 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1366 fmt->Format.nChannels) / 8;
1367 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1368 fmt->Format.nBlockAlign;
1370 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1371 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1373 *pwfx = (WAVEFORMATEX*)fmt;
1374 dump_fmt(*pwfx);
1376 return S_OK;
1379 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1380 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1382 ACImpl *This = impl_from_IAudioClient(iface);
1384 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1386 if(!defperiod && !minperiod)
1387 return E_POINTER;
1389 OSSpinLockLock(&This->lock);
1391 if(This->period_ms){
1392 if(defperiod)
1393 *defperiod = This->period_ms * 10000;
1394 if(minperiod)
1395 *minperiod = This->period_ms * 10000;
1396 }else{
1397 if(defperiod)
1398 *defperiod = DefaultPeriod;
1399 if(minperiod)
1400 *minperiod = MinimumPeriod;
1403 OSSpinLockUnlock(&This->lock);
1405 return S_OK;
1408 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1410 ACImpl *This = user;
1412 OSSpinLockLock(&This->lock);
1413 if(This->event)
1414 SetEvent(This->event);
1415 OSSpinLockUnlock(&This->lock);
1418 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1420 ACImpl *This = impl_from_IAudioClient(iface);
1421 OSStatus sc;
1423 TRACE("(%p)\n", This);
1425 OSSpinLockLock(&This->lock);
1427 if(!This->aqueue){
1428 OSSpinLockUnlock(&This->lock);
1429 return AUDCLNT_E_NOT_INITIALIZED;
1432 if(This->playing != StateStopped){
1433 OSSpinLockUnlock(&This->lock);
1434 return AUDCLNT_E_NOT_STOPPED;
1437 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1438 OSSpinLockUnlock(&This->lock);
1439 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1442 if(This->event)
1443 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1444 ca_period_cb, This, 0, This->period_ms, 0))
1445 ERR("Unable to create timer: %u\n", GetLastError());
1447 This->playing = StateInTransition;
1449 OSSpinLockUnlock(&This->lock);
1451 sc = AudioQueueStart(This->aqueue, NULL);
1452 if(sc != noErr){
1453 WARN("Unable to start audio queue: %lx\n", sc);
1454 return E_FAIL;
1457 OSSpinLockLock(&This->lock);
1459 This->playing = StatePlaying;
1461 OSSpinLockUnlock(&This->lock);
1463 return S_OK;
1466 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1468 ACImpl *This = impl_from_IAudioClient(iface);
1469 AudioTimeStamp tstamp;
1470 OSStatus sc;
1472 TRACE("(%p)\n", This);
1474 OSSpinLockLock(&This->lock);
1476 if(!This->aqueue){
1477 OSSpinLockUnlock(&This->lock);
1478 return AUDCLNT_E_NOT_INITIALIZED;
1481 if(This->playing == StateStopped){
1482 OSSpinLockUnlock(&This->lock);
1483 return S_FALSE;
1486 if(This->playing == StateInTransition){
1487 OSSpinLockUnlock(&This->lock);
1488 return S_OK;
1491 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1492 DeleteTimerQueueTimer(g_timer_q, This->timer, INVALID_HANDLE_VALUE);
1493 This->timer = NULL;
1496 This->playing = StateInTransition;
1498 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
1499 if(sc == noErr){
1500 if(tstamp.mFlags & kAudioTimeStampSampleTimeValid){
1501 if(tstamp.mSampleTime > This->highest_sampletime)
1502 This->highest_sampletime = tstamp.mSampleTime;
1503 }else
1504 WARN("Returned tstamp mSampleTime not valid: %lx\n", tstamp.mFlags);
1505 }else
1506 WARN("GetCurrentTime failed: %lx\n", sc);
1508 OSSpinLockUnlock(&This->lock);
1510 sc = AudioQueueFlush(This->aqueue);
1511 if(sc != noErr)
1512 WARN("Unable to flush audio queue: %lx\n", sc);
1514 sc = AudioQueuePause(This->aqueue);
1515 if(sc != noErr){
1516 WARN("Unable to pause audio queue: %lx\n", sc);
1517 return E_FAIL;
1520 OSSpinLockLock(&This->lock);
1522 This->playing = StateStopped;
1524 OSSpinLockUnlock(&This->lock);
1526 return S_OK;
1529 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1531 ACImpl *This = impl_from_IAudioClient(iface);
1532 OSStatus sc;
1533 QueuedBufInfo *bufinfo, *bufinfo2;
1535 TRACE("(%p)\n", This);
1537 OSSpinLockLock(&This->lock);
1539 if(!This->aqueue){
1540 OSSpinLockUnlock(&This->lock);
1541 return AUDCLNT_E_NOT_INITIALIZED;
1544 if(This->playing != StateStopped){
1545 OSSpinLockUnlock(&This->lock);
1546 return AUDCLNT_E_NOT_STOPPED;
1549 if(This->getbuf_last){
1550 OSSpinLockUnlock(&This->lock);
1551 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1554 This->written_frames = 0;
1555 This->inbuf_frames = 0;
1557 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
1558 QueuedBufInfo, entry){
1559 list_remove(&bufinfo->entry);
1560 HeapFree(GetProcessHeap(), 0, bufinfo);
1563 OSSpinLockUnlock(&This->lock);
1565 sc = AudioQueueReset(This->aqueue);
1566 if(sc != noErr){
1567 WARN("Unable to reset audio queue: %lx\n", sc);
1568 return E_FAIL;
1571 return S_OK;
1574 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1575 HANDLE event)
1577 ACImpl *This = impl_from_IAudioClient(iface);
1579 TRACE("(%p)->(%p)\n", This, event);
1581 if(!event)
1582 return E_INVALIDARG;
1584 OSSpinLockLock(&This->lock);
1586 if(!This->aqueue){
1587 OSSpinLockUnlock(&This->lock);
1588 return AUDCLNT_E_NOT_INITIALIZED;
1591 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1592 OSSpinLockUnlock(&This->lock);
1593 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1596 This->event = event;
1598 OSSpinLockUnlock(&This->lock);
1600 return S_OK;
1603 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1604 void **ppv)
1606 ACImpl *This = impl_from_IAudioClient(iface);
1608 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1610 if(!ppv)
1611 return E_POINTER;
1612 *ppv = NULL;
1614 OSSpinLockLock(&This->lock);
1616 if(!This->aqueue){
1617 OSSpinLockUnlock(&This->lock);
1618 return AUDCLNT_E_NOT_INITIALIZED;
1621 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1622 if(This->dataflow != eRender){
1623 OSSpinLockUnlock(&This->lock);
1624 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1626 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1627 *ppv = &This->IAudioRenderClient_iface;
1628 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1629 if(This->dataflow != eCapture){
1630 OSSpinLockUnlock(&This->lock);
1631 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1633 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1634 *ppv = &This->IAudioCaptureClient_iface;
1635 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1636 IAudioClock_AddRef(&This->IAudioClock_iface);
1637 *ppv = &This->IAudioClock_iface;
1638 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1639 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1640 *ppv = &This->IAudioStreamVolume_iface;
1641 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1642 if(!This->session_wrapper){
1643 This->session_wrapper = AudioSessionWrapper_Create(This);
1644 if(!This->session_wrapper){
1645 OSSpinLockUnlock(&This->lock);
1646 return E_OUTOFMEMORY;
1648 }else
1649 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1651 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1652 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1653 if(!This->session_wrapper){
1654 This->session_wrapper = AudioSessionWrapper_Create(This);
1655 if(!This->session_wrapper){
1656 OSSpinLockUnlock(&This->lock);
1657 return E_OUTOFMEMORY;
1659 }else
1660 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1662 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1663 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1664 if(!This->session_wrapper){
1665 This->session_wrapper = AudioSessionWrapper_Create(This);
1666 if(!This->session_wrapper){
1667 OSSpinLockUnlock(&This->lock);
1668 return E_OUTOFMEMORY;
1670 }else
1671 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1673 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1676 if(*ppv){
1677 OSSpinLockUnlock(&This->lock);
1678 return S_OK;
1681 OSSpinLockUnlock(&This->lock);
1683 FIXME("stub %s\n", debugstr_guid(riid));
1684 return E_NOINTERFACE;
1687 static const IAudioClientVtbl AudioClient_Vtbl =
1689 AudioClient_QueryInterface,
1690 AudioClient_AddRef,
1691 AudioClient_Release,
1692 AudioClient_Initialize,
1693 AudioClient_GetBufferSize,
1694 AudioClient_GetStreamLatency,
1695 AudioClient_GetCurrentPadding,
1696 AudioClient_IsFormatSupported,
1697 AudioClient_GetMixFormat,
1698 AudioClient_GetDevicePeriod,
1699 AudioClient_Start,
1700 AudioClient_Stop,
1701 AudioClient_Reset,
1702 AudioClient_SetEventHandle,
1703 AudioClient_GetService
1706 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1707 IAudioRenderClient *iface, REFIID riid, void **ppv)
1709 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1711 if(!ppv)
1712 return E_POINTER;
1713 *ppv = NULL;
1715 if(IsEqualIID(riid, &IID_IUnknown) ||
1716 IsEqualIID(riid, &IID_IAudioRenderClient))
1717 *ppv = iface;
1718 if(*ppv){
1719 IUnknown_AddRef((IUnknown*)*ppv);
1720 return S_OK;
1723 WARN("Unknown interface %s\n", debugstr_guid(riid));
1724 return E_NOINTERFACE;
1727 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1729 ACImpl *This = impl_from_IAudioRenderClient(iface);
1730 return AudioClient_AddRef(&This->IAudioClient_iface);
1733 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1735 ACImpl *This = impl_from_IAudioRenderClient(iface);
1736 return AudioClient_Release(&This->IAudioClient_iface);
1739 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1740 UINT32 frames, BYTE **data)
1742 ACImpl *This = impl_from_IAudioRenderClient(iface);
1743 AQBuffer *buf;
1744 UINT32 pad, bytes = frames * This->fmt->nBlockAlign;
1745 HRESULT hr;
1746 OSStatus sc;
1748 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1750 if(!data)
1751 return E_POINTER;
1752 *data = NULL;
1754 OSSpinLockLock(&This->lock);
1756 if(This->getbuf_last){
1757 OSSpinLockUnlock(&This->lock);
1758 return AUDCLNT_E_OUT_OF_ORDER;
1761 if(!frames){
1762 OSSpinLockUnlock(&This->lock);
1763 return S_OK;
1766 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
1767 if(FAILED(hr)){
1768 OSSpinLockUnlock(&This->lock);
1769 return hr;
1772 if(pad + frames > This->bufsize_frames){
1773 OSSpinLockUnlock(&This->lock);
1774 return AUDCLNT_E_BUFFER_TOO_LARGE;
1777 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry){
1778 if(buf->buf->mAudioDataBytesCapacity >= bytes){
1779 This->public_buffer = buf->buf;
1780 list_remove(&buf->entry);
1781 break;
1785 if(&buf->entry == &This->avail_buffers){
1786 sc = AudioQueueAllocateBuffer(This->aqueue, bytes,
1787 &This->public_buffer);
1788 if(sc != noErr){
1789 OSSpinLockUnlock(&This->lock);
1790 WARN("Unable to allocate buffer: %lx\n", sc);
1791 return E_FAIL;
1793 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1794 if(!buf){
1795 AudioQueueFreeBuffer(This->aqueue, This->public_buffer);
1796 This->public_buffer = NULL;
1797 OSSpinLockUnlock(&This->lock);
1798 return E_OUTOFMEMORY;
1800 buf->buf = This->public_buffer;
1801 This->public_buffer->mUserData = buf;
1804 *data = This->public_buffer->mAudioData;
1806 This->getbuf_last = frames;
1808 OSSpinLockUnlock(&This->lock);
1810 return S_OK;
1813 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1814 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
1816 ACImpl *This = impl_from_IAudioRenderClient(iface);
1817 AudioTimeStamp start_time;
1818 OSStatus sc;
1820 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
1822 OSSpinLockLock(&This->lock);
1824 if(!frames){
1825 This->getbuf_last = 0;
1826 if(This->public_buffer){
1827 AQBuffer *buf = This->public_buffer->mUserData;
1828 list_add_tail(&This->avail_buffers, &buf->entry);
1829 This->public_buffer = NULL;
1831 OSSpinLockUnlock(&This->lock);
1832 return S_OK;
1835 if(!This->getbuf_last){
1836 OSSpinLockUnlock(&This->lock);
1837 return AUDCLNT_E_OUT_OF_ORDER;
1840 if(frames > This->getbuf_last){
1841 OSSpinLockUnlock(&This->lock);
1842 return AUDCLNT_E_INVALID_SIZE;
1845 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1846 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1847 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1848 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1849 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1850 This->fmt->wBitsPerSample == 8)
1851 memset(This->public_buffer->mAudioData, 128,
1852 frames * This->fmt->nBlockAlign);
1853 else
1854 memset(This->public_buffer->mAudioData, 0,
1855 frames * This->fmt->nBlockAlign);
1858 This->public_buffer->mAudioDataByteSize = frames * This->fmt->nBlockAlign;
1860 sc = AudioQueueEnqueueBufferWithParameters(This->aqueue,
1861 This->public_buffer, 0, NULL, 0, 0, 0, NULL, NULL, &start_time);
1862 if(sc != noErr){
1863 OSSpinLockUnlock(&This->lock);
1864 WARN("Unable to enqueue buffer: %lx\n", sc);
1865 return E_FAIL;
1868 if(start_time.mFlags & kAudioTimeStampSampleTimeValid){
1869 QueuedBufInfo *bufinfo;
1871 bufinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*bufinfo));
1872 bufinfo->start_sampletime = start_time.mSampleTime;
1873 bufinfo->start_pos = This->written_frames;
1874 bufinfo->len_frames = frames;
1876 list_add_tail(&This->queued_bufinfos, &bufinfo->entry);
1877 }else
1878 WARN("Start time didn't contain valid SampleTime member\n");
1880 if(This->playing == StateStopped)
1881 AudioQueuePrime(This->aqueue, 0, NULL);
1883 This->public_buffer = NULL;
1884 This->getbuf_last = 0;
1885 This->written_frames += frames;
1886 This->inbuf_frames += frames;
1888 OSSpinLockUnlock(&This->lock);
1890 return S_OK;
1893 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1894 AudioRenderClient_QueryInterface,
1895 AudioRenderClient_AddRef,
1896 AudioRenderClient_Release,
1897 AudioRenderClient_GetBuffer,
1898 AudioRenderClient_ReleaseBuffer
1901 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1902 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1904 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1906 if(!ppv)
1907 return E_POINTER;
1908 *ppv = NULL;
1910 if(IsEqualIID(riid, &IID_IUnknown) ||
1911 IsEqualIID(riid, &IID_IAudioCaptureClient))
1912 *ppv = iface;
1913 if(*ppv){
1914 IUnknown_AddRef((IUnknown*)*ppv);
1915 return S_OK;
1918 WARN("Unknown interface %s\n", debugstr_guid(riid));
1919 return E_NOINTERFACE;
1922 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1924 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1925 return IAudioClient_AddRef(&This->IAudioClient_iface);
1928 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1930 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1931 return IAudioClient_Release(&This->IAudioClient_iface);
1934 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1935 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1936 UINT64 *qpcpos)
1938 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1940 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1941 devpos, qpcpos);
1943 if(!data || !frames || !flags)
1944 return E_POINTER;
1946 OSSpinLockLock(&This->lock);
1948 if(This->getbuf_last){
1949 OSSpinLockUnlock(&This->lock);
1950 return AUDCLNT_E_OUT_OF_ORDER;
1953 if(This->public_buffer){
1954 *data = This->public_buffer->mAudioData;
1955 *frames =
1956 This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
1957 }else{
1958 struct list *head = list_head(&This->avail_buffers);
1959 if(!head){
1960 *data = NULL;
1961 *frames = 0;
1962 }else{
1963 AQBuffer *buf = LIST_ENTRY(head, AQBuffer, entry);
1964 This->public_buffer = buf->buf;
1965 *data = This->public_buffer->mAudioData;
1966 *frames =
1967 This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
1968 list_remove(&buf->entry);
1972 *flags = 0;
1973 This->written_frames += *frames;
1974 This->inbuf_frames -= *frames;
1975 This->getbuf_last = 1;
1977 if(devpos || qpcpos)
1978 AudioClock_GetPosition_nolock(This, devpos, qpcpos);
1980 OSSpinLockUnlock(&This->lock);
1982 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1985 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1986 IAudioCaptureClient *iface, UINT32 done)
1988 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1989 UINT32 pbuf_frames;
1990 OSStatus sc;
1992 TRACE("(%p)->(%u)\n", This, done);
1994 OSSpinLockLock(&This->lock);
1996 if(!This->getbuf_last){
1997 OSSpinLockUnlock(&This->lock);
1998 return AUDCLNT_E_OUT_OF_ORDER;
2001 pbuf_frames = This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
2002 if(done != 0 && done != pbuf_frames){
2003 OSSpinLockUnlock(&This->lock);
2004 return AUDCLNT_E_INVALID_SIZE;
2007 if(done){
2008 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer,
2009 0, NULL);
2010 if(sc != noErr)
2011 WARN("Unable to enqueue buffer: %lx\n", sc);
2012 This->public_buffer = NULL;
2015 This->getbuf_last = 0;
2017 OSSpinLockUnlock(&This->lock);
2019 return S_OK;
2022 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2023 IAudioCaptureClient *iface, UINT32 *frames)
2025 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2026 struct list *head;
2027 AQBuffer *buf;
2029 TRACE("(%p)->(%p)\n", This, frames);
2031 if(!frames)
2032 return E_POINTER;
2034 OSSpinLockLock(&This->lock);
2036 head = list_head(&This->avail_buffers);
2038 if(!head){
2039 *frames = This->bufsize_frames / CAPTURE_BUFFERS;
2040 OSSpinLockUnlock(&This->lock);
2041 return S_OK;
2044 buf = LIST_ENTRY(head, AQBuffer, entry);
2045 *frames = buf->buf->mAudioDataByteSize / This->fmt->nBlockAlign;
2047 OSSpinLockUnlock(&This->lock);
2049 return S_OK;
2052 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2054 AudioCaptureClient_QueryInterface,
2055 AudioCaptureClient_AddRef,
2056 AudioCaptureClient_Release,
2057 AudioCaptureClient_GetBuffer,
2058 AudioCaptureClient_ReleaseBuffer,
2059 AudioCaptureClient_GetNextPacketSize
2062 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2063 REFIID riid, void **ppv)
2065 ACImpl *This = impl_from_IAudioClock(iface);
2067 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2069 if(!ppv)
2070 return E_POINTER;
2071 *ppv = NULL;
2073 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2074 *ppv = iface;
2075 else if(IsEqualIID(riid, &IID_IAudioClock2))
2076 *ppv = &This->IAudioClock2_iface;
2077 if(*ppv){
2078 IUnknown_AddRef((IUnknown*)*ppv);
2079 return S_OK;
2082 WARN("Unknown interface %s\n", debugstr_guid(riid));
2083 return E_NOINTERFACE;
2086 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2088 ACImpl *This = impl_from_IAudioClock(iface);
2089 return IAudioClient_AddRef(&This->IAudioClient_iface);
2092 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2094 ACImpl *This = impl_from_IAudioClock(iface);
2095 return IAudioClient_Release(&This->IAudioClient_iface);
2098 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2100 ACImpl *This = impl_from_IAudioClock(iface);
2102 TRACE("(%p)->(%p)\n", This, freq);
2104 *freq = This->fmt->nSamplesPerSec;
2106 return S_OK;
2109 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2110 UINT64 *pos, UINT64 *qpctime)
2112 if(This->dataflow == eRender)
2113 *pos = get_current_aqbuffer_position(This, BUFPOS_ABSOLUTE);
2114 else
2115 *pos = This->inbuf_frames + This->written_frames;
2117 if(qpctime){
2118 LARGE_INTEGER stamp, freq;
2119 QueryPerformanceCounter(&stamp);
2120 QueryPerformanceFrequency(&freq);
2121 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2124 return S_OK;
2127 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2128 UINT64 *qpctime)
2130 ACImpl *This = impl_from_IAudioClock(iface);
2131 HRESULT hr;
2133 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2135 if(!pos)
2136 return E_POINTER;
2138 OSSpinLockLock(&This->lock);
2140 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2142 OSSpinLockUnlock(&This->lock);
2144 return hr;
2147 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2148 DWORD *chars)
2150 ACImpl *This = impl_from_IAudioClock(iface);
2152 TRACE("(%p)->(%p)\n", This, chars);
2154 if(!chars)
2155 return E_POINTER;
2157 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2159 return S_OK;
2162 static const IAudioClockVtbl AudioClock_Vtbl =
2164 AudioClock_QueryInterface,
2165 AudioClock_AddRef,
2166 AudioClock_Release,
2167 AudioClock_GetFrequency,
2168 AudioClock_GetPosition,
2169 AudioClock_GetCharacteristics
2172 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2173 REFIID riid, void **ppv)
2175 ACImpl *This = impl_from_IAudioClock2(iface);
2176 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2179 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2181 ACImpl *This = impl_from_IAudioClock2(iface);
2182 return IAudioClient_AddRef(&This->IAudioClient_iface);
2185 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2187 ACImpl *This = impl_from_IAudioClock2(iface);
2188 return IAudioClient_Release(&This->IAudioClient_iface);
2191 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2192 UINT64 *pos, UINT64 *qpctime)
2194 ACImpl *This = impl_from_IAudioClock2(iface);
2196 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2198 return E_NOTIMPL;
2201 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2203 AudioClock2_QueryInterface,
2204 AudioClock2_AddRef,
2205 AudioClock2_Release,
2206 AudioClock2_GetDevicePosition
2209 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2211 AudioSessionWrapper *ret;
2213 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2214 sizeof(AudioSessionWrapper));
2215 if(!ret)
2216 return NULL;
2218 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2219 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2220 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2222 ret->ref = 1;
2224 ret->client = client;
2225 if(client){
2226 ret->session = client->session;
2227 AudioClient_AddRef(&client->IAudioClient_iface);
2230 return ret;
2233 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2234 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2236 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2238 if(!ppv)
2239 return E_POINTER;
2240 *ppv = NULL;
2242 if(IsEqualIID(riid, &IID_IUnknown) ||
2243 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2244 IsEqualIID(riid, &IID_IAudioSessionControl2))
2245 *ppv = iface;
2246 if(*ppv){
2247 IUnknown_AddRef((IUnknown*)*ppv);
2248 return S_OK;
2251 WARN("Unknown interface %s\n", debugstr_guid(riid));
2252 return E_NOINTERFACE;
2255 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2257 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2258 ULONG ref;
2259 ref = InterlockedIncrement(&This->ref);
2260 TRACE("(%p) Refcount now %u\n", This, ref);
2261 return ref;
2264 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2266 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2267 ULONG ref;
2268 ref = InterlockedDecrement(&This->ref);
2269 TRACE("(%p) Refcount now %u\n", This, ref);
2270 if(!ref){
2271 if(This->client){
2272 OSSpinLockLock(&This->client->lock);
2273 This->client->session_wrapper = NULL;
2274 OSSpinLockUnlock(&This->client->lock);
2275 AudioClient_Release(&This->client->IAudioClient_iface);
2277 HeapFree(GetProcessHeap(), 0, This);
2279 return ref;
2282 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2283 AudioSessionState *state)
2285 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2286 ACImpl *client;
2288 TRACE("(%p)->(%p)\n", This, state);
2290 if(!state)
2291 return NULL_PTR_ERR;
2293 EnterCriticalSection(&g_sessions_lock);
2295 if(list_empty(&This->session->clients)){
2296 *state = AudioSessionStateExpired;
2297 LeaveCriticalSection(&g_sessions_lock);
2298 return S_OK;
2301 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2302 OSSpinLockLock(&client->lock);
2303 if(client->playing == StatePlaying ||
2304 client->playing == StateInTransition){
2305 *state = AudioSessionStateActive;
2306 OSSpinLockUnlock(&client->lock);
2307 LeaveCriticalSection(&g_sessions_lock);
2308 return S_OK;
2310 OSSpinLockUnlock(&client->lock);
2313 LeaveCriticalSection(&g_sessions_lock);
2315 *state = AudioSessionStateInactive;
2317 return S_OK;
2320 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2321 IAudioSessionControl2 *iface, WCHAR **name)
2323 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2325 FIXME("(%p)->(%p) - stub\n", This, name);
2327 return E_NOTIMPL;
2330 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2331 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2333 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2335 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2337 return E_NOTIMPL;
2340 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2341 IAudioSessionControl2 *iface, WCHAR **path)
2343 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2345 FIXME("(%p)->(%p) - stub\n", This, path);
2347 return E_NOTIMPL;
2350 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2351 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2353 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2355 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2357 return E_NOTIMPL;
2360 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2361 IAudioSessionControl2 *iface, GUID *group)
2363 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2365 FIXME("(%p)->(%p) - stub\n", This, group);
2367 return E_NOTIMPL;
2370 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2371 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2373 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2375 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2376 debugstr_guid(session));
2378 return E_NOTIMPL;
2381 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2382 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2384 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2386 FIXME("(%p)->(%p) - stub\n", This, events);
2388 return S_OK;
2391 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2392 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2394 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2396 FIXME("(%p)->(%p) - stub\n", This, events);
2398 return S_OK;
2401 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2402 IAudioSessionControl2 *iface, WCHAR **id)
2404 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2406 FIXME("(%p)->(%p) - stub\n", This, id);
2408 return E_NOTIMPL;
2411 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2412 IAudioSessionControl2 *iface, WCHAR **id)
2414 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2416 FIXME("(%p)->(%p) - stub\n", This, id);
2418 return E_NOTIMPL;
2421 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2422 IAudioSessionControl2 *iface, DWORD *pid)
2424 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2426 TRACE("(%p)->(%p)\n", This, pid);
2428 if(!pid)
2429 return E_POINTER;
2431 *pid = GetCurrentProcessId();
2433 return S_OK;
2436 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2437 IAudioSessionControl2 *iface)
2439 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2441 TRACE("(%p)\n", This);
2443 return S_FALSE;
2446 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2447 IAudioSessionControl2 *iface, BOOL optout)
2449 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2451 TRACE("(%p)->(%d)\n", This, optout);
2453 return S_OK;
2456 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2458 AudioSessionControl_QueryInterface,
2459 AudioSessionControl_AddRef,
2460 AudioSessionControl_Release,
2461 AudioSessionControl_GetState,
2462 AudioSessionControl_GetDisplayName,
2463 AudioSessionControl_SetDisplayName,
2464 AudioSessionControl_GetIconPath,
2465 AudioSessionControl_SetIconPath,
2466 AudioSessionControl_GetGroupingParam,
2467 AudioSessionControl_SetGroupingParam,
2468 AudioSessionControl_RegisterAudioSessionNotification,
2469 AudioSessionControl_UnregisterAudioSessionNotification,
2470 AudioSessionControl_GetSessionIdentifier,
2471 AudioSessionControl_GetSessionInstanceIdentifier,
2472 AudioSessionControl_GetProcessId,
2473 AudioSessionControl_IsSystemSoundsSession,
2474 AudioSessionControl_SetDuckingPreference
2477 /* index == -1 means set all channels, otherwise sets only the given channel */
2478 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2480 float level;
2481 OSStatus sc;
2483 if(index == (UINT32)-1){
2484 HRESULT ret = S_OK;
2485 UINT32 i;
2486 for(i = 0; i < This->fmt->nChannels; ++i){
2487 HRESULT hr;
2488 hr = ca_setvol(This, i);
2489 if(FAILED(hr))
2490 ret = hr;
2492 return ret;
2495 if(This->session->mute)
2496 level = 0;
2497 else
2498 level = This->session->master_vol *
2499 This->session->channel_vols[index] * This->vols[index];
2501 sc = AudioQueueSetParameter(This->aqueue, kAudioQueueParam_Volume, level);
2502 if(sc != noErr)
2503 WARN("Setting _Volume property failed: %lx\n", sc);
2505 return S_OK;
2508 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
2510 HRESULT ret = S_OK;
2511 ACImpl *client;
2513 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2514 HRESULT hr;
2515 hr = ca_setvol(client, index);
2516 if(FAILED(hr))
2517 ret = hr;
2520 return ret;
2523 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2524 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2526 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2528 if(!ppv)
2529 return E_POINTER;
2530 *ppv = NULL;
2532 if(IsEqualIID(riid, &IID_IUnknown) ||
2533 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2534 *ppv = iface;
2535 if(*ppv){
2536 IUnknown_AddRef((IUnknown*)*ppv);
2537 return S_OK;
2540 WARN("Unknown interface %s\n", debugstr_guid(riid));
2541 return E_NOINTERFACE;
2544 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2546 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2547 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2550 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2552 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2553 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2556 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2557 ISimpleAudioVolume *iface, float level, const GUID *context)
2559 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2560 AudioSession *session = This->session;
2561 HRESULT ret;
2563 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2565 if(level < 0.f || level > 1.f)
2566 return E_INVALIDARG;
2568 if(context)
2569 FIXME("Notifications not supported yet\n");
2571 EnterCriticalSection(&session->lock);
2573 session->master_vol = level;
2575 ret = ca_session_setvol(session, -1);
2577 LeaveCriticalSection(&session->lock);
2579 return ret;
2582 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2583 ISimpleAudioVolume *iface, float *level)
2585 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2586 AudioSession *session = This->session;
2588 TRACE("(%p)->(%p)\n", session, level);
2590 if(!level)
2591 return NULL_PTR_ERR;
2593 *level = session->master_vol;
2595 return S_OK;
2598 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2599 BOOL mute, const GUID *context)
2601 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2602 AudioSession *session = This->session;
2604 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2606 if(context)
2607 FIXME("Notifications not supported yet\n");
2609 EnterCriticalSection(&session->lock);
2611 session->mute = mute;
2613 ca_session_setvol(session, -1);
2615 LeaveCriticalSection(&session->lock);
2617 return S_OK;
2620 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2621 BOOL *mute)
2623 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2624 AudioSession *session = This->session;
2626 TRACE("(%p)->(%p)\n", session, mute);
2628 if(!mute)
2629 return NULL_PTR_ERR;
2631 *mute = session->mute;
2633 return S_OK;
2636 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2638 SimpleAudioVolume_QueryInterface,
2639 SimpleAudioVolume_AddRef,
2640 SimpleAudioVolume_Release,
2641 SimpleAudioVolume_SetMasterVolume,
2642 SimpleAudioVolume_GetMasterVolume,
2643 SimpleAudioVolume_SetMute,
2644 SimpleAudioVolume_GetMute
2647 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2648 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2650 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2652 if(!ppv)
2653 return E_POINTER;
2654 *ppv = NULL;
2656 if(IsEqualIID(riid, &IID_IUnknown) ||
2657 IsEqualIID(riid, &IID_IAudioStreamVolume))
2658 *ppv = iface;
2659 if(*ppv){
2660 IUnknown_AddRef((IUnknown*)*ppv);
2661 return S_OK;
2664 WARN("Unknown interface %s\n", debugstr_guid(riid));
2665 return E_NOINTERFACE;
2668 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2670 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2671 return IAudioClient_AddRef(&This->IAudioClient_iface);
2674 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2676 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2677 return IAudioClient_Release(&This->IAudioClient_iface);
2680 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2681 IAudioStreamVolume *iface, UINT32 *out)
2683 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2685 TRACE("(%p)->(%p)\n", This, out);
2687 if(!out)
2688 return E_POINTER;
2690 *out = This->fmt->nChannels;
2692 return S_OK;
2695 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2696 IAudioStreamVolume *iface, UINT32 index, float level)
2698 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2699 HRESULT ret;
2701 TRACE("(%p)->(%d, %f)\n", This, index, level);
2703 if(level < 0.f || level > 1.f)
2704 return E_INVALIDARG;
2706 if(index >= This->fmt->nChannels)
2707 return E_INVALIDARG;
2709 OSSpinLockLock(&This->lock);
2711 This->vols[index] = level;
2713 WARN("AudioQueue doesn't support per-channel volume control\n");
2714 ret = ca_setvol(This, index);
2716 OSSpinLockUnlock(&This->lock);
2718 return ret;
2721 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2722 IAudioStreamVolume *iface, UINT32 index, float *level)
2724 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2726 TRACE("(%p)->(%d, %p)\n", This, index, level);
2728 if(!level)
2729 return E_POINTER;
2731 if(index >= This->fmt->nChannels)
2732 return E_INVALIDARG;
2734 *level = This->vols[index];
2736 return S_OK;
2739 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2740 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2742 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2743 int i;
2744 HRESULT ret;
2746 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2748 if(!levels)
2749 return E_POINTER;
2751 if(count != This->fmt->nChannels)
2752 return E_INVALIDARG;
2754 OSSpinLockLock(&This->lock);
2756 for(i = 0; i < count; ++i)
2757 This->vols[i] = levels[i];
2759 ret = ca_setvol(This, -1);
2761 OSSpinLockUnlock(&This->lock);
2763 return ret;
2766 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2767 IAudioStreamVolume *iface, UINT32 count, float *levels)
2769 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2770 int i;
2772 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2774 if(!levels)
2775 return E_POINTER;
2777 if(count != This->fmt->nChannels)
2778 return E_INVALIDARG;
2780 OSSpinLockLock(&This->lock);
2782 for(i = 0; i < count; ++i)
2783 levels[i] = This->vols[i];
2785 OSSpinLockUnlock(&This->lock);
2787 return S_OK;
2790 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2792 AudioStreamVolume_QueryInterface,
2793 AudioStreamVolume_AddRef,
2794 AudioStreamVolume_Release,
2795 AudioStreamVolume_GetChannelCount,
2796 AudioStreamVolume_SetChannelVolume,
2797 AudioStreamVolume_GetChannelVolume,
2798 AudioStreamVolume_SetAllVolumes,
2799 AudioStreamVolume_GetAllVolumes
2802 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2803 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2805 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2807 if(!ppv)
2808 return E_POINTER;
2809 *ppv = NULL;
2811 if(IsEqualIID(riid, &IID_IUnknown) ||
2812 IsEqualIID(riid, &IID_IChannelAudioVolume))
2813 *ppv = iface;
2814 if(*ppv){
2815 IUnknown_AddRef((IUnknown*)*ppv);
2816 return S_OK;
2819 WARN("Unknown interface %s\n", debugstr_guid(riid));
2820 return E_NOINTERFACE;
2823 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2825 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2826 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2829 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2831 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2832 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2835 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2836 IChannelAudioVolume *iface, UINT32 *out)
2838 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2839 AudioSession *session = This->session;
2841 TRACE("(%p)->(%p)\n", session, out);
2843 if(!out)
2844 return NULL_PTR_ERR;
2846 *out = session->channel_count;
2848 return S_OK;
2851 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2852 IChannelAudioVolume *iface, UINT32 index, float level,
2853 const GUID *context)
2855 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2856 AudioSession *session = This->session;
2857 HRESULT ret;
2859 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2860 wine_dbgstr_guid(context));
2862 if(level < 0.f || level > 1.f)
2863 return E_INVALIDARG;
2865 if(index >= session->channel_count)
2866 return E_INVALIDARG;
2868 if(context)
2869 FIXME("Notifications not supported yet\n");
2871 EnterCriticalSection(&session->lock);
2873 session->channel_vols[index] = level;
2875 WARN("AudioQueue doesn't support per-channel volume control\n");
2876 ret = ca_session_setvol(session, index);
2878 LeaveCriticalSection(&session->lock);
2880 return ret;
2883 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2884 IChannelAudioVolume *iface, UINT32 index, float *level)
2886 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2887 AudioSession *session = This->session;
2889 TRACE("(%p)->(%d, %p)\n", session, index, level);
2891 if(!level)
2892 return NULL_PTR_ERR;
2894 if(index >= session->channel_count)
2895 return E_INVALIDARG;
2897 *level = session->channel_vols[index];
2899 return S_OK;
2902 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2903 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2904 const GUID *context)
2906 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2907 AudioSession *session = This->session;
2908 int i;
2909 HRESULT ret;
2911 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2912 wine_dbgstr_guid(context));
2914 if(!levels)
2915 return NULL_PTR_ERR;
2917 if(count != session->channel_count)
2918 return E_INVALIDARG;
2920 if(context)
2921 FIXME("Notifications not supported yet\n");
2923 EnterCriticalSection(&session->lock);
2925 for(i = 0; i < count; ++i)
2926 session->channel_vols[i] = levels[i];
2928 ret = ca_session_setvol(session, -1);
2930 LeaveCriticalSection(&session->lock);
2932 return ret;
2935 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2936 IChannelAudioVolume *iface, UINT32 count, float *levels)
2938 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2939 AudioSession *session = This->session;
2940 int i;
2942 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2944 if(!levels)
2945 return NULL_PTR_ERR;
2947 if(count != session->channel_count)
2948 return E_INVALIDARG;
2950 for(i = 0; i < count; ++i)
2951 levels[i] = session->channel_vols[i];
2953 return S_OK;
2956 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2958 ChannelAudioVolume_QueryInterface,
2959 ChannelAudioVolume_AddRef,
2960 ChannelAudioVolume_Release,
2961 ChannelAudioVolume_GetChannelCount,
2962 ChannelAudioVolume_SetChannelVolume,
2963 ChannelAudioVolume_GetChannelVolume,
2964 ChannelAudioVolume_SetAllVolumes,
2965 ChannelAudioVolume_GetAllVolumes
2968 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2969 REFIID riid, void **ppv)
2971 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2973 if(!ppv)
2974 return E_POINTER;
2975 *ppv = NULL;
2977 if(IsEqualIID(riid, &IID_IUnknown) ||
2978 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2979 IsEqualIID(riid, &IID_IAudioSessionManager2))
2980 *ppv = iface;
2981 if(*ppv){
2982 IUnknown_AddRef((IUnknown*)*ppv);
2983 return S_OK;
2986 WARN("Unknown interface %s\n", debugstr_guid(riid));
2987 return E_NOINTERFACE;
2990 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2992 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2993 ULONG ref;
2994 ref = InterlockedIncrement(&This->ref);
2995 TRACE("(%p) Refcount now %u\n", This, ref);
2996 return ref;
2999 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3001 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3002 ULONG ref;
3003 ref = InterlockedDecrement(&This->ref);
3004 TRACE("(%p) Refcount now %u\n", This, ref);
3005 if(!ref)
3006 HeapFree(GetProcessHeap(), 0, This);
3007 return ref;
3010 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3011 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3012 IAudioSessionControl **out)
3014 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3015 AudioSession *session;
3016 AudioSessionWrapper *wrapper;
3017 HRESULT hr;
3019 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3020 flags, out);
3022 hr = get_audio_session(session_guid, This->device, 0, &session);
3023 if(FAILED(hr))
3024 return hr;
3026 wrapper = AudioSessionWrapper_Create(NULL);
3027 if(!wrapper)
3028 return E_OUTOFMEMORY;
3030 wrapper->session = session;
3032 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3034 return S_OK;
3037 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3038 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3039 ISimpleAudioVolume **out)
3041 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3042 AudioSession *session;
3043 AudioSessionWrapper *wrapper;
3044 HRESULT hr;
3046 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3047 flags, out);
3049 hr = get_audio_session(session_guid, This->device, 0, &session);
3050 if(FAILED(hr))
3051 return hr;
3053 wrapper = AudioSessionWrapper_Create(NULL);
3054 if(!wrapper)
3055 return E_OUTOFMEMORY;
3057 wrapper->session = session;
3059 *out = &wrapper->ISimpleAudioVolume_iface;
3061 return S_OK;
3064 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3065 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3067 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3068 FIXME("(%p)->(%p) - stub\n", This, out);
3069 return E_NOTIMPL;
3072 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3073 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3075 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3076 FIXME("(%p)->(%p) - stub\n", This, notification);
3077 return E_NOTIMPL;
3080 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3081 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3083 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3084 FIXME("(%p)->(%p) - stub\n", This, notification);
3085 return E_NOTIMPL;
3088 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3089 IAudioSessionManager2 *iface, const WCHAR *session_id,
3090 IAudioVolumeDuckNotification *notification)
3092 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3093 FIXME("(%p)->(%p) - stub\n", This, notification);
3094 return E_NOTIMPL;
3097 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3098 IAudioSessionManager2 *iface,
3099 IAudioVolumeDuckNotification *notification)
3101 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3102 FIXME("(%p)->(%p) - stub\n", This, notification);
3103 return E_NOTIMPL;
3106 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3108 AudioSessionManager_QueryInterface,
3109 AudioSessionManager_AddRef,
3110 AudioSessionManager_Release,
3111 AudioSessionManager_GetAudioSessionControl,
3112 AudioSessionManager_GetSimpleAudioVolume,
3113 AudioSessionManager_GetSessionEnumerator,
3114 AudioSessionManager_RegisterSessionNotification,
3115 AudioSessionManager_UnregisterSessionNotification,
3116 AudioSessionManager_RegisterDuckNotification,
3117 AudioSessionManager_UnregisterDuckNotification
3120 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3121 IAudioSessionManager2 **out)
3123 SessionMgr *This;
3125 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3126 if(!This)
3127 return E_OUTOFMEMORY;
3129 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3130 This->device = device;
3131 This->ref = 1;
3133 *out = &This->IAudioSessionManager2_iface;
3135 return S_OK;