notepad: Fix the position of the Encoding combobox.
[wine/multimedia.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blob42d8d1894747d6cb7bc474c192fa524d151dba9a
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 HANDLE g_timer_q;
182 static CRITICAL_SECTION g_sessions_lock;
183 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
185 0, 0, &g_sessions_lock,
186 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
187 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
189 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
190 static struct list g_sessions = LIST_INIT(g_sessions);
192 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames);
193 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
194 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
196 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
198 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
201 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
203 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
206 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
208 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
211 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
213 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
216 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
218 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
221 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
223 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
226 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
228 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
231 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
233 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
236 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
238 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
241 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
243 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
246 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
248 switch (reason)
250 case DLL_PROCESS_ATTACH:
251 g_timer_q = CreateTimerQueue();
252 if(!g_timer_q)
253 return FALSE;
254 break;
256 case DLL_PROCESS_DETACH:
257 DeleteCriticalSection(&g_sessions_lock);
258 break;
260 return TRUE;
263 /* From <dlls/mmdevapi/mmdevapi.h> */
264 enum DriverPriority {
265 Priority_Unavailable = 0,
266 Priority_Low,
267 Priority_Neutral,
268 Priority_Preferred
271 int WINAPI AUDDRV_GetPriority(void)
273 return Priority_Neutral;
276 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
277 AudioDeviceID ***keys, UINT *num, UINT *def_index)
279 UInt32 devsize, size;
280 AudioDeviceID *devices;
281 AudioDeviceID default_id;
282 AudioObjectPropertyAddress addr;
283 OSStatus sc;
284 int i, ndevices;
286 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
288 addr.mScope = kAudioObjectPropertyScopeGlobal;
289 addr.mElement = kAudioObjectPropertyElementMaster;
290 if(flow == eRender)
291 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
292 else if(flow == eCapture)
293 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
294 else
295 return E_INVALIDARG;
297 size = sizeof(default_id);
298 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
299 NULL, &size, &default_id);
300 if(sc != noErr){
301 WARN("Getting _DefaultInputDevice property failed: %lx\n", sc);
302 default_id = -1;
305 addr.mSelector = kAudioHardwarePropertyDevices;
306 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
307 NULL, &devsize);
308 if(sc != noErr){
309 WARN("Getting _Devices property size failed: %lx\n", sc);
310 return E_FAIL;
313 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
314 if(!devices)
315 return E_OUTOFMEMORY;
317 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
318 &devsize, devices);
319 if(sc != noErr){
320 WARN("Getting _Devices property failed: %lx\n", sc);
321 HeapFree(GetProcessHeap(), 0, devices);
322 return E_FAIL;
325 ndevices = devsize / sizeof(AudioDeviceID);
327 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
328 if(!*ids){
329 HeapFree(GetProcessHeap(), 0, devices);
330 return E_OUTOFMEMORY;
333 *keys = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(AudioDeviceID *));
334 if(!*ids){
335 HeapFree(GetProcessHeap(), 0, *ids);
336 HeapFree(GetProcessHeap(), 0, devices);
337 return E_OUTOFMEMORY;
340 *num = 0;
341 *def_index = (UINT)-1;
342 for(i = 0; i < ndevices; ++i){
343 AudioBufferList *buffers;
344 CFStringRef name;
345 SIZE_T len;
346 int j;
348 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
349 if(flow == eRender)
350 addr.mScope = kAudioDevicePropertyScopeOutput;
351 else
352 addr.mScope = kAudioDevicePropertyScopeInput;
353 addr.mElement = 0;
354 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
355 if(sc != noErr){
356 WARN("Unable to get _StreamConfiguration property size for "
357 "device %lu: %lx\n", devices[i], sc);
358 continue;
361 buffers = HeapAlloc(GetProcessHeap(), 0, size);
362 if(!buffers){
363 HeapFree(GetProcessHeap(), 0, devices);
364 for(j = 0; j < *num; ++j){
365 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
366 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
368 HeapFree(GetProcessHeap(), 0, *keys);
369 HeapFree(GetProcessHeap(), 0, *ids);
370 return E_OUTOFMEMORY;
373 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
374 &size, buffers);
375 if(sc != noErr){
376 WARN("Unable to get _StreamConfiguration property for "
377 "device %lu: %lx\n", devices[i], sc);
378 HeapFree(GetProcessHeap(), 0, buffers);
379 continue;
382 /* check that there's at least one channel in this device before
383 * we claim it as usable */
384 for(j = 0; j < buffers->mNumberBuffers; ++j)
385 if(buffers->mBuffers[j].mNumberChannels > 0)
386 break;
387 if(j >= buffers->mNumberBuffers){
388 HeapFree(GetProcessHeap(), 0, buffers);
389 continue;
392 HeapFree(GetProcessHeap(), 0, buffers);
394 size = sizeof(name);
395 addr.mSelector = kAudioObjectPropertyName;
396 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
397 &size, &name);
398 if(sc != noErr){
399 WARN("Unable to get _Name property for device %lu: %lx\n",
400 devices[i], sc);
401 continue;
404 len = CFStringGetLength(name) + 1;
405 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
406 if(!(*ids)[*num]){
407 CFRelease(name);
408 HeapFree(GetProcessHeap(), 0, devices);
409 for(j = 0; j < *num; ++j){
410 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
411 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
413 HeapFree(GetProcessHeap(), 0, *ids);
414 HeapFree(GetProcessHeap(), 0, *keys);
415 return E_OUTOFMEMORY;
417 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
418 ((*ids)[*num])[len - 1] = 0;
419 CFRelease(name);
421 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID));
422 if(!(*keys)[*num]){
423 HeapFree(GetProcessHeap(), 0, devices);
424 HeapFree(GetProcessHeap(), 0, (*ids)[*num]);
425 for(j = 0; j < *num; ++j){
426 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
427 HeapFree(GetProcessHeap(), 0, (*keys)[j]);
429 HeapFree(GetProcessHeap(), 0, *ids);
430 HeapFree(GetProcessHeap(), 0, *keys);
431 return E_OUTOFMEMORY;
433 *(*keys)[*num] = devices[i];
435 if(*def_index == (UINT)-1 && devices[i] == default_id)
436 *def_index = *num;
438 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
439 (unsigned int)*(*keys)[*num], (*def_index == *num) ? " (default)" : "");
441 (*num)++;
444 if(*def_index == (UINT)-1)
445 *def_index = 0;
447 HeapFree(GetProcessHeap(), 0, devices);
449 return S_OK;
452 HRESULT WINAPI AUDDRV_GetAudioEndpoint(AudioDeviceID *adevid, IMMDevice *dev,
453 EDataFlow dataflow, IAudioClient **out)
455 ACImpl *This;
457 TRACE("%u %p %d %p\n", (unsigned int)*adevid, dev, dataflow, out);
459 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
460 if(!This)
461 return E_OUTOFMEMORY;
463 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
464 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
465 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
466 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
467 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
468 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
470 This->dataflow = dataflow;
472 if(dataflow == eRender)
473 This->scope = kAudioDevicePropertyScopeOutput;
474 else if(dataflow == eCapture)
475 This->scope = kAudioDevicePropertyScopeInput;
476 else{
477 HeapFree(GetProcessHeap(), 0, This);
478 return E_INVALIDARG;
481 This->lock = 0;
483 This->parent = dev;
484 IMMDevice_AddRef(This->parent);
486 list_init(&This->avail_buffers);
487 list_init(&This->queued_buffers);
488 list_init(&This->queued_bufinfos);
490 This->adevid = *adevid;
492 *out = &This->IAudioClient_iface;
493 IAudioClient_AddRef(&This->IAudioClient_iface);
495 return S_OK;
498 /* current position from start of stream */
499 #define BUFPOS_ABSOLUTE 1
500 /* current position from start of this buffer */
501 #define BUFPOS_RELATIVE 2
503 static UINT64 get_current_aqbuffer_position(ACImpl *This, int mode)
505 struct list *head;
506 QueuedBufInfo *bufinfo;
507 UINT64 ret;
509 head = list_head(&This->queued_bufinfos);
510 if(!head){
511 TRACE("No buffers queued\n");
512 if(mode == BUFPOS_ABSOLUTE)
513 return This->written_frames;
514 return 0;
516 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
518 if(This->playing == StatePlaying){
519 AudioTimeStamp tstamp;
520 OSStatus sc;
522 /* AudioQueueGetCurrentTime() is brain damaged. The returned
523 * mSampleTime member jumps backwards seemingly at random, so
524 * we record the highest sampletime and use that during these
525 * anomalies.
527 * It also behaves poorly when the queue is paused, jumping
528 * forwards during the pause and backwards again after resuming.
529 * So we record the sampletime when the queue is paused and use
530 * that. */
531 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
532 if(sc != noErr){
533 if(sc != kAudioQueueErr_InvalidRunState)
534 WARN("Unable to get current time: %lx\n", sc);
535 return 0;
538 if(!(tstamp.mFlags & kAudioTimeStampSampleTimeValid)){
539 FIXME("SampleTime not valid: %lx\n", tstamp.mFlags);
540 return 0;
543 if(tstamp.mSampleTime > This->highest_sampletime)
544 This->highest_sampletime = tstamp.mSampleTime;
547 while(This->highest_sampletime > bufinfo->start_sampletime + bufinfo->len_frames){
548 This->inbuf_frames -= bufinfo->len_frames;
549 list_remove(&bufinfo->entry);
550 HeapFree(GetProcessHeap(), 0, bufinfo);
552 head = list_head(&This->queued_bufinfos);
553 if(!head){
554 TRACE("No buffers queued\n");
555 if(mode == BUFPOS_ABSOLUTE)
556 return This->written_frames;
557 return 0;
559 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
562 if(This->highest_sampletime < bufinfo->start_sampletime)
563 ret = 0;
564 else
565 ret = This->highest_sampletime - bufinfo->start_sampletime;
567 if(mode == BUFPOS_ABSOLUTE){
568 ret = This->written_frames - (bufinfo->len_frames - ret);
569 while((head = list_next(&This->queued_bufinfos, &bufinfo->entry))){
570 bufinfo = LIST_ENTRY(head, QueuedBufInfo, entry);
571 ret -= bufinfo->len_frames;
575 TRACE("%llu frames (%s)\n", ret,
576 mode == BUFPOS_ABSOLUTE ? "absolute" : "relative");
578 return ret;
581 static void avail_update(ACImpl *This)
583 AQBuffer *buf, *next;
585 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
586 if(buf->used)
587 break;
588 if(This->dataflow == eCapture)
589 This->inbuf_frames += buf->buf->mAudioDataByteSize / This->fmt->nBlockAlign;
590 list_remove(&buf->entry);
591 list_add_tail(&This->avail_buffers, &buf->entry);
595 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
596 REFIID riid, void **ppv)
598 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
600 if(!ppv)
601 return E_POINTER;
602 *ppv = NULL;
603 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
604 *ppv = iface;
605 if(*ppv){
606 IUnknown_AddRef((IUnknown*)*ppv);
607 return S_OK;
609 WARN("Unknown interface %s\n", debugstr_guid(riid));
610 return E_NOINTERFACE;
613 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
615 ACImpl *This = impl_from_IAudioClient(iface);
616 ULONG ref;
617 ref = InterlockedIncrement(&This->ref);
618 TRACE("(%p) Refcount now %u\n", This, ref);
619 return ref;
622 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
624 ACImpl *This = impl_from_IAudioClient(iface);
625 ULONG ref;
626 ref = InterlockedDecrement(&This->ref);
627 TRACE("(%p) Refcount now %u\n", This, ref);
628 if(!ref){
629 if(This->aqueue){
630 AQBuffer *buf, *next;
631 QueuedBufInfo *bufinfo, *bufinfo2;
633 if(This->public_buffer){
634 buf = This->public_buffer->mUserData;
635 list_add_tail(&This->avail_buffers, &buf->entry);
638 IAudioClient_Stop(iface);
639 AudioQueueStop(This->aqueue, 1);
641 /* Stopped synchronously, all buffers returned. */
642 list_move_tail(&This->avail_buffers, &This->queued_buffers);
643 LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->avail_buffers, AQBuffer, entry){
644 AudioQueueFreeBuffer(This->aqueue, buf->buf);
645 HeapFree(GetProcessHeap(), 0, buf);
648 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
649 QueuedBufInfo, entry)
650 HeapFree(GetProcessHeap(), 0, bufinfo);
652 AudioQueueDispose(This->aqueue, 1);
654 if(This->session){
655 EnterCriticalSection(&g_sessions_lock);
656 list_remove(&This->entry);
657 LeaveCriticalSection(&g_sessions_lock);
659 HeapFree(GetProcessHeap(), 0, This->vols);
660 CoTaskMemFree(This->fmt);
661 IMMDevice_Release(This->parent);
662 HeapFree(GetProcessHeap(), 0, This);
664 return ref;
667 static void dump_fmt(const WAVEFORMATEX *fmt)
669 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
670 switch(fmt->wFormatTag){
671 case WAVE_FORMAT_PCM:
672 TRACE("WAVE_FORMAT_PCM");
673 break;
674 case WAVE_FORMAT_IEEE_FLOAT:
675 TRACE("WAVE_FORMAT_IEEE_FLOAT");
676 break;
677 case WAVE_FORMAT_EXTENSIBLE:
678 TRACE("WAVE_FORMAT_EXTENSIBLE");
679 break;
680 default:
681 TRACE("Unknown");
682 break;
684 TRACE(")\n");
686 TRACE("nChannels: %u\n", fmt->nChannels);
687 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
688 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
689 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
690 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
691 TRACE("cbSize: %u\n", fmt->cbSize);
693 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
694 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
695 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
696 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
697 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
701 static DWORD get_channel_mask(unsigned int channels)
703 switch(channels){
704 case 0:
705 return 0;
706 case 1:
707 return KSAUDIO_SPEAKER_MONO;
708 case 2:
709 return KSAUDIO_SPEAKER_STEREO;
710 case 3:
711 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
712 case 4:
713 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
714 case 5:
715 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
716 case 6:
717 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
718 case 7:
719 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
720 case 8:
721 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
723 FIXME("Unknown speaker configuration: %u\n", channels);
724 return 0;
727 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
729 WAVEFORMATEX *ret;
730 size_t size;
732 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
733 size = sizeof(WAVEFORMATEXTENSIBLE);
734 else
735 size = sizeof(WAVEFORMATEX);
737 ret = CoTaskMemAlloc(size);
738 if(!ret)
739 return NULL;
741 memcpy(ret, fmt, size);
743 ret->cbSize = size - sizeof(WAVEFORMATEX);
745 return ret;
748 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
749 const WAVEFORMATEX *fmt)
751 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
753 desc->mFormatFlags = 0;
755 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
756 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
757 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
758 desc->mFormatID = kAudioFormatLinearPCM;
759 if(fmt->wBitsPerSample > 8)
760 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
761 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
762 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
763 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
764 desc->mFormatID = kAudioFormatLinearPCM;
765 desc->mFormatFlags = kAudioFormatFlagIsFloat;
766 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
767 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
768 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
769 desc->mFormatID = kAudioFormatULaw;
770 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
771 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
772 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
773 desc->mFormatID = kAudioFormatALaw;
774 }else
775 return AUDCLNT_E_UNSUPPORTED_FORMAT;
777 desc->mSampleRate = fmt->nSamplesPerSec;
778 desc->mBytesPerPacket = fmt->nBlockAlign;
779 desc->mFramesPerPacket = 1;
780 desc->mBytesPerFrame = fmt->nBlockAlign;
781 desc->mChannelsPerFrame = fmt->nChannels;
782 desc->mBitsPerChannel = fmt->wBitsPerSample;
783 desc->mReserved = 0;
785 return S_OK;
788 /* We can't use debug printing or {Enter,Leave}CriticalSection from
789 * OSX callback threads. We may use OSSpinLock.
790 * OSSpinLock is not a recursive lock, so don't call
791 * synchronized functions while holding the lock. */
792 static void ca_out_buffer_cb(void *user, AudioQueueRef aqueue,
793 AudioQueueBufferRef buffer)
795 AQBuffer *buf = buffer->mUserData;
797 buf->used = FALSE;
800 static void ca_in_buffer_cb(void *user, AudioQueueRef aqueue,
801 AudioQueueBufferRef buffer, const AudioTimeStamp *start,
802 UInt32 ndesc, const AudioStreamPacketDescription *descs)
804 AQBuffer *buf = buffer->mUserData;
806 buf->used = FALSE;
807 /* let's update inbuf_frames synchronously without OSAddAtomic */
810 static HRESULT ca_setup_aqueue(AudioDeviceID did, EDataFlow flow,
811 const WAVEFORMATEX *fmt, void *user, AudioQueueRef *aqueue)
813 AudioStreamBasicDescription desc;
814 AudioObjectPropertyAddress addr;
815 CFStringRef uid;
816 OSStatus sc;
817 HRESULT hr;
818 UInt32 size;
820 addr.mScope = kAudioObjectPropertyScopeGlobal;
821 addr.mElement = 0;
822 addr.mSelector = kAudioDevicePropertyDeviceUID;
824 size = sizeof(uid);
825 sc = AudioObjectGetPropertyData(did, &addr, 0, NULL, &size, &uid);
826 if(sc != noErr){
827 WARN("Unable to get _DeviceUID property: %lx\n", sc);
828 return E_FAIL;
831 hr = ca_get_audiodesc(&desc, fmt);
832 if(FAILED(hr)){
833 CFRelease(uid);
834 return hr;
837 if(flow == eRender)
838 sc = AudioQueueNewOutput(&desc, ca_out_buffer_cb, user, NULL, NULL, 0,
839 aqueue);
840 else if(flow == eCapture)
841 sc = AudioQueueNewInput(&desc, ca_in_buffer_cb, user, NULL, NULL, 0,
842 aqueue);
843 else{
844 CFRelease(uid);
845 return E_UNEXPECTED;
847 if(sc != noErr){
848 WARN("Unable to create AudioQueue: %lx\n", sc);
849 CFRelease(uid);
850 return E_FAIL;
853 sc = AudioQueueSetProperty(*aqueue, kAudioQueueProperty_CurrentDevice,
854 &uid, sizeof(uid));
855 if(sc != noErr){
856 CFRelease(uid);
857 return E_FAIL;
860 CFRelease(uid);
862 return S_OK;
865 static void session_init_vols(AudioSession *session, UINT channels)
867 if(session->channel_count < channels){
868 UINT i;
870 if(session->channel_vols)
871 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
872 session->channel_vols, sizeof(float) * channels);
873 else
874 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
875 sizeof(float) * channels);
876 if(!session->channel_vols)
877 return;
879 for(i = session->channel_count; i < channels; ++i)
880 session->channel_vols[i] = 1.f;
882 session->channel_count = channels;
886 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
887 UINT num_channels)
889 AudioSession *ret;
891 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
892 if(!ret)
893 return NULL;
895 memcpy(&ret->guid, guid, sizeof(GUID));
897 ret->device = device;
899 list_init(&ret->clients);
901 list_add_head(&g_sessions, &ret->entry);
903 InitializeCriticalSection(&ret->lock);
904 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
906 session_init_vols(ret, num_channels);
908 ret->master_vol = 1.f;
910 return ret;
913 /* if channels == 0, then this will return or create a session with
914 * matching dataflow and GUID. otherwise, channels must also match */
915 static HRESULT get_audio_session(const GUID *sessionguid,
916 IMMDevice *device, UINT channels, AudioSession **out)
918 AudioSession *session;
920 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
921 *out = create_session(&GUID_NULL, device, channels);
922 if(!*out)
923 return E_OUTOFMEMORY;
925 return S_OK;
928 *out = NULL;
929 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
930 if(session->device == device &&
931 IsEqualGUID(sessionguid, &session->guid)){
932 session_init_vols(session, channels);
933 *out = session;
934 break;
938 if(!*out){
939 *out = create_session(sessionguid, device, channels);
940 if(!*out)
941 return E_OUTOFMEMORY;
944 return S_OK;
947 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
948 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
949 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
950 const GUID *sessionguid)
952 ACImpl *This = impl_from_IAudioClient(iface);
953 HRESULT hr;
954 OSStatus sc;
955 int i;
957 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
958 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
960 if(!fmt)
961 return E_POINTER;
963 dump_fmt(fmt);
965 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
966 return AUDCLNT_E_NOT_INITIALIZED;
968 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
969 AUDCLNT_STREAMFLAGS_LOOPBACK |
970 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
971 AUDCLNT_STREAMFLAGS_NOPERSIST |
972 AUDCLNT_STREAMFLAGS_RATEADJUST |
973 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
974 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
975 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
976 TRACE("Unknown flags: %08x\n", flags);
977 return E_INVALIDARG;
980 if(mode == AUDCLNT_SHAREMODE_SHARED){
981 period = DefaultPeriod;
982 if( duration < 3 * period)
983 duration = 3 * period;
984 }else{
985 if(!period)
986 period = DefaultPeriod; /* not minimum */
987 if(period < MinimumPeriod || period > 5000000)
988 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
989 if(duration > 20000000) /* the smaller the period, the lower this limit */
990 return AUDCLNT_E_BUFFER_SIZE_ERROR;
991 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
992 if(duration != period)
993 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
994 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
995 return AUDCLNT_E_DEVICE_IN_USE;
996 }else{
997 if( duration < 8 * period)
998 duration = 8 * period; /* may grow above 2s */
1002 OSSpinLockLock(&This->lock);
1004 if(This->aqueue){
1005 OSSpinLockUnlock(&This->lock);
1006 return AUDCLNT_E_ALREADY_INITIALIZED;
1009 hr = ca_setup_aqueue(This->adevid, This->dataflow, fmt, This, &This->aqueue);
1010 if(FAILED(hr)){
1011 OSSpinLockUnlock(&This->lock);
1012 return hr;
1015 This->fmt = clone_format(fmt);
1016 if(!This->fmt){
1017 AudioQueueDispose(This->aqueue, 1);
1018 This->aqueue = NULL;
1019 OSSpinLockUnlock(&This->lock);
1020 return E_OUTOFMEMORY;
1023 This->period_ms = period / 10000;
1025 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1027 if(This->dataflow == eCapture){
1028 int i;
1029 UInt32 bsize = ceil((This->bufsize_frames / (double)CAPTURE_BUFFERS) *
1030 This->fmt->nBlockAlign);
1031 for(i = 0; i < CAPTURE_BUFFERS; ++i){
1032 AQBuffer *buf;
1034 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1035 if(!buf){
1036 AudioQueueDispose(This->aqueue, 1);
1037 This->aqueue = NULL;
1038 CoTaskMemFree(This->fmt);
1039 This->fmt = NULL;
1040 OSSpinLockUnlock(&This->lock);
1041 return E_OUTOFMEMORY;
1044 sc = AudioQueueAllocateBuffer(This->aqueue, bsize, &buf->buf);
1045 if(sc != noErr){
1046 AudioQueueDispose(This->aqueue, 1);
1047 This->aqueue = NULL;
1048 CoTaskMemFree(This->fmt);
1049 This->fmt = NULL;
1050 OSSpinLockUnlock(&This->lock);
1051 WARN("Couldn't allocate buffer: %lx\n", sc);
1052 return E_FAIL;
1055 buf->buf->mUserData = buf;
1056 buf->used = TRUE;
1057 sc = AudioQueueEnqueueBuffer(This->aqueue, buf->buf, 0, NULL);
1058 if(sc != noErr){
1059 ERR("Couldn't enqueue buffer: %lx\n", sc);
1060 break;
1062 list_add_tail(&This->queued_buffers, &buf->entry);
1066 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1067 if(!This->vols){
1068 AudioQueueDispose(This->aqueue, 1);
1069 This->aqueue = NULL;
1070 CoTaskMemFree(This->fmt);
1071 This->fmt = NULL;
1072 OSSpinLockUnlock(&This->lock);
1073 return E_OUTOFMEMORY;
1076 for(i = 0; i < fmt->nChannels; ++i)
1077 This->vols[i] = 1.f;
1079 This->share = mode;
1080 This->flags = flags;
1082 EnterCriticalSection(&g_sessions_lock);
1084 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1085 &This->session);
1086 if(FAILED(hr)){
1087 LeaveCriticalSection(&g_sessions_lock);
1088 AudioQueueDispose(This->aqueue, 1);
1089 This->aqueue = NULL;
1090 CoTaskMemFree(This->fmt);
1091 This->fmt = NULL;
1092 HeapFree(GetProcessHeap(), 0, This->vols);
1093 This->vols = NULL;
1094 OSSpinLockUnlock(&This->lock);
1095 return E_INVALIDARG;
1098 list_add_tail(&This->session->clients, &This->entry);
1100 LeaveCriticalSection(&g_sessions_lock);
1102 ca_setvol(This, -1);
1104 OSSpinLockUnlock(&This->lock);
1106 return S_OK;
1109 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1110 UINT32 *frames)
1112 ACImpl *This = impl_from_IAudioClient(iface);
1114 TRACE("(%p)->(%p)\n", This, frames);
1116 if(!frames)
1117 return E_POINTER;
1119 OSSpinLockLock(&This->lock);
1121 if(!This->aqueue){
1122 OSSpinLockUnlock(&This->lock);
1123 return AUDCLNT_E_NOT_INITIALIZED;
1126 *frames = This->bufsize_frames;
1128 OSSpinLockUnlock(&This->lock);
1130 return S_OK;
1133 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1135 AudioObjectPropertyAddress addr;
1136 AudioStreamID *ids;
1137 UInt32 size;
1138 OSStatus sc;
1139 int nstreams, i;
1141 addr.mScope = This->scope;
1142 addr.mElement = 0;
1143 addr.mSelector = kAudioDevicePropertyStreams;
1145 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1146 &size);
1147 if(sc != noErr){
1148 WARN("Unable to get size for _Streams property: %lx\n", sc);
1149 return E_FAIL;
1152 ids = HeapAlloc(GetProcessHeap(), 0, size);
1153 if(!ids)
1154 return E_OUTOFMEMORY;
1156 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1157 if(sc != noErr){
1158 WARN("Unable to get _Streams property: %lx\n", sc);
1159 HeapFree(GetProcessHeap(), 0, ids);
1160 return E_FAIL;
1163 nstreams = size / sizeof(AudioStreamID);
1164 *max = 0;
1166 addr.mSelector = kAudioStreamPropertyLatency;
1167 for(i = 0; i < nstreams; ++i){
1168 UInt32 latency;
1170 size = sizeof(latency);
1171 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1172 &size, &latency);
1173 if(sc != noErr){
1174 WARN("Unable to get _Latency property: %lx\n", sc);
1175 continue;
1178 if(latency > *max)
1179 *max = latency;
1182 HeapFree(GetProcessHeap(), 0, ids);
1184 return S_OK;
1187 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1188 REFERENCE_TIME *out)
1190 ACImpl *This = impl_from_IAudioClient(iface);
1191 UInt32 latency, stream_latency, size;
1192 AudioObjectPropertyAddress addr;
1193 OSStatus sc;
1194 HRESULT hr;
1196 TRACE("(%p)->(%p)\n", This, out);
1198 if(!out)
1199 return E_POINTER;
1201 OSSpinLockLock(&This->lock);
1203 if(!This->aqueue){
1204 OSSpinLockUnlock(&This->lock);
1205 return AUDCLNT_E_NOT_INITIALIZED;
1208 addr.mScope = This->scope;
1209 addr.mSelector = kAudioDevicePropertyLatency;
1210 addr.mElement = 0;
1212 size = sizeof(latency);
1213 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1214 &size, &latency);
1215 if(sc != noErr){
1216 WARN("Couldn't get _Latency property: %lx\n", sc);
1217 OSSpinLockUnlock(&This->lock);
1218 return E_FAIL;
1221 hr = ca_get_max_stream_latency(This, &stream_latency);
1222 if(FAILED(hr)){
1223 OSSpinLockUnlock(&This->lock);
1224 return hr;
1227 latency += stream_latency;
1228 /* pretend we process audio in Period chunks, so max latency includes
1229 * the period time */
1230 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1231 + This->period_ms * 10000;
1233 OSSpinLockUnlock(&This->lock);
1235 return S_OK;
1238 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1239 UINT32 *numpad)
1241 if(!This->aqueue)
1242 return AUDCLNT_E_NOT_INITIALIZED;
1244 avail_update(This);
1246 if(This->dataflow == eRender){
1247 UINT64 bufpos;
1248 bufpos = get_current_aqbuffer_position(This, BUFPOS_RELATIVE);
1249 *numpad = This->inbuf_frames - bufpos;
1250 }else
1251 *numpad = This->inbuf_frames;
1253 return S_OK;
1256 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1257 UINT32 *numpad)
1259 ACImpl *This = impl_from_IAudioClient(iface);
1260 HRESULT hr;
1262 TRACE("(%p)->(%p)\n", This, numpad);
1264 if(!numpad)
1265 return E_POINTER;
1267 OSSpinLockLock(&This->lock);
1269 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1271 OSSpinLockUnlock(&This->lock);
1273 return hr;
1276 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1277 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1278 WAVEFORMATEX **outpwfx)
1280 ACImpl *This = impl_from_IAudioClient(iface);
1281 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1282 AudioQueueRef aqueue;
1283 HRESULT hr;
1285 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1287 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1288 return E_POINTER;
1290 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1291 return E_INVALIDARG;
1293 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1294 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1295 return E_INVALIDARG;
1297 dump_fmt(pwfx);
1299 if(outpwfx)
1300 *outpwfx = NULL;
1302 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1303 fmtex->dwChannelMask != 0 &&
1304 fmtex->dwChannelMask != get_channel_mask(pwfx->nChannels))
1305 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1307 OSSpinLockLock(&This->lock);
1309 hr = ca_setup_aqueue(This->adevid, This->dataflow, pwfx, NULL, &aqueue);
1310 if(SUCCEEDED(hr)){
1311 AudioQueueDispose(aqueue, 1);
1312 OSSpinLockUnlock(&This->lock);
1313 TRACE("returning %08x\n", S_OK);
1314 return S_OK;
1317 OSSpinLockUnlock(&This->lock);
1319 TRACE("returning %08x\n", AUDCLNT_E_UNSUPPORTED_FORMAT);
1320 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1323 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1324 WAVEFORMATEX **pwfx)
1326 ACImpl *This = impl_from_IAudioClient(iface);
1327 WAVEFORMATEXTENSIBLE *fmt;
1328 OSStatus sc;
1329 UInt32 size;
1330 Float64 rate;
1331 AudioBufferList *buffers;
1332 AudioObjectPropertyAddress addr;
1333 int i;
1335 TRACE("(%p)->(%p)\n", This, pwfx);
1337 if(!pwfx)
1338 return E_POINTER;
1339 *pwfx = NULL;
1341 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1342 if(!fmt)
1343 return E_OUTOFMEMORY;
1345 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1347 addr.mScope = This->scope;
1348 addr.mElement = 0;
1349 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1351 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1352 if(sc != noErr){
1353 CoTaskMemFree(fmt);
1354 WARN("Unable to get size for _StreamConfiguration property: %lx\n", sc);
1355 return E_FAIL;
1358 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1359 if(!buffers){
1360 CoTaskMemFree(fmt);
1361 return E_OUTOFMEMORY;
1364 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1365 &size, buffers);
1366 if(sc != noErr){
1367 CoTaskMemFree(fmt);
1368 HeapFree(GetProcessHeap(), 0, buffers);
1369 WARN("Unable to get _StreamConfiguration property: %lx\n", sc);
1370 return E_FAIL;
1373 fmt->Format.nChannels = 0;
1374 for(i = 0; i < buffers->mNumberBuffers; ++i)
1375 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1377 HeapFree(GetProcessHeap(), 0, buffers);
1379 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1381 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1382 size = sizeof(Float64);
1383 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1384 if(sc != noErr){
1385 CoTaskMemFree(fmt);
1386 WARN("Unable to get _NominalSampleRate property: %lx\n", sc);
1387 return E_FAIL;
1389 fmt->Format.nSamplesPerSec = rate;
1391 fmt->Format.wBitsPerSample = 32;
1392 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1394 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1395 fmt->Format.nChannels) / 8;
1396 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1397 fmt->Format.nBlockAlign;
1399 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1400 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1402 *pwfx = (WAVEFORMATEX*)fmt;
1403 dump_fmt(*pwfx);
1405 return S_OK;
1408 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1409 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1411 ACImpl *This = impl_from_IAudioClient(iface);
1413 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1415 if(!defperiod && !minperiod)
1416 return E_POINTER;
1418 if(defperiod)
1419 *defperiod = DefaultPeriod;
1420 if(minperiod)
1421 *minperiod = MinimumPeriod;
1423 return S_OK;
1426 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1428 ACImpl *This = user;
1430 if(This->event)
1431 SetEvent(This->event);
1434 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1436 ACImpl *This = impl_from_IAudioClient(iface);
1437 OSStatus sc;
1439 TRACE("(%p)\n", This);
1441 OSSpinLockLock(&This->lock);
1443 if(!This->aqueue){
1444 OSSpinLockUnlock(&This->lock);
1445 return AUDCLNT_E_NOT_INITIALIZED;
1448 if(This->playing != StateStopped){
1449 OSSpinLockUnlock(&This->lock);
1450 return AUDCLNT_E_NOT_STOPPED;
1453 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1454 OSSpinLockUnlock(&This->lock);
1455 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1458 if(This->event)
1459 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
1460 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
1461 This->timer = NULL;
1462 OSSpinLockUnlock(&This->lock);
1463 WARN("Unable to create timer: %u\n", GetLastError());
1464 return E_OUTOFMEMORY;
1467 if(This->dataflow == eCapture){
1468 UINT32 frames; /* enqueue packets */
1469 AudioCaptureClient_GetNextPacket(This, &frames);
1472 This->playing = StateInTransition;
1474 sc = AudioQueueStart(This->aqueue, NULL);
1475 if(sc != noErr){
1476 OSSpinLockUnlock(&This->lock);
1477 WARN("Unable to start audio queue: %lx\n", sc);
1478 return E_FAIL;
1481 This->playing = StatePlaying;
1483 OSSpinLockUnlock(&This->lock);
1485 return S_OK;
1488 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1490 ACImpl *This = impl_from_IAudioClient(iface);
1491 AudioTimeStamp tstamp;
1492 OSStatus sc;
1493 HANDLE event = NULL;
1494 BOOL wait = FALSE;
1496 TRACE("(%p)\n", This);
1498 OSSpinLockLock(&This->lock);
1500 if(!This->aqueue){
1501 OSSpinLockUnlock(&This->lock);
1502 return AUDCLNT_E_NOT_INITIALIZED;
1505 if(This->playing == StateStopped){
1506 OSSpinLockUnlock(&This->lock);
1507 return S_FALSE;
1510 if(This->playing == StateInTransition){
1511 OSSpinLockUnlock(&This->lock);
1512 return S_OK;
1515 if(This->timer){
1516 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1517 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1518 This->timer = NULL;
1519 if(wait)
1520 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1521 wait = wait && GetLastError() == ERROR_IO_PENDING;
1524 This->playing = StateInTransition;
1526 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &tstamp, NULL);
1527 if(sc == noErr){
1528 if(tstamp.mFlags & kAudioTimeStampSampleTimeValid){
1529 if(tstamp.mSampleTime > This->highest_sampletime)
1530 This->highest_sampletime = tstamp.mSampleTime;
1531 }else
1532 WARN("Returned tstamp mSampleTime not valid: %lx\n", tstamp.mFlags);
1533 }else
1534 WARN("GetCurrentTime failed: %lx\n", sc);
1536 /* Mac OS bug? Our capture callback is no more called past AQStop */
1537 sc = AudioQueuePause(This->aqueue);
1538 if(sc != noErr){
1539 OSSpinLockUnlock(&This->lock);
1540 WARN("Unable to pause audio queue: %lx\n", sc);
1541 return E_FAIL;
1544 This->playing = StateStopped;
1546 OSSpinLockUnlock(&This->lock);
1548 if(event && wait)
1549 WaitForSingleObject(event, INFINITE);
1550 CloseHandle(event);
1552 return S_OK;
1555 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1557 ACImpl *This = impl_from_IAudioClient(iface);
1558 OSStatus sc;
1559 QueuedBufInfo *bufinfo, *bufinfo2;
1560 AQBuffer *buf;
1562 TRACE("(%p)\n", This);
1564 OSSpinLockLock(&This->lock);
1566 if(!This->aqueue){
1567 OSSpinLockUnlock(&This->lock);
1568 return AUDCLNT_E_NOT_INITIALIZED;
1571 if(This->playing != StateStopped){
1572 OSSpinLockUnlock(&This->lock);
1573 return AUDCLNT_E_NOT_STOPPED;
1576 if(This->getbuf_last){
1577 OSSpinLockUnlock(&This->lock);
1578 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1581 avail_update(This); /* going to skip over inbuf_frames */
1583 LIST_FOR_EACH_ENTRY_SAFE(bufinfo, bufinfo2, &This->queued_bufinfos,
1584 QueuedBufInfo, entry){
1585 list_remove(&bufinfo->entry);
1586 HeapFree(GetProcessHeap(), 0, bufinfo);
1589 sc = AudioQueueReset(This->aqueue);
1590 if(sc != noErr){
1591 OSSpinLockUnlock(&This->lock);
1592 WARN("Unable to reset audio queue: %lx\n", sc);
1593 return E_FAIL;
1596 /* AQReset is synchronous */
1597 list_move_tail(&This->avail_buffers, &This->queued_buffers);
1599 if(This->dataflow == eRender){
1600 This->written_frames = 0;
1601 }else{
1602 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry)
1603 buf->buf->mAudioDataByteSize = 0;
1604 This->written_frames += This->inbuf_frames;
1606 This->inbuf_frames = 0;
1608 OSSpinLockUnlock(&This->lock);
1610 return S_OK;
1613 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1614 HANDLE event)
1616 ACImpl *This = impl_from_IAudioClient(iface);
1618 TRACE("(%p)->(%p)\n", This, event);
1620 if(!event)
1621 return E_INVALIDARG;
1623 OSSpinLockLock(&This->lock);
1625 if(!This->aqueue){
1626 OSSpinLockUnlock(&This->lock);
1627 return AUDCLNT_E_NOT_INITIALIZED;
1630 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1631 OSSpinLockUnlock(&This->lock);
1632 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1635 This->event = event;
1637 OSSpinLockUnlock(&This->lock);
1639 return S_OK;
1642 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1643 void **ppv)
1645 ACImpl *This = impl_from_IAudioClient(iface);
1647 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1649 if(!ppv)
1650 return E_POINTER;
1651 *ppv = NULL;
1653 OSSpinLockLock(&This->lock);
1655 if(!This->aqueue){
1656 OSSpinLockUnlock(&This->lock);
1657 return AUDCLNT_E_NOT_INITIALIZED;
1660 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1661 if(This->dataflow != eRender){
1662 OSSpinLockUnlock(&This->lock);
1663 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1665 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1666 *ppv = &This->IAudioRenderClient_iface;
1667 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1668 if(This->dataflow != eCapture){
1669 OSSpinLockUnlock(&This->lock);
1670 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1672 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1673 *ppv = &This->IAudioCaptureClient_iface;
1674 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1675 IAudioClock_AddRef(&This->IAudioClock_iface);
1676 *ppv = &This->IAudioClock_iface;
1677 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1678 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1679 *ppv = &This->IAudioStreamVolume_iface;
1680 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1681 if(!This->session_wrapper){
1682 This->session_wrapper = AudioSessionWrapper_Create(This);
1683 if(!This->session_wrapper){
1684 OSSpinLockUnlock(&This->lock);
1685 return E_OUTOFMEMORY;
1687 }else
1688 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1690 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1691 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1692 if(!This->session_wrapper){
1693 This->session_wrapper = AudioSessionWrapper_Create(This);
1694 if(!This->session_wrapper){
1695 OSSpinLockUnlock(&This->lock);
1696 return E_OUTOFMEMORY;
1698 }else
1699 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1701 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1702 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1703 if(!This->session_wrapper){
1704 This->session_wrapper = AudioSessionWrapper_Create(This);
1705 if(!This->session_wrapper){
1706 OSSpinLockUnlock(&This->lock);
1707 return E_OUTOFMEMORY;
1709 }else
1710 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1712 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1715 if(*ppv){
1716 OSSpinLockUnlock(&This->lock);
1717 return S_OK;
1720 OSSpinLockUnlock(&This->lock);
1722 FIXME("stub %s\n", debugstr_guid(riid));
1723 return E_NOINTERFACE;
1726 static const IAudioClientVtbl AudioClient_Vtbl =
1728 AudioClient_QueryInterface,
1729 AudioClient_AddRef,
1730 AudioClient_Release,
1731 AudioClient_Initialize,
1732 AudioClient_GetBufferSize,
1733 AudioClient_GetStreamLatency,
1734 AudioClient_GetCurrentPadding,
1735 AudioClient_IsFormatSupported,
1736 AudioClient_GetMixFormat,
1737 AudioClient_GetDevicePeriod,
1738 AudioClient_Start,
1739 AudioClient_Stop,
1740 AudioClient_Reset,
1741 AudioClient_SetEventHandle,
1742 AudioClient_GetService
1745 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1746 IAudioRenderClient *iface, REFIID riid, void **ppv)
1748 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1750 if(!ppv)
1751 return E_POINTER;
1752 *ppv = NULL;
1754 if(IsEqualIID(riid, &IID_IUnknown) ||
1755 IsEqualIID(riid, &IID_IAudioRenderClient))
1756 *ppv = iface;
1757 if(*ppv){
1758 IUnknown_AddRef((IUnknown*)*ppv);
1759 return S_OK;
1762 WARN("Unknown interface %s\n", debugstr_guid(riid));
1763 return E_NOINTERFACE;
1766 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1768 ACImpl *This = impl_from_IAudioRenderClient(iface);
1769 return AudioClient_AddRef(&This->IAudioClient_iface);
1772 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1774 ACImpl *This = impl_from_IAudioRenderClient(iface);
1775 return AudioClient_Release(&This->IAudioClient_iface);
1778 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1779 UINT32 frames, BYTE **data)
1781 ACImpl *This = impl_from_IAudioRenderClient(iface);
1782 AQBuffer *buf;
1783 UINT32 pad, bytes;
1784 HRESULT hr;
1785 OSStatus sc;
1787 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1789 if(!data)
1790 return E_POINTER;
1791 *data = NULL;
1793 OSSpinLockLock(&This->lock);
1795 if(This->getbuf_last){
1796 OSSpinLockUnlock(&This->lock);
1797 return AUDCLNT_E_OUT_OF_ORDER;
1800 if(!frames){
1801 OSSpinLockUnlock(&This->lock);
1802 return S_OK;
1805 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
1806 if(FAILED(hr)){
1807 OSSpinLockUnlock(&This->lock);
1808 return hr;
1811 if(pad + frames > This->bufsize_frames){
1812 OSSpinLockUnlock(&This->lock);
1813 return AUDCLNT_E_BUFFER_TOO_LARGE;
1816 bytes = frames * This->fmt->nBlockAlign;
1817 LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry){
1818 if(buf->buf->mAudioDataBytesCapacity >= bytes){
1819 This->public_buffer = buf->buf;
1820 list_remove(&buf->entry);
1821 break;
1825 if(&buf->entry == &This->avail_buffers){
1826 sc = AudioQueueAllocateBuffer(This->aqueue, bytes,
1827 &This->public_buffer);
1828 if(sc != noErr){
1829 This->public_buffer = NULL;
1830 OSSpinLockUnlock(&This->lock);
1831 WARN("Unable to allocate buffer: %lx\n", sc);
1832 return E_OUTOFMEMORY;
1834 buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
1835 if(!buf){
1836 AudioQueueFreeBuffer(This->aqueue, This->public_buffer);
1837 This->public_buffer = NULL;
1838 OSSpinLockUnlock(&This->lock);
1839 return E_OUTOFMEMORY;
1841 buf->used = FALSE;
1842 buf->buf = This->public_buffer;
1843 This->public_buffer->mUserData = buf;
1846 This->getbuf_last = frames;
1847 *data = This->public_buffer->mAudioData;
1849 OSSpinLockUnlock(&This->lock);
1851 return S_OK;
1854 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1855 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
1857 ACImpl *This = impl_from_IAudioRenderClient(iface);
1858 AQBuffer *buf;
1859 AudioTimeStamp start_time, req_time = {0}, *passed_time = NULL;
1860 OSStatus sc;
1862 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
1864 OSSpinLockLock(&This->lock);
1866 if(!frames){
1867 This->getbuf_last = 0;
1868 if(This->public_buffer){
1869 buf = This->public_buffer->mUserData;
1870 list_add_head(&This->avail_buffers, &buf->entry);
1871 This->public_buffer = NULL;
1873 OSSpinLockUnlock(&This->lock);
1874 return S_OK;
1877 if(!This->getbuf_last){
1878 OSSpinLockUnlock(&This->lock);
1879 return AUDCLNT_E_OUT_OF_ORDER;
1882 if(frames > This->getbuf_last){
1883 OSSpinLockUnlock(&This->lock);
1884 return AUDCLNT_E_INVALID_SIZE;
1887 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1888 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1889 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1890 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1891 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1892 This->fmt->wBitsPerSample == 8)
1893 memset(This->public_buffer->mAudioData, 128,
1894 frames * This->fmt->nBlockAlign);
1895 else
1896 memset(This->public_buffer->mAudioData, 0,
1897 frames * This->fmt->nBlockAlign);
1900 This->public_buffer->mAudioDataByteSize = frames * This->fmt->nBlockAlign;
1902 buf = This->public_buffer->mUserData;
1903 buf->used = TRUE;
1905 if(list_empty(&This->queued_bufinfos)){
1906 sc = AudioQueueGetCurrentTime(This->aqueue, NULL, &req_time, NULL);
1907 if(sc == noErr)
1908 passed_time = &req_time;
1909 else
1910 TRACE("AudioQueueGetCurrentTime failed: %lx\n", sc);
1911 }else{
1912 req_time.mSampleTime = This->next_sampletime;
1913 req_time.mFlags = kAudioTimeStampSampleTimeValid;
1914 passed_time = &req_time;
1917 sc = AudioQueueEnqueueBufferWithParameters(This->aqueue,
1918 This->public_buffer, 0, NULL, 0, 0, 0, NULL, passed_time,
1919 &start_time);
1920 if(sc != noErr){
1921 OSSpinLockUnlock(&This->lock);
1922 ERR("Unable to enqueue buffer: %lx\n", sc);
1923 return AUDCLNT_E_DEVICE_INVALIDATED;
1925 list_add_tail(&This->queued_buffers, &buf->entry);
1927 if(start_time.mFlags & kAudioTimeStampSampleTimeValid){
1928 QueuedBufInfo *bufinfo;
1930 bufinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*bufinfo));
1931 bufinfo->start_sampletime = start_time.mSampleTime;
1932 bufinfo->start_pos = This->written_frames;
1933 bufinfo->len_frames = frames;
1935 list_add_tail(&This->queued_bufinfos, &bufinfo->entry);
1937 This->next_sampletime = start_time.mSampleTime + bufinfo->len_frames;
1938 }else
1939 WARN("Start time didn't contain valid SampleTime member\n");
1941 if(This->playing == StateStopped)
1942 AudioQueuePrime(This->aqueue, 0, NULL);
1944 This->public_buffer = NULL;
1945 This->getbuf_last = 0;
1946 This->written_frames += frames;
1947 This->inbuf_frames += frames;
1949 OSSpinLockUnlock(&This->lock);
1951 return S_OK;
1954 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1955 AudioRenderClient_QueryInterface,
1956 AudioRenderClient_AddRef,
1957 AudioRenderClient_Release,
1958 AudioRenderClient_GetBuffer,
1959 AudioRenderClient_ReleaseBuffer
1962 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1963 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1965 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1967 if(!ppv)
1968 return E_POINTER;
1969 *ppv = NULL;
1971 if(IsEqualIID(riid, &IID_IUnknown) ||
1972 IsEqualIID(riid, &IID_IAudioCaptureClient))
1973 *ppv = iface;
1974 if(*ppv){
1975 IUnknown_AddRef((IUnknown*)*ppv);
1976 return S_OK;
1979 WARN("Unknown interface %s\n", debugstr_guid(riid));
1980 return E_NOINTERFACE;
1983 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1985 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1986 return IAudioClient_AddRef(&This->IAudioClient_iface);
1989 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1991 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1992 return IAudioClient_Release(&This->IAudioClient_iface);
1995 static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames)
1997 AQBuffer *buf;
1998 OSStatus sc;
2000 avail_update(This); /* once, not inside loop */
2002 for(;;){
2003 if(!This->public_buffer){
2004 struct list *head = list_head(&This->avail_buffers);
2006 if(!head){
2007 *frames = 0;
2008 return S_OK;
2010 buf = LIST_ENTRY(head, AQBuffer, entry);
2011 This->public_buffer = buf->buf;
2012 list_remove(&buf->entry);
2013 }else
2014 buf = This->public_buffer->mUserData;
2015 *frames = This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
2016 if(*frames)
2017 return S_OK;
2019 WARN("empty packet\n");
2020 buf->used = TRUE;
2021 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2022 if(sc != noErr){
2023 ERR("Unable to enqueue buffer: %lx\n", sc);
2024 /* Release will free This->public_buffer */
2025 return AUDCLNT_E_DEVICE_INVALIDATED;
2026 }else
2027 list_add_tail(&This->queued_buffers, &buf->entry);
2028 This->public_buffer = NULL;
2032 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2033 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2034 UINT64 *qpcpos)
2036 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2037 HRESULT hr;
2039 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2040 devpos, qpcpos);
2042 if(!data || !frames || !flags)
2043 return E_POINTER;
2045 OSSpinLockLock(&This->lock);
2047 if(This->getbuf_last){
2048 OSSpinLockUnlock(&This->lock);
2049 return AUDCLNT_E_OUT_OF_ORDER;
2052 hr = AudioCaptureClient_GetNextPacket(This, frames);
2053 if(FAILED(hr)){
2054 OSSpinLockUnlock(&This->lock);
2055 return hr;
2058 if((This->getbuf_last = *frames)){
2059 *flags = 0;
2060 *data = This->public_buffer->mAudioData;
2062 if(devpos)
2063 *devpos = This->written_frames;
2064 if(qpcpos){ /* fixme: qpc of recording time */
2065 LARGE_INTEGER stamp, freq;
2066 QueryPerformanceCounter(&stamp);
2067 QueryPerformanceFrequency(&freq);
2068 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2071 OSSpinLockUnlock(&This->lock);
2073 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2076 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2077 IAudioCaptureClient *iface, UINT32 done)
2079 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2080 AQBuffer *buf;
2081 OSStatus sc;
2083 TRACE("(%p)->(%u)\n", This, done);
2085 OSSpinLockLock(&This->lock);
2087 if(!done){
2088 This->getbuf_last = 0;
2089 OSSpinLockUnlock(&This->lock);
2090 return S_OK;
2093 if(!This->getbuf_last){
2094 OSSpinLockUnlock(&This->lock);
2095 return AUDCLNT_E_OUT_OF_ORDER;
2098 if(This->getbuf_last != done){
2099 OSSpinLockUnlock(&This->lock);
2100 return AUDCLNT_E_INVALID_SIZE;
2103 This->written_frames += done;
2104 This->inbuf_frames -= done;
2105 This->getbuf_last = 0;
2107 buf = This->public_buffer->mUserData;
2108 buf->used = TRUE;
2109 sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
2110 if(sc != noErr){
2111 OSSpinLockUnlock(&This->lock);
2112 /* fixme: can't zero public_buffer or we lose memory, but then
2113 * GetBuffer will see that packet again and again. */
2114 ERR("Unable to enqueue buffer: %lx\n", sc);
2115 return AUDCLNT_E_DEVICE_INVALIDATED;
2116 }else
2117 list_add_tail(&This->queued_buffers, &buf->entry);
2118 This->public_buffer = NULL;
2120 OSSpinLockUnlock(&This->lock);
2122 return S_OK;
2125 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2126 IAudioCaptureClient *iface, UINT32 *frames)
2128 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2129 HRESULT hr;
2131 TRACE("(%p)->(%p)\n", This, frames);
2133 if(!frames)
2134 return E_POINTER;
2136 OSSpinLockLock(&This->lock);
2138 hr = AudioCaptureClient_GetNextPacket(This, frames);
2140 OSSpinLockUnlock(&This->lock);
2142 return hr;
2145 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2147 AudioCaptureClient_QueryInterface,
2148 AudioCaptureClient_AddRef,
2149 AudioCaptureClient_Release,
2150 AudioCaptureClient_GetBuffer,
2151 AudioCaptureClient_ReleaseBuffer,
2152 AudioCaptureClient_GetNextPacketSize
2155 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2156 REFIID riid, void **ppv)
2158 ACImpl *This = impl_from_IAudioClock(iface);
2160 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2162 if(!ppv)
2163 return E_POINTER;
2164 *ppv = NULL;
2166 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2167 *ppv = iface;
2168 else if(IsEqualIID(riid, &IID_IAudioClock2))
2169 *ppv = &This->IAudioClock2_iface;
2170 if(*ppv){
2171 IUnknown_AddRef((IUnknown*)*ppv);
2172 return S_OK;
2175 WARN("Unknown interface %s\n", debugstr_guid(riid));
2176 return E_NOINTERFACE;
2179 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2181 ACImpl *This = impl_from_IAudioClock(iface);
2182 return IAudioClient_AddRef(&This->IAudioClient_iface);
2185 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2187 ACImpl *This = impl_from_IAudioClock(iface);
2188 return IAudioClient_Release(&This->IAudioClient_iface);
2191 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2193 ACImpl *This = impl_from_IAudioClock(iface);
2195 TRACE("(%p)->(%p)\n", This, freq);
2197 *freq = This->fmt->nSamplesPerSec;
2199 return S_OK;
2202 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2203 UINT64 *pos, UINT64 *qpctime)
2205 avail_update(This);
2207 if(This->dataflow == eRender)
2208 *pos = get_current_aqbuffer_position(This, BUFPOS_ABSOLUTE);
2209 else
2210 *pos = This->inbuf_frames + This->written_frames;
2212 if(qpctime){
2213 LARGE_INTEGER stamp, freq;
2214 QueryPerformanceCounter(&stamp);
2215 QueryPerformanceFrequency(&freq);
2216 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2219 return S_OK;
2222 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2223 UINT64 *qpctime)
2225 ACImpl *This = impl_from_IAudioClock(iface);
2226 HRESULT hr;
2228 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2230 if(!pos)
2231 return E_POINTER;
2233 OSSpinLockLock(&This->lock);
2235 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2237 OSSpinLockUnlock(&This->lock);
2239 return hr;
2242 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2243 DWORD *chars)
2245 ACImpl *This = impl_from_IAudioClock(iface);
2247 TRACE("(%p)->(%p)\n", This, chars);
2249 if(!chars)
2250 return E_POINTER;
2252 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2254 return S_OK;
2257 static const IAudioClockVtbl AudioClock_Vtbl =
2259 AudioClock_QueryInterface,
2260 AudioClock_AddRef,
2261 AudioClock_Release,
2262 AudioClock_GetFrequency,
2263 AudioClock_GetPosition,
2264 AudioClock_GetCharacteristics
2267 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2268 REFIID riid, void **ppv)
2270 ACImpl *This = impl_from_IAudioClock2(iface);
2271 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2274 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2276 ACImpl *This = impl_from_IAudioClock2(iface);
2277 return IAudioClient_AddRef(&This->IAudioClient_iface);
2280 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2282 ACImpl *This = impl_from_IAudioClock2(iface);
2283 return IAudioClient_Release(&This->IAudioClient_iface);
2286 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2287 UINT64 *pos, UINT64 *qpctime)
2289 ACImpl *This = impl_from_IAudioClock2(iface);
2291 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2293 return E_NOTIMPL;
2296 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2298 AudioClock2_QueryInterface,
2299 AudioClock2_AddRef,
2300 AudioClock2_Release,
2301 AudioClock2_GetDevicePosition
2304 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2306 AudioSessionWrapper *ret;
2308 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2309 sizeof(AudioSessionWrapper));
2310 if(!ret)
2311 return NULL;
2313 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2314 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2315 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2317 ret->ref = 1;
2319 ret->client = client;
2320 if(client){
2321 ret->session = client->session;
2322 AudioClient_AddRef(&client->IAudioClient_iface);
2325 return ret;
2328 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2329 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2331 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2333 if(!ppv)
2334 return E_POINTER;
2335 *ppv = NULL;
2337 if(IsEqualIID(riid, &IID_IUnknown) ||
2338 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2339 IsEqualIID(riid, &IID_IAudioSessionControl2))
2340 *ppv = iface;
2341 if(*ppv){
2342 IUnknown_AddRef((IUnknown*)*ppv);
2343 return S_OK;
2346 WARN("Unknown interface %s\n", debugstr_guid(riid));
2347 return E_NOINTERFACE;
2350 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2352 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2353 ULONG ref;
2354 ref = InterlockedIncrement(&This->ref);
2355 TRACE("(%p) Refcount now %u\n", This, ref);
2356 return ref;
2359 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2361 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2362 ULONG ref;
2363 ref = InterlockedDecrement(&This->ref);
2364 TRACE("(%p) Refcount now %u\n", This, ref);
2365 if(!ref){
2366 if(This->client){
2367 OSSpinLockLock(&This->client->lock);
2368 This->client->session_wrapper = NULL;
2369 OSSpinLockUnlock(&This->client->lock);
2370 AudioClient_Release(&This->client->IAudioClient_iface);
2372 HeapFree(GetProcessHeap(), 0, This);
2374 return ref;
2377 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2378 AudioSessionState *state)
2380 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2381 ACImpl *client;
2383 TRACE("(%p)->(%p)\n", This, state);
2385 if(!state)
2386 return NULL_PTR_ERR;
2388 EnterCriticalSection(&g_sessions_lock);
2390 if(list_empty(&This->session->clients)){
2391 *state = AudioSessionStateExpired;
2392 LeaveCriticalSection(&g_sessions_lock);
2393 return S_OK;
2396 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2397 OSSpinLockLock(&client->lock);
2398 if(client->playing == StatePlaying ||
2399 client->playing == StateInTransition){
2400 *state = AudioSessionStateActive;
2401 OSSpinLockUnlock(&client->lock);
2402 LeaveCriticalSection(&g_sessions_lock);
2403 return S_OK;
2405 OSSpinLockUnlock(&client->lock);
2408 LeaveCriticalSection(&g_sessions_lock);
2410 *state = AudioSessionStateInactive;
2412 return S_OK;
2415 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2416 IAudioSessionControl2 *iface, WCHAR **name)
2418 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2420 FIXME("(%p)->(%p) - stub\n", This, name);
2422 return E_NOTIMPL;
2425 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2426 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2428 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2430 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2432 return E_NOTIMPL;
2435 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2436 IAudioSessionControl2 *iface, WCHAR **path)
2438 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2440 FIXME("(%p)->(%p) - stub\n", This, path);
2442 return E_NOTIMPL;
2445 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2446 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2448 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2450 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2452 return E_NOTIMPL;
2455 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2456 IAudioSessionControl2 *iface, GUID *group)
2458 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2460 FIXME("(%p)->(%p) - stub\n", This, group);
2462 return E_NOTIMPL;
2465 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2466 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2468 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2470 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2471 debugstr_guid(session));
2473 return E_NOTIMPL;
2476 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2477 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2479 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2481 FIXME("(%p)->(%p) - stub\n", This, events);
2483 return S_OK;
2486 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2487 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2489 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2491 FIXME("(%p)->(%p) - stub\n", This, events);
2493 return S_OK;
2496 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2497 IAudioSessionControl2 *iface, WCHAR **id)
2499 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2501 FIXME("(%p)->(%p) - stub\n", This, id);
2503 return E_NOTIMPL;
2506 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2507 IAudioSessionControl2 *iface, WCHAR **id)
2509 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2511 FIXME("(%p)->(%p) - stub\n", This, id);
2513 return E_NOTIMPL;
2516 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2517 IAudioSessionControl2 *iface, DWORD *pid)
2519 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2521 TRACE("(%p)->(%p)\n", This, pid);
2523 if(!pid)
2524 return E_POINTER;
2526 *pid = GetCurrentProcessId();
2528 return S_OK;
2531 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2532 IAudioSessionControl2 *iface)
2534 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2536 TRACE("(%p)\n", This);
2538 return S_FALSE;
2541 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2542 IAudioSessionControl2 *iface, BOOL optout)
2544 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2546 TRACE("(%p)->(%d)\n", This, optout);
2548 return S_OK;
2551 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2553 AudioSessionControl_QueryInterface,
2554 AudioSessionControl_AddRef,
2555 AudioSessionControl_Release,
2556 AudioSessionControl_GetState,
2557 AudioSessionControl_GetDisplayName,
2558 AudioSessionControl_SetDisplayName,
2559 AudioSessionControl_GetIconPath,
2560 AudioSessionControl_SetIconPath,
2561 AudioSessionControl_GetGroupingParam,
2562 AudioSessionControl_SetGroupingParam,
2563 AudioSessionControl_RegisterAudioSessionNotification,
2564 AudioSessionControl_UnregisterAudioSessionNotification,
2565 AudioSessionControl_GetSessionIdentifier,
2566 AudioSessionControl_GetSessionInstanceIdentifier,
2567 AudioSessionControl_GetProcessId,
2568 AudioSessionControl_IsSystemSoundsSession,
2569 AudioSessionControl_SetDuckingPreference
2572 /* index == -1 means set all channels, otherwise sets only the given channel */
2573 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2575 float level;
2576 OSStatus sc;
2578 if(index == (UINT32)-1){
2579 HRESULT ret = S_OK;
2580 UINT32 i;
2581 for(i = 0; i < This->fmt->nChannels; ++i){
2582 HRESULT hr;
2583 hr = ca_setvol(This, i);
2584 if(FAILED(hr))
2585 ret = hr;
2587 return ret;
2590 if(This->session->mute)
2591 level = 0;
2592 else
2593 level = This->session->master_vol *
2594 This->session->channel_vols[index] * This->vols[index];
2596 sc = AudioQueueSetParameter(This->aqueue, kAudioQueueParam_Volume, level);
2597 if(sc != noErr)
2598 WARN("Setting _Volume property failed: %lx\n", sc);
2600 return S_OK;
2603 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
2605 HRESULT ret = S_OK;
2606 ACImpl *client;
2608 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2609 HRESULT hr;
2610 hr = ca_setvol(client, index);
2611 if(FAILED(hr))
2612 ret = hr;
2615 return ret;
2618 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2619 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2621 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2623 if(!ppv)
2624 return E_POINTER;
2625 *ppv = NULL;
2627 if(IsEqualIID(riid, &IID_IUnknown) ||
2628 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2629 *ppv = iface;
2630 if(*ppv){
2631 IUnknown_AddRef((IUnknown*)*ppv);
2632 return S_OK;
2635 WARN("Unknown interface %s\n", debugstr_guid(riid));
2636 return E_NOINTERFACE;
2639 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2641 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2642 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2645 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2647 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2648 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2651 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2652 ISimpleAudioVolume *iface, float level, const GUID *context)
2654 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2655 AudioSession *session = This->session;
2656 HRESULT ret;
2658 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2660 if(level < 0.f || level > 1.f)
2661 return E_INVALIDARG;
2663 if(context)
2664 FIXME("Notifications not supported yet\n");
2666 EnterCriticalSection(&session->lock);
2668 session->master_vol = level;
2670 ret = ca_session_setvol(session, -1);
2672 LeaveCriticalSection(&session->lock);
2674 return ret;
2677 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2678 ISimpleAudioVolume *iface, float *level)
2680 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2681 AudioSession *session = This->session;
2683 TRACE("(%p)->(%p)\n", session, level);
2685 if(!level)
2686 return NULL_PTR_ERR;
2688 *level = session->master_vol;
2690 return S_OK;
2693 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2694 BOOL mute, const GUID *context)
2696 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2697 AudioSession *session = This->session;
2699 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2701 if(context)
2702 FIXME("Notifications not supported yet\n");
2704 EnterCriticalSection(&session->lock);
2706 session->mute = mute;
2708 ca_session_setvol(session, -1);
2710 LeaveCriticalSection(&session->lock);
2712 return S_OK;
2715 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2716 BOOL *mute)
2718 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2719 AudioSession *session = This->session;
2721 TRACE("(%p)->(%p)\n", session, mute);
2723 if(!mute)
2724 return NULL_PTR_ERR;
2726 *mute = session->mute;
2728 return S_OK;
2731 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2733 SimpleAudioVolume_QueryInterface,
2734 SimpleAudioVolume_AddRef,
2735 SimpleAudioVolume_Release,
2736 SimpleAudioVolume_SetMasterVolume,
2737 SimpleAudioVolume_GetMasterVolume,
2738 SimpleAudioVolume_SetMute,
2739 SimpleAudioVolume_GetMute
2742 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2743 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2745 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2747 if(!ppv)
2748 return E_POINTER;
2749 *ppv = NULL;
2751 if(IsEqualIID(riid, &IID_IUnknown) ||
2752 IsEqualIID(riid, &IID_IAudioStreamVolume))
2753 *ppv = iface;
2754 if(*ppv){
2755 IUnknown_AddRef((IUnknown*)*ppv);
2756 return S_OK;
2759 WARN("Unknown interface %s\n", debugstr_guid(riid));
2760 return E_NOINTERFACE;
2763 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2765 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2766 return IAudioClient_AddRef(&This->IAudioClient_iface);
2769 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2771 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2772 return IAudioClient_Release(&This->IAudioClient_iface);
2775 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2776 IAudioStreamVolume *iface, UINT32 *out)
2778 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2780 TRACE("(%p)->(%p)\n", This, out);
2782 if(!out)
2783 return E_POINTER;
2785 *out = This->fmt->nChannels;
2787 return S_OK;
2790 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2791 IAudioStreamVolume *iface, UINT32 index, float level)
2793 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2794 HRESULT ret;
2796 TRACE("(%p)->(%d, %f)\n", This, index, level);
2798 if(level < 0.f || level > 1.f)
2799 return E_INVALIDARG;
2801 if(index >= This->fmt->nChannels)
2802 return E_INVALIDARG;
2804 OSSpinLockLock(&This->lock);
2806 This->vols[index] = level;
2808 WARN("AudioQueue doesn't support per-channel volume control\n");
2809 ret = ca_setvol(This, index);
2811 OSSpinLockUnlock(&This->lock);
2813 return ret;
2816 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2817 IAudioStreamVolume *iface, UINT32 index, float *level)
2819 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2821 TRACE("(%p)->(%d, %p)\n", This, index, level);
2823 if(!level)
2824 return E_POINTER;
2826 if(index >= This->fmt->nChannels)
2827 return E_INVALIDARG;
2829 *level = This->vols[index];
2831 return S_OK;
2834 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2835 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2837 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2838 int i;
2839 HRESULT ret;
2841 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2843 if(!levels)
2844 return E_POINTER;
2846 if(count != This->fmt->nChannels)
2847 return E_INVALIDARG;
2849 OSSpinLockLock(&This->lock);
2851 for(i = 0; i < count; ++i)
2852 This->vols[i] = levels[i];
2854 ret = ca_setvol(This, -1);
2856 OSSpinLockUnlock(&This->lock);
2858 return ret;
2861 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2862 IAudioStreamVolume *iface, UINT32 count, float *levels)
2864 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2865 int i;
2867 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2869 if(!levels)
2870 return E_POINTER;
2872 if(count != This->fmt->nChannels)
2873 return E_INVALIDARG;
2875 OSSpinLockLock(&This->lock);
2877 for(i = 0; i < count; ++i)
2878 levels[i] = This->vols[i];
2880 OSSpinLockUnlock(&This->lock);
2882 return S_OK;
2885 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2887 AudioStreamVolume_QueryInterface,
2888 AudioStreamVolume_AddRef,
2889 AudioStreamVolume_Release,
2890 AudioStreamVolume_GetChannelCount,
2891 AudioStreamVolume_SetChannelVolume,
2892 AudioStreamVolume_GetChannelVolume,
2893 AudioStreamVolume_SetAllVolumes,
2894 AudioStreamVolume_GetAllVolumes
2897 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2898 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2900 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2902 if(!ppv)
2903 return E_POINTER;
2904 *ppv = NULL;
2906 if(IsEqualIID(riid, &IID_IUnknown) ||
2907 IsEqualIID(riid, &IID_IChannelAudioVolume))
2908 *ppv = iface;
2909 if(*ppv){
2910 IUnknown_AddRef((IUnknown*)*ppv);
2911 return S_OK;
2914 WARN("Unknown interface %s\n", debugstr_guid(riid));
2915 return E_NOINTERFACE;
2918 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2920 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2921 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2924 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2926 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2927 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2930 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2931 IChannelAudioVolume *iface, UINT32 *out)
2933 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2934 AudioSession *session = This->session;
2936 TRACE("(%p)->(%p)\n", session, out);
2938 if(!out)
2939 return NULL_PTR_ERR;
2941 *out = session->channel_count;
2943 return S_OK;
2946 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2947 IChannelAudioVolume *iface, UINT32 index, float level,
2948 const GUID *context)
2950 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2951 AudioSession *session = This->session;
2952 HRESULT ret;
2954 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2955 wine_dbgstr_guid(context));
2957 if(level < 0.f || level > 1.f)
2958 return E_INVALIDARG;
2960 if(index >= session->channel_count)
2961 return E_INVALIDARG;
2963 if(context)
2964 FIXME("Notifications not supported yet\n");
2966 EnterCriticalSection(&session->lock);
2968 session->channel_vols[index] = level;
2970 WARN("AudioQueue doesn't support per-channel volume control\n");
2971 ret = ca_session_setvol(session, index);
2973 LeaveCriticalSection(&session->lock);
2975 return ret;
2978 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2979 IChannelAudioVolume *iface, UINT32 index, float *level)
2981 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2982 AudioSession *session = This->session;
2984 TRACE("(%p)->(%d, %p)\n", session, index, level);
2986 if(!level)
2987 return NULL_PTR_ERR;
2989 if(index >= session->channel_count)
2990 return E_INVALIDARG;
2992 *level = session->channel_vols[index];
2994 return S_OK;
2997 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2998 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2999 const GUID *context)
3001 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3002 AudioSession *session = This->session;
3003 int i;
3004 HRESULT ret;
3006 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3007 wine_dbgstr_guid(context));
3009 if(!levels)
3010 return NULL_PTR_ERR;
3012 if(count != session->channel_count)
3013 return E_INVALIDARG;
3015 if(context)
3016 FIXME("Notifications not supported yet\n");
3018 EnterCriticalSection(&session->lock);
3020 for(i = 0; i < count; ++i)
3021 session->channel_vols[i] = levels[i];
3023 ret = ca_session_setvol(session, -1);
3025 LeaveCriticalSection(&session->lock);
3027 return ret;
3030 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3031 IChannelAudioVolume *iface, UINT32 count, float *levels)
3033 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3034 AudioSession *session = This->session;
3035 int i;
3037 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3039 if(!levels)
3040 return NULL_PTR_ERR;
3042 if(count != session->channel_count)
3043 return E_INVALIDARG;
3045 for(i = 0; i < count; ++i)
3046 levels[i] = session->channel_vols[i];
3048 return S_OK;
3051 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3053 ChannelAudioVolume_QueryInterface,
3054 ChannelAudioVolume_AddRef,
3055 ChannelAudioVolume_Release,
3056 ChannelAudioVolume_GetChannelCount,
3057 ChannelAudioVolume_SetChannelVolume,
3058 ChannelAudioVolume_GetChannelVolume,
3059 ChannelAudioVolume_SetAllVolumes,
3060 ChannelAudioVolume_GetAllVolumes
3063 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3064 REFIID riid, void **ppv)
3066 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3068 if(!ppv)
3069 return E_POINTER;
3070 *ppv = NULL;
3072 if(IsEqualIID(riid, &IID_IUnknown) ||
3073 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3074 IsEqualIID(riid, &IID_IAudioSessionManager2))
3075 *ppv = iface;
3076 if(*ppv){
3077 IUnknown_AddRef((IUnknown*)*ppv);
3078 return S_OK;
3081 WARN("Unknown interface %s\n", debugstr_guid(riid));
3082 return E_NOINTERFACE;
3085 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3087 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3088 ULONG ref;
3089 ref = InterlockedIncrement(&This->ref);
3090 TRACE("(%p) Refcount now %u\n", This, ref);
3091 return ref;
3094 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3096 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3097 ULONG ref;
3098 ref = InterlockedDecrement(&This->ref);
3099 TRACE("(%p) Refcount now %u\n", This, ref);
3100 if(!ref)
3101 HeapFree(GetProcessHeap(), 0, This);
3102 return ref;
3105 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3106 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3107 IAudioSessionControl **out)
3109 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3110 AudioSession *session;
3111 AudioSessionWrapper *wrapper;
3112 HRESULT hr;
3114 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3115 flags, out);
3117 hr = get_audio_session(session_guid, This->device, 0, &session);
3118 if(FAILED(hr))
3119 return hr;
3121 wrapper = AudioSessionWrapper_Create(NULL);
3122 if(!wrapper)
3123 return E_OUTOFMEMORY;
3125 wrapper->session = session;
3127 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3129 return S_OK;
3132 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3133 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3134 ISimpleAudioVolume **out)
3136 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3137 AudioSession *session;
3138 AudioSessionWrapper *wrapper;
3139 HRESULT hr;
3141 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3142 flags, out);
3144 hr = get_audio_session(session_guid, This->device, 0, &session);
3145 if(FAILED(hr))
3146 return hr;
3148 wrapper = AudioSessionWrapper_Create(NULL);
3149 if(!wrapper)
3150 return E_OUTOFMEMORY;
3152 wrapper->session = session;
3154 *out = &wrapper->ISimpleAudioVolume_iface;
3156 return S_OK;
3159 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3160 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3162 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3163 FIXME("(%p)->(%p) - stub\n", This, out);
3164 return E_NOTIMPL;
3167 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3168 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3170 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3171 FIXME("(%p)->(%p) - stub\n", This, notification);
3172 return E_NOTIMPL;
3175 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3176 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3178 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3179 FIXME("(%p)->(%p) - stub\n", This, notification);
3180 return E_NOTIMPL;
3183 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3184 IAudioSessionManager2 *iface, const WCHAR *session_id,
3185 IAudioVolumeDuckNotification *notification)
3187 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3188 FIXME("(%p)->(%p) - stub\n", This, notification);
3189 return E_NOTIMPL;
3192 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3193 IAudioSessionManager2 *iface,
3194 IAudioVolumeDuckNotification *notification)
3196 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3197 FIXME("(%p)->(%p) - stub\n", This, notification);
3198 return E_NOTIMPL;
3201 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3203 AudioSessionManager_QueryInterface,
3204 AudioSessionManager_AddRef,
3205 AudioSessionManager_Release,
3206 AudioSessionManager_GetAudioSessionControl,
3207 AudioSessionManager_GetSimpleAudioVolume,
3208 AudioSessionManager_GetSessionEnumerator,
3209 AudioSessionManager_RegisterSessionNotification,
3210 AudioSessionManager_UnregisterSessionNotification,
3211 AudioSessionManager_RegisterDuckNotification,
3212 AudioSessionManager_UnregisterDuckNotification
3215 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3216 IAudioSessionManager2 **out)
3218 SessionMgr *This;
3220 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3221 if(!This)
3222 return E_OUTOFMEMORY;
3224 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3225 This->device = device;
3226 This->ref = 1;
3228 *out = &This->IAudioSessionManager2_iface;
3230 return S_OK;