wined3d: Get rid of the "render_to_fbo" field from the wined3d_swapchain structure.
[wine.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobf07f4bae5fb211dabd23ddfc12c889aafb701ab9
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 #define LoadResource __carbon_LoadResource
24 #define CompareString __carbon_CompareString
25 #define GetCurrentThread __carbon_GetCurrentThread
26 #define GetCurrentProcess __carbon_GetCurrentProcess
28 #include <stdarg.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/ioctl.h>
38 #include <fcntl.h>
39 #include <fenv.h>
40 #include <unistd.h>
42 #include <libkern/OSAtomic.h>
43 #include <CoreAudio/CoreAudio.h>
44 #include <AudioToolbox/AudioFormat.h>
45 #include <AudioToolbox/AudioConverter.h>
46 #include <AudioUnit/AudioUnit.h>
48 #undef LoadResource
49 #undef CompareString
50 #undef GetCurrentThread
51 #undef GetCurrentProcess
52 #undef _CDECL
54 #include "windef.h"
55 #include "winbase.h"
56 #include "winnls.h"
57 #include "winreg.h"
58 #include "wine/debug.h"
59 #include "wine/unicode.h"
60 #include "wine/list.h"
62 #include "ole2.h"
63 #include "mmdeviceapi.h"
64 #include "devpkey.h"
65 #include "dshow.h"
66 #include "dsound.h"
68 #include "initguid.h"
69 #include "endpointvolume.h"
70 #include "audioclient.h"
71 #include "audiopolicy.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
75 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
77 static const REFERENCE_TIME DefaultPeriod = 100000;
78 static const REFERENCE_TIME MinimumPeriod = 50000;
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 IAudioClient3 IAudioClient3_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;
121 IUnknown *pUnkFTMarshal;
123 WAVEFORMATEX *fmt;
125 EDataFlow dataflow;
126 DWORD flags;
127 AUDCLNT_SHAREMODE share;
128 HANDLE event;
129 float *vols;
131 BOOL initted;
132 AudioDeviceID adevid;
133 AudioObjectPropertyScope scope;
134 AudioConverterRef converter;
135 AudioComponentInstance unit;
136 AudioStreamBasicDescription dev_desc; /* audio unit format, not necessarily the same as fmt */
137 HANDLE timer;
138 UINT32 period_ms, bufsize_frames, period_frames;
139 UINT64 written_frames;
140 UINT32 lcl_offs_frames, wri_offs_frames, held_frames, tmp_buffer_frames;
141 UINT32 cap_bufsize_frames, cap_offs_frames, cap_held_frames, wrap_bufsize_frames, resamp_bufsize_frames;
142 INT32 getbuf_last;
143 BOOL playing;
144 BYTE *cap_buffer, *wrap_buffer, *resamp_buffer, *local_buffer, *tmp_buffer;
146 AudioSession *session;
147 AudioSessionWrapper *session_wrapper;
149 struct list entry;
151 OSSpinLock lock;
154 static const IAudioClient3Vtbl AudioClient3_Vtbl;
155 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
156 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
157 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
158 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
159 static const IAudioClockVtbl AudioClock_Vtbl;
160 static const IAudioClock2Vtbl AudioClock2_Vtbl;
161 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
162 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
163 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
165 typedef struct _SessionMgr {
166 IAudioSessionManager2 IAudioSessionManager2_iface;
168 LONG ref;
170 IMMDevice *device;
171 } SessionMgr;
173 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
174 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
175 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v','\\','d','e','v','i','c','e','s',0};
176 static const WCHAR guidW[] = {'g','u','i','d',0};
178 static HANDLE g_timer_q;
180 static CRITICAL_SECTION g_sessions_lock;
181 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
183 0, 0, &g_sessions_lock,
184 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
185 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
187 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
188 static struct list g_sessions = LIST_INIT(g_sessions);
190 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
191 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
193 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
195 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
198 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
200 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
203 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
205 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
208 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
210 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
213 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
215 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
218 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
220 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
223 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
225 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
228 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
230 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
233 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
235 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
238 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
240 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
243 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
245 switch (reason)
247 case DLL_PROCESS_ATTACH:
248 g_timer_q = CreateTimerQueue();
249 if(!g_timer_q)
250 return FALSE;
251 break;
253 case DLL_PROCESS_DETACH:
254 if (reserved) break;
255 DeleteCriticalSection(&g_sessions_lock);
256 break;
258 return TRUE;
261 /* From <dlls/mmdevapi/mmdevapi.h> */
262 enum DriverPriority {
263 Priority_Unavailable = 0,
264 Priority_Low,
265 Priority_Neutral,
266 Priority_Preferred
269 int WINAPI AUDDRV_GetPriority(void)
271 return Priority_Neutral;
274 static HRESULT osstatus_to_hresult(OSStatus sc)
276 switch(sc){
277 case kAudioFormatUnsupportedDataFormatError:
278 case kAudioFormatUnknownFormatError:
279 case kAudioDeviceUnsupportedFormatError:
280 return AUDCLNT_E_UNSUPPORTED_FORMAT;
281 case kAudioHardwareBadDeviceError:
282 return AUDCLNT_E_DEVICE_INVALIDATED;
284 return E_FAIL;
287 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
288 GUID *guid)
290 HKEY key;
291 BOOL opened = FALSE;
292 LONG lr;
294 if(!drv_key){
295 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
296 NULL, &drv_key, NULL);
297 if(lr != ERROR_SUCCESS){
298 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
299 return;
301 opened = TRUE;
304 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
305 NULL, &key, NULL);
306 if(lr != ERROR_SUCCESS){
307 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
308 goto exit;
311 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
312 sizeof(GUID));
313 if(lr != ERROR_SUCCESS)
314 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
316 RegCloseKey(key);
317 exit:
318 if(opened)
319 RegCloseKey(drv_key);
322 static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid)
324 HKEY key = NULL, dev_key;
325 DWORD type, size = sizeof(*guid);
326 WCHAR key_name[256];
328 static const WCHAR key_fmt[] = {'%','u',0};
330 if(flow == eCapture)
331 key_name[0] = '1';
332 else
333 key_name[0] = '0';
334 key_name[1] = ',';
336 sprintfW(key_name + 2, key_fmt, device);
338 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
339 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
340 if(RegQueryValueExW(dev_key, guidW, 0, &type,
341 (BYTE*)guid, &size) == ERROR_SUCCESS){
342 if(type == REG_BINARY){
343 RegCloseKey(dev_key);
344 RegCloseKey(key);
345 return;
347 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
348 wine_dbgstr_w(key_name), type);
350 RegCloseKey(dev_key);
354 CoCreateGuid(guid);
356 set_device_guid(flow, key, key_name, guid);
358 if(key)
359 RegCloseKey(key);
362 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
363 GUID **guids, UINT *num, UINT *def_index)
365 UInt32 devsize, size;
366 AudioDeviceID *devices;
367 AudioDeviceID default_id;
368 AudioObjectPropertyAddress addr;
369 OSStatus sc;
370 int i, ndevices;
372 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
374 addr.mScope = kAudioObjectPropertyScopeGlobal;
375 addr.mElement = kAudioObjectPropertyElementMaster;
376 if(flow == eRender)
377 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
378 else if(flow == eCapture)
379 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
380 else
381 return E_INVALIDARG;
383 size = sizeof(default_id);
384 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
385 NULL, &size, &default_id);
386 if(sc != noErr){
387 WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc);
388 default_id = -1;
391 addr.mSelector = kAudioHardwarePropertyDevices;
392 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
393 NULL, &devsize);
394 if(sc != noErr){
395 WARN("Getting _Devices property size failed: %x\n", (int)sc);
396 return osstatus_to_hresult(sc);
399 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
400 if(!devices)
401 return E_OUTOFMEMORY;
403 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
404 &devsize, devices);
405 if(sc != noErr){
406 WARN("Getting _Devices property failed: %x\n", (int)sc);
407 HeapFree(GetProcessHeap(), 0, devices);
408 return osstatus_to_hresult(sc);
411 ndevices = devsize / sizeof(AudioDeviceID);
413 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
414 if(!*ids){
415 HeapFree(GetProcessHeap(), 0, devices);
416 return E_OUTOFMEMORY;
419 *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
420 if(!*guids){
421 HeapFree(GetProcessHeap(), 0, *ids);
422 HeapFree(GetProcessHeap(), 0, devices);
423 return E_OUTOFMEMORY;
426 *num = 0;
427 *def_index = (UINT)-1;
428 for(i = 0; i < ndevices; ++i){
429 AudioBufferList *buffers;
430 CFStringRef name;
431 SIZE_T len;
432 int j;
434 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
435 if(flow == eRender)
436 addr.mScope = kAudioDevicePropertyScopeOutput;
437 else
438 addr.mScope = kAudioDevicePropertyScopeInput;
439 addr.mElement = 0;
440 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
441 if(sc != noErr){
442 WARN("Unable to get _StreamConfiguration property size for "
443 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
444 continue;
447 buffers = HeapAlloc(GetProcessHeap(), 0, size);
448 if(!buffers){
449 HeapFree(GetProcessHeap(), 0, devices);
450 for(j = 0; j < *num; ++j)
451 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
452 HeapFree(GetProcessHeap(), 0, *guids);
453 HeapFree(GetProcessHeap(), 0, *ids);
454 return E_OUTOFMEMORY;
457 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
458 &size, buffers);
459 if(sc != noErr){
460 WARN("Unable to get _StreamConfiguration property for "
461 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
462 HeapFree(GetProcessHeap(), 0, buffers);
463 continue;
466 /* check that there's at least one channel in this device before
467 * we claim it as usable */
468 for(j = 0; j < buffers->mNumberBuffers; ++j)
469 if(buffers->mBuffers[j].mNumberChannels > 0)
470 break;
471 if(j >= buffers->mNumberBuffers){
472 HeapFree(GetProcessHeap(), 0, buffers);
473 continue;
476 HeapFree(GetProcessHeap(), 0, buffers);
478 size = sizeof(name);
479 addr.mSelector = kAudioObjectPropertyName;
480 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
481 &size, &name);
482 if(sc != noErr){
483 WARN("Unable to get _Name property for device %u: %x\n",
484 (unsigned int)devices[i], (int)sc);
485 continue;
488 len = CFStringGetLength(name) + 1;
489 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
490 if(!(*ids)[*num]){
491 CFRelease(name);
492 HeapFree(GetProcessHeap(), 0, devices);
493 for(j = 0; j < *num; ++j)
494 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
495 HeapFree(GetProcessHeap(), 0, *ids);
496 HeapFree(GetProcessHeap(), 0, *guids);
497 return E_OUTOFMEMORY;
499 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
500 ((*ids)[*num])[len - 1] = 0;
501 CFRelease(name);
503 get_device_guid(flow, devices[i], &(*guids)[*num]);
505 if(*def_index == (UINT)-1 && devices[i] == default_id)
506 *def_index = *num;
508 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
509 (unsigned int)devices[i], (*def_index == *num) ? " (default)" : "");
511 (*num)++;
514 if(*def_index == (UINT)-1)
515 *def_index = 0;
517 HeapFree(GetProcessHeap(), 0, devices);
519 return S_OK;
522 static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow)
524 HKEY devices_key;
525 UINT i = 0;
526 WCHAR key_name[256];
527 DWORD key_name_size;
529 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
530 ERR("No devices in registry?\n");
531 return FALSE;
534 while(1){
535 HKEY key;
536 DWORD size, type;
537 GUID reg_guid;
539 key_name_size = sizeof(key_name);
540 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
541 NULL, NULL, NULL) != ERROR_SUCCESS)
542 break;
544 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
545 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
546 continue;
549 size = sizeof(reg_guid);
550 if(RegQueryValueExW(key, guidW, 0, &type,
551 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
552 if(IsEqualGUID(&reg_guid, guid)){
553 RegCloseKey(key);
554 RegCloseKey(devices_key);
556 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
558 if(key_name[0] == '0')
559 *flow = eRender;
560 else if(key_name[0] == '1')
561 *flow = eCapture;
562 else{
563 ERR("Unknown device type: %c\n", key_name[0]);
564 return FALSE;
567 *id = strtoulW(key_name + 2, NULL, 10);
569 return TRUE;
573 RegCloseKey(key);
576 RegCloseKey(devices_key);
578 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
580 return FALSE;
583 static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
585 AudioComponentInstance unit;
586 AudioComponent comp;
587 AudioComponentDescription desc;
588 OSStatus sc;
590 memset(&desc, 0, sizeof(desc));
591 desc.componentType = kAudioUnitType_Output;
592 desc.componentSubType = kAudioUnitSubType_HALOutput;
593 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
595 if(!(comp = AudioComponentFindNext(NULL, &desc))){
596 WARN("AudioComponentFindNext failed\n");
597 return NULL;
600 sc = AudioComponentInstanceNew(comp, &unit);
601 if(sc != noErr){
602 WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
603 return NULL;
606 if(dataflow == eCapture){
607 UInt32 enableio;
609 enableio = 1;
610 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
611 kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
612 if(sc != noErr){
613 WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
614 AudioComponentInstanceDispose(unit);
615 return NULL;
618 enableio = 0;
619 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
620 kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
621 if(sc != noErr){
622 WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
623 AudioComponentInstanceDispose(unit);
624 return NULL;
628 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
629 kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
630 if(sc != noErr){
631 WARN("Couldn't set audio unit device\n");
632 AudioComponentInstanceDispose(unit);
633 return NULL;
636 return unit;
639 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
641 ACImpl *This;
642 AudioDeviceID adevid;
643 EDataFlow dataflow;
644 HRESULT hr;
646 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
648 if(!get_deviceid_by_guid(guid, &adevid, &dataflow))
649 return AUDCLNT_E_DEVICE_INVALIDATED;
651 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
652 if(!This)
653 return E_OUTOFMEMORY;
655 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
656 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
657 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
658 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
659 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
660 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
662 This->dataflow = dataflow;
664 if(dataflow == eRender)
665 This->scope = kAudioDevicePropertyScopeOutput;
666 else if(dataflow == eCapture)
667 This->scope = kAudioDevicePropertyScopeInput;
668 else{
669 HeapFree(GetProcessHeap(), 0, This);
670 return E_INVALIDARG;
673 This->lock = 0;
675 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->pUnkFTMarshal);
676 if (FAILED(hr)) {
677 HeapFree(GetProcessHeap(), 0, This);
678 return hr;
681 This->parent = dev;
682 IMMDevice_AddRef(This->parent);
684 This->adevid = adevid;
686 if(!(This->unit = get_audiounit(This->dataflow, This->adevid))){
687 HeapFree(GetProcessHeap(), 0, This);
688 return AUDCLNT_E_DEVICE_INVALIDATED;
691 *out = (IAudioClient *)&This->IAudioClient3_iface;
692 IAudioClient3_AddRef(&This->IAudioClient3_iface);
694 return S_OK;
697 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
698 REFIID riid, void **ppv)
700 ACImpl *This = impl_from_IAudioClient3(iface);
701 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
703 if(!ppv)
704 return E_POINTER;
705 *ppv = NULL;
706 if(IsEqualIID(riid, &IID_IUnknown) ||
707 IsEqualIID(riid, &IID_IAudioClient) ||
708 IsEqualIID(riid, &IID_IAudioClient2) ||
709 IsEqualIID(riid, &IID_IAudioClient3))
710 *ppv = iface;
711 else if(IsEqualIID(riid, &IID_IMarshal))
712 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
714 if(*ppv){
715 IUnknown_AddRef((IUnknown*)*ppv);
716 return S_OK;
718 WARN("Unknown interface %s\n", debugstr_guid(riid));
719 return E_NOINTERFACE;
722 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
724 ACImpl *This = impl_from_IAudioClient3(iface);
725 ULONG ref;
726 ref = InterlockedIncrement(&This->ref);
727 TRACE("(%p) Refcount now %u\n", This, ref);
728 return ref;
731 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
733 ACImpl *This = impl_from_IAudioClient3(iface);
734 ULONG ref;
735 ref = InterlockedDecrement(&This->ref);
736 TRACE("(%p) Refcount now %u\n", This, ref);
737 if(!ref){
738 if(This->timer){
739 HANDLE event;
740 BOOL wait;
741 event = CreateEventW(NULL, TRUE, FALSE, NULL);
742 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
743 wait = wait && GetLastError() == ERROR_IO_PENDING;
744 if(event && wait)
745 WaitForSingleObject(event, INFINITE);
746 CloseHandle(event);
748 AudioOutputUnitStop(This->unit);
749 AudioComponentInstanceDispose(This->unit);
750 if(This->converter)
751 AudioConverterDispose(This->converter);
752 if(This->session){
753 EnterCriticalSection(&g_sessions_lock);
754 list_remove(&This->entry);
755 LeaveCriticalSection(&g_sessions_lock);
757 HeapFree(GetProcessHeap(), 0, This->vols);
758 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
759 HeapFree(GetProcessHeap(), 0, This->cap_buffer);
760 HeapFree(GetProcessHeap(), 0, This->local_buffer);
761 free(This->wrap_buffer);
762 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
763 CoTaskMemFree(This->fmt);
764 IMMDevice_Release(This->parent);
765 IUnknown_Release(This->pUnkFTMarshal);
766 HeapFree(GetProcessHeap(), 0, This);
768 return ref;
771 static void dump_fmt(const WAVEFORMATEX *fmt)
773 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
774 switch(fmt->wFormatTag){
775 case WAVE_FORMAT_PCM:
776 TRACE("WAVE_FORMAT_PCM");
777 break;
778 case WAVE_FORMAT_IEEE_FLOAT:
779 TRACE("WAVE_FORMAT_IEEE_FLOAT");
780 break;
781 case WAVE_FORMAT_EXTENSIBLE:
782 TRACE("WAVE_FORMAT_EXTENSIBLE");
783 break;
784 default:
785 TRACE("Unknown");
786 break;
788 TRACE(")\n");
790 TRACE("nChannels: %u\n", fmt->nChannels);
791 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
792 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
793 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
794 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
795 TRACE("cbSize: %u\n", fmt->cbSize);
797 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
798 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
799 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
800 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
801 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
805 static DWORD get_channel_mask(unsigned int channels)
807 switch(channels){
808 case 0:
809 return 0;
810 case 1:
811 return KSAUDIO_SPEAKER_MONO;
812 case 2:
813 return KSAUDIO_SPEAKER_STEREO;
814 case 3:
815 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
816 case 4:
817 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
818 case 5:
819 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
820 case 6:
821 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
822 case 7:
823 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
824 case 8:
825 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
827 FIXME("Unknown speaker configuration: %u\n", channels);
828 return 0;
831 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
833 WAVEFORMATEX *ret;
834 size_t size;
836 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
837 size = sizeof(WAVEFORMATEXTENSIBLE);
838 else
839 size = sizeof(WAVEFORMATEX);
841 ret = CoTaskMemAlloc(size);
842 if(!ret)
843 return NULL;
845 memcpy(ret, fmt, size);
847 ret->cbSize = size - sizeof(WAVEFORMATEX);
849 return ret;
852 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
853 const WAVEFORMATEX *fmt)
855 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
857 desc->mFormatFlags = 0;
859 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
860 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
861 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
862 desc->mFormatID = kAudioFormatLinearPCM;
863 if(fmt->wBitsPerSample > 8)
864 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
865 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
866 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
867 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
868 desc->mFormatID = kAudioFormatLinearPCM;
869 desc->mFormatFlags = kAudioFormatFlagIsFloat;
870 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
871 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
872 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
873 desc->mFormatID = kAudioFormatULaw;
874 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
875 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
876 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
877 desc->mFormatID = kAudioFormatALaw;
878 }else
879 return AUDCLNT_E_UNSUPPORTED_FORMAT;
881 desc->mSampleRate = fmt->nSamplesPerSec;
882 desc->mBytesPerPacket = fmt->nBlockAlign;
883 desc->mFramesPerPacket = 1;
884 desc->mBytesPerFrame = fmt->nBlockAlign;
885 desc->mChannelsPerFrame = fmt->nChannels;
886 desc->mBitsPerChannel = fmt->wBitsPerSample;
887 desc->mReserved = 0;
889 return S_OK;
892 static void session_init_vols(AudioSession *session, UINT channels)
894 if(session->channel_count < channels){
895 UINT i;
897 if(session->channel_vols)
898 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
899 session->channel_vols, sizeof(float) * channels);
900 else
901 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
902 sizeof(float) * channels);
903 if(!session->channel_vols)
904 return;
906 for(i = session->channel_count; i < channels; ++i)
907 session->channel_vols[i] = 1.f;
909 session->channel_count = channels;
913 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
914 UINT num_channels)
916 AudioSession *ret;
918 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
919 if(!ret)
920 return NULL;
922 memcpy(&ret->guid, guid, sizeof(GUID));
924 ret->device = device;
926 list_init(&ret->clients);
928 list_add_head(&g_sessions, &ret->entry);
930 InitializeCriticalSection(&ret->lock);
931 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
933 session_init_vols(ret, num_channels);
935 ret->master_vol = 1.f;
937 return ret;
940 /* if channels == 0, then this will return or create a session with
941 * matching dataflow and GUID. otherwise, channels must also match */
942 static HRESULT get_audio_session(const GUID *sessionguid,
943 IMMDevice *device, UINT channels, AudioSession **out)
945 AudioSession *session;
947 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
948 *out = create_session(&GUID_NULL, device, channels);
949 if(!*out)
950 return E_OUTOFMEMORY;
952 return S_OK;
955 *out = NULL;
956 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
957 if(session->device == device &&
958 IsEqualGUID(sessionguid, &session->guid)){
959 session_init_vols(session, channels);
960 *out = session;
961 break;
965 if(!*out){
966 *out = create_session(sessionguid, device, channels);
967 if(!*out)
968 return E_OUTOFMEMORY;
971 return S_OK;
974 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
975 BYTE *src, UINT32 src_bytes)
977 UINT32 chunk_bytes = dst_bytes - dst_offs;
979 if(chunk_bytes < src_bytes){
980 memcpy(dst + dst_offs, src, chunk_bytes);
981 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
982 }else
983 memcpy(dst + dst_offs, src, src_bytes);
986 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
988 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
989 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
990 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
991 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
992 This->fmt->wBitsPerSample == 8)
993 memset(buffer, 128, frames * This->fmt->nBlockAlign);
994 else
995 memset(buffer, 0, frames * This->fmt->nBlockAlign);
998 /* CA is pulling data from us */
999 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
1000 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1001 AudioBufferList *data)
1003 ACImpl *This = user;
1004 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
1006 OSSpinLockLock(&This->lock);
1008 if(This->playing){
1009 lcl_offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1010 to_copy_frames = min(nframes, This->held_frames);
1011 to_copy_bytes = to_copy_frames * This->fmt->nBlockAlign;
1013 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) * This->fmt->nBlockAlign;
1015 if(to_copy_bytes > chunk_bytes){
1016 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, chunk_bytes);
1017 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, This->local_buffer, to_copy_bytes - chunk_bytes);
1018 }else
1019 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, to_copy_bytes);
1021 This->lcl_offs_frames += to_copy_frames;
1022 This->lcl_offs_frames %= This->bufsize_frames;
1023 This->held_frames -= to_copy_frames;
1024 }else
1025 to_copy_bytes = to_copy_frames = 0;
1027 if(nframes > to_copy_frames)
1028 silence_buffer(This, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
1030 OSSpinLockUnlock(&This->lock);
1032 return noErr;
1035 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1037 if(left <= right)
1038 return right - left;
1039 return bufsize - (left - right);
1042 /* place data from cap_buffer into provided AudioBufferList */
1043 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1044 AudioStreamPacketDescription **packets, void *user)
1046 ACImpl *This = user;
1048 *nframes = min(*nframes, This->cap_held_frames);
1049 if(!*nframes){
1050 data->mBuffers[0].mData = NULL;
1051 data->mBuffers[0].mDataByteSize = 0;
1052 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1053 return noErr;
1056 data->mBuffers[0].mDataByteSize = *nframes * This->fmt->nBlockAlign;
1057 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1059 if(This->cap_offs_frames + *nframes > This->cap_bufsize_frames){
1060 UINT32 chunk_frames = This->cap_bufsize_frames - This->cap_offs_frames;
1062 if(This->wrap_bufsize_frames < *nframes){
1063 free(This->wrap_buffer);
1064 This->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1065 This->wrap_bufsize_frames = *nframes;
1068 memcpy(This->wrap_buffer, This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign,
1069 chunk_frames * This->fmt->nBlockAlign);
1070 memcpy(This->wrap_buffer + chunk_frames * This->fmt->nBlockAlign, This->cap_buffer,
1071 (*nframes - chunk_frames) * This->fmt->nBlockAlign);
1073 data->mBuffers[0].mData = This->wrap_buffer;
1074 }else
1075 data->mBuffers[0].mData = This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign;
1077 This->cap_offs_frames += *nframes;
1078 This->cap_offs_frames %= This->cap_bufsize_frames;
1079 This->cap_held_frames -= *nframes;
1081 if(packets)
1082 *packets = NULL;
1084 return noErr;
1087 static void capture_resample(ACImpl *This)
1089 UINT32 resamp_period_frames = MulDiv(This->period_frames, This->dev_desc.mSampleRate, This->fmt->nSamplesPerSec);
1090 OSStatus sc;
1092 /* the resampling process often needs more source frames than we'd
1093 * guess from a straight conversion using the sample rate ratio. so
1094 * only convert if we have extra source data. */
1095 while(This->cap_held_frames > resamp_period_frames * 2){
1096 AudioBufferList converted_list;
1097 UInt32 wanted_frames = This->period_frames;
1099 converted_list.mNumberBuffers = 1;
1100 converted_list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1101 converted_list.mBuffers[0].mDataByteSize = wanted_frames * This->fmt->nBlockAlign;
1103 if(This->resamp_bufsize_frames < wanted_frames){
1104 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
1105 This->resamp_buffer = HeapAlloc(GetProcessHeap(), 0, converted_list.mBuffers[0].mDataByteSize);
1106 This->resamp_bufsize_frames = wanted_frames;
1109 converted_list.mBuffers[0].mData = This->resamp_buffer;
1111 sc = AudioConverterFillComplexBuffer(This->converter, feed_cb,
1112 This, &wanted_frames, &converted_list, NULL);
1113 if(sc != noErr){
1114 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1115 break;
1118 ca_wrap_buffer(This->local_buffer,
1119 This->wri_offs_frames * This->fmt->nBlockAlign,
1120 This->bufsize_frames * This->fmt->nBlockAlign,
1121 This->resamp_buffer, wanted_frames * This->fmt->nBlockAlign);
1123 This->wri_offs_frames += wanted_frames;
1124 This->wri_offs_frames %= This->bufsize_frames;
1125 if(This->held_frames + wanted_frames > This->bufsize_frames){
1126 This->lcl_offs_frames += buf_ptr_diff(This->lcl_offs_frames,
1127 This->wri_offs_frames, This->bufsize_frames);
1128 This->held_frames = This->bufsize_frames;
1129 }else
1130 This->held_frames += wanted_frames;
1134 /* we need to trigger CA to pull data from the device and give it to us
1136 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
1138 * raw data is resampled from cap_buffer into resamp_buffer in period-size
1139 * chunks and copied to local_buffer
1141 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
1142 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1143 AudioBufferList *data)
1145 ACImpl *This = user;
1146 AudioBufferList list;
1147 OSStatus sc;
1148 UINT32 cap_wri_offs_frames;
1150 OSSpinLockLock(&This->lock);
1152 cap_wri_offs_frames = (This->cap_offs_frames + This->cap_held_frames) % This->cap_bufsize_frames;
1154 list.mNumberBuffers = 1;
1155 list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1156 list.mBuffers[0].mDataByteSize = nframes * This->fmt->nBlockAlign;
1158 if(!This->playing || cap_wri_offs_frames + nframes > This->cap_bufsize_frames){
1159 if(This->wrap_bufsize_frames < nframes){
1160 free(This->wrap_buffer);
1161 This->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
1162 This->wrap_bufsize_frames = nframes;
1165 list.mBuffers[0].mData = This->wrap_buffer;
1166 }else
1167 list.mBuffers[0].mData = This->cap_buffer + cap_wri_offs_frames * This->fmt->nBlockAlign;
1169 sc = AudioUnitRender(This->unit, flags, ts, bus, nframes, &list);
1170 if(sc != noErr){
1171 OSSpinLockUnlock(&This->lock);
1172 return sc;
1175 if(This->playing){
1176 if(list.mBuffers[0].mData == This->wrap_buffer){
1177 ca_wrap_buffer(This->cap_buffer,
1178 cap_wri_offs_frames * This->fmt->nBlockAlign,
1179 This->cap_bufsize_frames * This->fmt->nBlockAlign,
1180 This->wrap_buffer, list.mBuffers[0].mDataByteSize);
1183 This->cap_held_frames += list.mBuffers[0].mDataByteSize / This->fmt->nBlockAlign;
1184 if(This->cap_held_frames > This->cap_bufsize_frames){
1185 This->cap_offs_frames += This->cap_held_frames % This->cap_bufsize_frames;
1186 This->cap_offs_frames %= This->cap_bufsize_frames;
1187 This->cap_held_frames = This->cap_bufsize_frames;
1191 OSSpinLockUnlock(&This->lock);
1192 return noErr;
1195 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
1197 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
1198 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
1199 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
1200 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
1201 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
1202 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
1205 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
1206 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
1207 AudioConverterRef *converter)
1209 OSStatus sc;
1210 HRESULT hr;
1212 if(dataflow == eCapture){
1213 AudioStreamBasicDescription desc;
1214 UInt32 size;
1215 Float64 rate;
1216 fenv_t fenv;
1217 BOOL fenv_stored = TRUE;
1219 hr = ca_get_audiodesc(&desc, fmt);
1220 if(FAILED(hr))
1221 return hr;
1222 dump_adesc("requested", &desc);
1224 /* input-only units can't perform sample rate conversion, so we have to
1225 * set up our own AudioConverter to support arbitrary sample rates. */
1226 size = sizeof(*dev_desc);
1227 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
1228 kAudioUnitScope_Input, 1, dev_desc, &size);
1229 if(sc != noErr){
1230 WARN("Couldn't get unit format: %x\n", (int)sc);
1231 return osstatus_to_hresult(sc);
1233 dump_adesc("hardware", dev_desc);
1235 rate = dev_desc->mSampleRate;
1236 *dev_desc = desc;
1237 dev_desc->mSampleRate = rate;
1239 dump_adesc("final", dev_desc);
1240 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1241 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
1242 if(sc != noErr){
1243 WARN("Couldn't set unit format: %x\n", (int)sc);
1244 return osstatus_to_hresult(sc);
1247 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
1248 if(feholdexcept(&fenv)){
1249 WARN("Failed to store fenv state\n");
1250 fenv_stored = FALSE;
1253 sc = AudioConverterNew(dev_desc, &desc, converter);
1255 if(fenv_stored && fesetenv(&fenv))
1256 WARN("Failed to restore fenv state\n");
1258 if(sc != noErr){
1259 WARN("Couldn't create audio converter: %x\n", (int)sc);
1260 return osstatus_to_hresult(sc);
1262 }else{
1263 hr = ca_get_audiodesc(dev_desc, fmt);
1264 if(FAILED(hr))
1265 return hr;
1267 dump_adesc("final", dev_desc);
1268 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1269 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
1270 if(sc != noErr){
1271 WARN("Couldn't set format: %x\n", (int)sc);
1272 return osstatus_to_hresult(sc);
1276 return S_OK;
1279 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
1280 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1281 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1282 const GUID *sessionguid)
1284 ACImpl *This = impl_from_IAudioClient3(iface);
1285 HRESULT hr;
1286 OSStatus sc;
1287 int i;
1289 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1290 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1292 if(!fmt)
1293 return E_POINTER;
1295 dump_fmt(fmt);
1297 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1298 return E_INVALIDARG;
1300 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1301 AUDCLNT_STREAMFLAGS_LOOPBACK |
1302 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1303 AUDCLNT_STREAMFLAGS_NOPERSIST |
1304 AUDCLNT_STREAMFLAGS_RATEADJUST |
1305 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1306 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1307 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
1308 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
1309 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
1310 FIXME("Unknown flags: %08x\n", flags);
1311 return E_INVALIDARG;
1314 if(mode == AUDCLNT_SHAREMODE_SHARED){
1315 period = DefaultPeriod;
1316 if( duration < 3 * period)
1317 duration = 3 * period;
1318 }else{
1319 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1320 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1321 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1322 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1325 if(!period)
1326 period = DefaultPeriod; /* not minimum */
1327 if(period < MinimumPeriod || period > 5000000)
1328 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1329 if(duration > 20000000) /* the smaller the period, the lower this limit */
1330 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1331 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1332 if(duration != period)
1333 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1334 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1335 return AUDCLNT_E_DEVICE_IN_USE;
1336 }else{
1337 if( duration < 8 * period)
1338 duration = 8 * period; /* may grow above 2s */
1342 OSSpinLockLock(&This->lock);
1344 if(This->initted){
1345 OSSpinLockUnlock(&This->lock);
1346 return AUDCLNT_E_ALREADY_INITIALIZED;
1349 This->fmt = clone_format(fmt);
1350 if(!This->fmt){
1351 OSSpinLockUnlock(&This->lock);
1352 return E_OUTOFMEMORY;
1355 This->period_ms = period / 10000;
1356 This->period_frames = MulDiv(period, This->fmt->nSamplesPerSec, 10000000);
1358 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1359 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1360 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1362 hr = ca_setup_audiounit(This->dataflow, This->unit, This->fmt, &This->dev_desc, &This->converter);
1363 if(FAILED(hr)){
1364 CoTaskMemFree(This->fmt);
1365 This->fmt = NULL;
1366 OSSpinLockUnlock(&This->lock);
1367 return hr;
1370 if(This->dataflow == eCapture){
1371 AURenderCallbackStruct input;
1373 memset(&input, 0, sizeof(input));
1374 input.inputProc = &ca_capture_cb;
1375 input.inputProcRefCon = This;
1377 sc = AudioUnitSetProperty(This->unit, kAudioOutputUnitProperty_SetInputCallback,
1378 kAudioUnitScope_Output, 1, &input, sizeof(input));
1379 if(sc != noErr){
1380 WARN("Couldn't set callback: %x\n", (int)sc);
1381 AudioConverterDispose(This->converter);
1382 This->converter = NULL;
1383 CoTaskMemFree(This->fmt);
1384 This->fmt = NULL;
1385 OSSpinLockUnlock(&This->lock);
1386 return osstatus_to_hresult(sc);
1388 }else{
1389 AURenderCallbackStruct input;
1391 memset(&input, 0, sizeof(input));
1392 input.inputProc = &ca_render_cb;
1393 input.inputProcRefCon = This;
1395 sc = AudioUnitSetProperty(This->unit, kAudioUnitProperty_SetRenderCallback,
1396 kAudioUnitScope_Input, 0, &input, sizeof(input));
1397 if(sc != noErr){
1398 WARN("Couldn't set callback: %x\n", (int)sc);
1399 CoTaskMemFree(This->fmt);
1400 This->fmt = NULL;
1401 OSSpinLockUnlock(&This->lock);
1402 return osstatus_to_hresult(sc);
1406 sc = AudioUnitInitialize(This->unit);
1407 if(sc != noErr){
1408 WARN("Couldn't initialize: %x\n", (int)sc);
1409 if(This->converter){
1410 AudioConverterDispose(This->converter);
1411 This->converter = NULL;
1413 CoTaskMemFree(This->fmt);
1414 This->fmt = NULL;
1415 OSSpinLockUnlock(&This->lock);
1416 return osstatus_to_hresult(sc);
1419 /* we play audio continuously because AudioOutputUnitStart sometimes takes
1420 * a while to return */
1421 sc = AudioOutputUnitStart(This->unit);
1422 if(sc != noErr){
1423 WARN("Unit failed to start: %x\n", (int)sc);
1424 if(This->converter){
1425 AudioConverterDispose(This->converter);
1426 This->converter = NULL;
1428 CoTaskMemFree(This->fmt);
1429 This->fmt = NULL;
1430 OSSpinLockUnlock(&This->lock);
1431 return osstatus_to_hresult(sc);
1434 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_frames * fmt->nBlockAlign);
1435 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1437 if(This->dataflow == eCapture){
1438 This->cap_bufsize_frames = MulDiv(duration, This->dev_desc.mSampleRate, 10000000);
1439 This->cap_buffer = HeapAlloc(GetProcessHeap(), 0, This->cap_bufsize_frames * This->fmt->nBlockAlign);
1442 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1443 if(!This->vols){
1444 CoTaskMemFree(This->fmt);
1445 This->fmt = NULL;
1446 OSSpinLockUnlock(&This->lock);
1447 return E_OUTOFMEMORY;
1450 for(i = 0; i < fmt->nChannels; ++i)
1451 This->vols[i] = 1.f;
1453 This->share = mode;
1454 This->flags = flags;
1456 EnterCriticalSection(&g_sessions_lock);
1458 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1459 &This->session);
1460 if(FAILED(hr)){
1461 LeaveCriticalSection(&g_sessions_lock);
1462 CoTaskMemFree(This->fmt);
1463 This->fmt = NULL;
1464 HeapFree(GetProcessHeap(), 0, This->vols);
1465 This->vols = NULL;
1466 OSSpinLockUnlock(&This->lock);
1467 return E_INVALIDARG;
1470 list_add_tail(&This->session->clients, &This->entry);
1472 LeaveCriticalSection(&g_sessions_lock);
1474 ca_setvol(This, -1);
1476 This->initted = TRUE;
1478 OSSpinLockUnlock(&This->lock);
1480 return S_OK;
1483 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
1484 UINT32 *frames)
1486 ACImpl *This = impl_from_IAudioClient3(iface);
1488 TRACE("(%p)->(%p)\n", This, frames);
1490 if(!frames)
1491 return E_POINTER;
1493 OSSpinLockLock(&This->lock);
1495 if(!This->initted){
1496 OSSpinLockUnlock(&This->lock);
1497 return AUDCLNT_E_NOT_INITIALIZED;
1500 *frames = This->bufsize_frames;
1502 OSSpinLockUnlock(&This->lock);
1504 return S_OK;
1507 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1509 AudioObjectPropertyAddress addr;
1510 AudioStreamID *ids;
1511 UInt32 size;
1512 OSStatus sc;
1513 int nstreams, i;
1515 addr.mScope = This->scope;
1516 addr.mElement = 0;
1517 addr.mSelector = kAudioDevicePropertyStreams;
1519 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1520 &size);
1521 if(sc != noErr){
1522 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1523 return osstatus_to_hresult(sc);
1526 ids = HeapAlloc(GetProcessHeap(), 0, size);
1527 if(!ids)
1528 return E_OUTOFMEMORY;
1530 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1531 if(sc != noErr){
1532 WARN("Unable to get _Streams property: %x\n", (int)sc);
1533 HeapFree(GetProcessHeap(), 0, ids);
1534 return osstatus_to_hresult(sc);
1537 nstreams = size / sizeof(AudioStreamID);
1538 *max = 0;
1540 addr.mSelector = kAudioStreamPropertyLatency;
1541 for(i = 0; i < nstreams; ++i){
1542 UInt32 latency;
1544 size = sizeof(latency);
1545 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1546 &size, &latency);
1547 if(sc != noErr){
1548 WARN("Unable to get _Latency property: %x\n", (int)sc);
1549 continue;
1552 if(latency > *max)
1553 *max = latency;
1556 HeapFree(GetProcessHeap(), 0, ids);
1558 return S_OK;
1561 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
1562 REFERENCE_TIME *out)
1564 ACImpl *This = impl_from_IAudioClient3(iface);
1565 UInt32 latency, stream_latency, size;
1566 AudioObjectPropertyAddress addr;
1567 OSStatus sc;
1568 HRESULT hr;
1570 TRACE("(%p)->(%p)\n", This, out);
1572 if(!out)
1573 return E_POINTER;
1575 OSSpinLockLock(&This->lock);
1577 if(!This->initted){
1578 OSSpinLockUnlock(&This->lock);
1579 return AUDCLNT_E_NOT_INITIALIZED;
1582 addr.mScope = This->scope;
1583 addr.mSelector = kAudioDevicePropertyLatency;
1584 addr.mElement = 0;
1586 size = sizeof(latency);
1587 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1588 &size, &latency);
1589 if(sc != noErr){
1590 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1591 OSSpinLockUnlock(&This->lock);
1592 return osstatus_to_hresult(sc);
1595 hr = ca_get_max_stream_latency(This, &stream_latency);
1596 if(FAILED(hr)){
1597 OSSpinLockUnlock(&This->lock);
1598 return hr;
1601 latency += stream_latency;
1602 /* pretend we process audio in Period chunks, so max latency includes
1603 * the period time */
1604 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1605 + This->period_ms * 10000;
1607 OSSpinLockUnlock(&This->lock);
1609 return S_OK;
1612 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1613 UINT32 *numpad)
1615 if(!This->initted)
1616 return AUDCLNT_E_NOT_INITIALIZED;
1618 if(This->dataflow == eCapture)
1619 capture_resample(This);
1621 *numpad = This->held_frames;
1623 return S_OK;
1626 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
1627 UINT32 *numpad)
1629 ACImpl *This = impl_from_IAudioClient3(iface);
1630 HRESULT hr;
1632 TRACE("(%p)->(%p)\n", This, numpad);
1634 if(!numpad)
1635 return E_POINTER;
1637 OSSpinLockLock(&This->lock);
1639 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1641 OSSpinLockUnlock(&This->lock);
1643 return hr;
1646 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
1647 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1648 WAVEFORMATEX **outpwfx)
1650 ACImpl *This = impl_from_IAudioClient3(iface);
1651 AudioStreamBasicDescription dev_desc;
1652 AudioConverterRef converter;
1653 AudioComponentInstance unit;
1654 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1655 HRESULT hr;
1657 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1659 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1660 return E_POINTER;
1662 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1663 return E_INVALIDARG;
1665 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1666 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1667 return E_INVALIDARG;
1669 dump_fmt(pwfx);
1671 if(outpwfx){
1672 *outpwfx = NULL;
1673 if(mode != AUDCLNT_SHAREMODE_SHARED)
1674 outpwfx = NULL;
1677 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1678 if(pwfx->nAvgBytesPerSec == 0 ||
1679 pwfx->nBlockAlign == 0 ||
1680 fmtex->Samples.wValidBitsPerSample > pwfx->wBitsPerSample)
1681 return E_INVALIDARG;
1682 if(fmtex->Samples.wValidBitsPerSample < pwfx->wBitsPerSample)
1683 goto unsupported;
1684 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1685 if(fmtex->dwChannelMask == 0 ||
1686 fmtex->dwChannelMask & SPEAKER_RESERVED)
1687 goto unsupported;
1691 if(pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample / 8 ||
1692 pwfx->nAvgBytesPerSec != pwfx->nBlockAlign * pwfx->nSamplesPerSec)
1693 goto unsupported;
1695 if(pwfx->nChannels == 0)
1696 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1698 unit = get_audiounit(This->dataflow, This->adevid);
1700 converter = NULL;
1701 hr = ca_setup_audiounit(This->dataflow, unit, pwfx, &dev_desc, &converter);
1702 AudioComponentInstanceDispose(unit);
1703 if(FAILED(hr))
1704 goto unsupported;
1706 if(converter)
1707 AudioConverterDispose(converter);
1709 return S_OK;
1711 unsupported:
1712 if(outpwfx){
1713 hr = IAudioClient3_GetMixFormat(&This->IAudioClient3_iface, outpwfx);
1714 if(FAILED(hr))
1715 return hr;
1716 return S_FALSE;
1719 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1722 static DWORD ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
1724 int i;
1725 DWORD mask = 0;
1727 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
1728 switch (layout->mChannelDescriptions[i].mChannelLabel) {
1729 default: FIXME("Unhandled channel 0x%x\n", (unsigned int)layout->mChannelDescriptions[i].mChannelLabel); break;
1730 case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
1731 case kAudioChannelLabel_Mono:
1732 case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
1733 case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
1734 case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
1735 case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
1736 case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
1737 case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
1738 case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
1739 case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
1740 case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
1741 case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
1742 case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
1743 case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
1744 case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
1745 case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
1746 case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
1747 case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
1748 case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
1752 return mask;
1755 /* For most hardware on Windows, users must choose a configuration with an even
1756 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1757 * channels, but those channels are still reported to applications from
1758 * GetMixFormat! Some applications behave badly if given an odd number of
1759 * channels (e.g. 2.1). Here, we find the nearest configuration that Windows
1760 * would report for a given channel layout. */
1761 static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
1763 DWORD ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
1765 TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
1767 if (ca_layout->mNumberChannelDescriptions == 1)
1769 fmt->Format.nChannels = 1;
1770 fmt->dwChannelMask = ca_mask;
1771 return;
1774 /* compare against known configurations and find smallest configuration
1775 * which is a superset of the given speakers */
1777 if (ca_layout->mNumberChannelDescriptions <= 2 &&
1778 (ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
1780 fmt->Format.nChannels = 2;
1781 fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1782 return;
1785 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1786 (ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
1788 fmt->Format.nChannels = 4;
1789 fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
1790 return;
1793 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1794 (ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
1796 fmt->Format.nChannels = 4;
1797 fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
1798 return;
1801 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1802 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
1804 fmt->Format.nChannels = 6;
1805 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
1806 return;
1809 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1810 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
1812 fmt->Format.nChannels = 6;
1813 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
1814 return;
1817 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1818 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
1820 fmt->Format.nChannels = 8;
1821 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
1822 return;
1825 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1826 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
1828 fmt->Format.nChannels = 8;
1829 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
1830 return;
1833 /* oddball format, report truthfully */
1834 fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
1835 fmt->dwChannelMask = ca_mask;
1838 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1839 WAVEFORMATEX **pwfx)
1841 ACImpl *This = impl_from_IAudioClient3(iface);
1842 WAVEFORMATEXTENSIBLE *fmt;
1843 OSStatus sc;
1844 UInt32 size;
1845 Float64 rate;
1846 AudioBufferList *buffers;
1847 AudioChannelLayout *layout;
1848 AudioObjectPropertyAddress addr;
1849 int i;
1851 TRACE("(%p)->(%p)\n", This, pwfx);
1853 if(!pwfx)
1854 return E_POINTER;
1855 *pwfx = NULL;
1857 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1858 if(!fmt)
1859 return E_OUTOFMEMORY;
1861 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1863 addr.mScope = This->scope;
1864 addr.mElement = 0;
1865 addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
1867 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1868 if(sc == noErr){
1869 layout = HeapAlloc(GetProcessHeap(), 0, size);
1871 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, layout);
1872 if(sc == noErr){
1873 TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
1874 (unsigned int)layout->mChannelLayoutTag, (unsigned int)layout->mChannelBitmap,
1875 (unsigned int)layout->mNumberChannelDescriptions);
1877 if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
1878 convert_channel_layout(layout, fmt);
1879 }else{
1880 WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n", (unsigned int)layout->mChannelLayoutTag);
1881 fmt->Format.nChannels = 0;
1883 }else{
1884 TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1885 fmt->Format.nChannels = 0;
1888 HeapFree(GetProcessHeap(), 0, layout);
1889 }else{
1890 TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1891 fmt->Format.nChannels = 0;
1894 if(fmt->Format.nChannels == 0){
1895 addr.mScope = This->scope;
1896 addr.mElement = 0;
1897 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1899 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1900 if(sc != noErr){
1901 CoTaskMemFree(fmt);
1902 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
1903 return osstatus_to_hresult(sc);
1906 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1907 if(!buffers){
1908 CoTaskMemFree(fmt);
1909 return E_OUTOFMEMORY;
1912 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1913 &size, buffers);
1914 if(sc != noErr){
1915 CoTaskMemFree(fmt);
1916 HeapFree(GetProcessHeap(), 0, buffers);
1917 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
1918 return osstatus_to_hresult(sc);
1921 fmt->Format.nChannels = 0;
1922 for(i = 0; i < buffers->mNumberBuffers; ++i)
1923 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1925 HeapFree(GetProcessHeap(), 0, buffers);
1927 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1930 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1931 size = sizeof(Float64);
1932 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1933 if(sc != noErr){
1934 CoTaskMemFree(fmt);
1935 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
1936 return osstatus_to_hresult(sc);
1938 fmt->Format.nSamplesPerSec = rate;
1940 fmt->Format.wBitsPerSample = 32;
1941 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1943 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1944 fmt->Format.nChannels) / 8;
1945 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1946 fmt->Format.nBlockAlign;
1948 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1949 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1951 *pwfx = (WAVEFORMATEX*)fmt;
1952 dump_fmt(*pwfx);
1954 return S_OK;
1957 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1958 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1960 ACImpl *This = impl_from_IAudioClient3(iface);
1962 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1964 if(!defperiod && !minperiod)
1965 return E_POINTER;
1967 if(defperiod)
1968 *defperiod = DefaultPeriod;
1969 if(minperiod)
1970 *minperiod = MinimumPeriod;
1972 return S_OK;
1975 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1977 ACImpl *This = user;
1979 if(This->event)
1980 SetEvent(This->event);
1983 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
1985 ACImpl *This = impl_from_IAudioClient3(iface);
1987 TRACE("(%p)\n", This);
1989 OSSpinLockLock(&This->lock);
1991 if(!This->initted){
1992 OSSpinLockUnlock(&This->lock);
1993 return AUDCLNT_E_NOT_INITIALIZED;
1996 if(This->playing){
1997 OSSpinLockUnlock(&This->lock);
1998 return AUDCLNT_E_NOT_STOPPED;
2001 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2002 OSSpinLockUnlock(&This->lock);
2003 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2006 if(This->event && !This->timer)
2007 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
2008 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
2009 This->timer = NULL;
2010 OSSpinLockUnlock(&This->lock);
2011 WARN("Unable to create timer: %u\n", GetLastError());
2012 return E_OUTOFMEMORY;
2015 This->playing = TRUE;
2017 OSSpinLockUnlock(&This->lock);
2019 return S_OK;
2022 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
2024 ACImpl *This = impl_from_IAudioClient3(iface);
2026 TRACE("(%p)\n", This);
2028 OSSpinLockLock(&This->lock);
2030 if(!This->initted){
2031 OSSpinLockUnlock(&This->lock);
2032 return AUDCLNT_E_NOT_INITIALIZED;
2035 if(!This->playing){
2036 OSSpinLockUnlock(&This->lock);
2037 return S_FALSE;
2040 This->playing = FALSE;
2042 OSSpinLockUnlock(&This->lock);
2044 return S_OK;
2047 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
2049 ACImpl *This = impl_from_IAudioClient3(iface);
2051 TRACE("(%p)\n", This);
2053 OSSpinLockLock(&This->lock);
2055 if(!This->initted){
2056 OSSpinLockUnlock(&This->lock);
2057 return AUDCLNT_E_NOT_INITIALIZED;
2060 if(This->playing){
2061 OSSpinLockUnlock(&This->lock);
2062 return AUDCLNT_E_NOT_STOPPED;
2065 if(This->getbuf_last){
2066 OSSpinLockUnlock(&This->lock);
2067 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2070 if(This->dataflow == eRender){
2071 This->written_frames = 0;
2072 }else{
2073 This->written_frames += This->held_frames;
2076 This->held_frames = 0;
2077 This->lcl_offs_frames = 0;
2078 This->wri_offs_frames = 0;
2079 This->cap_offs_frames = 0;
2080 This->cap_held_frames = 0;
2082 OSSpinLockUnlock(&This->lock);
2084 return S_OK;
2087 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
2088 HANDLE event)
2090 ACImpl *This = impl_from_IAudioClient3(iface);
2092 TRACE("(%p)->(%p)\n", This, event);
2094 if(!event)
2095 return E_INVALIDARG;
2097 OSSpinLockLock(&This->lock);
2099 if(!This->initted){
2100 OSSpinLockUnlock(&This->lock);
2101 return AUDCLNT_E_NOT_INITIALIZED;
2104 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2105 OSSpinLockUnlock(&This->lock);
2106 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2109 if (This->event){
2110 OSSpinLockUnlock(&This->lock);
2111 FIXME("called twice\n");
2112 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2115 This->event = event;
2117 OSSpinLockUnlock(&This->lock);
2119 return S_OK;
2122 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
2123 void **ppv)
2125 ACImpl *This = impl_from_IAudioClient3(iface);
2127 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2129 if(!ppv)
2130 return E_POINTER;
2131 *ppv = NULL;
2133 OSSpinLockLock(&This->lock);
2135 if(!This->initted){
2136 OSSpinLockUnlock(&This->lock);
2137 return AUDCLNT_E_NOT_INITIALIZED;
2140 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2141 if(This->dataflow != eRender){
2142 OSSpinLockUnlock(&This->lock);
2143 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2145 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2146 *ppv = &This->IAudioRenderClient_iface;
2147 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2148 if(This->dataflow != eCapture){
2149 OSSpinLockUnlock(&This->lock);
2150 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2152 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2153 *ppv = &This->IAudioCaptureClient_iface;
2154 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2155 IAudioClock_AddRef(&This->IAudioClock_iface);
2156 *ppv = &This->IAudioClock_iface;
2157 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2158 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2159 *ppv = &This->IAudioStreamVolume_iface;
2160 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2161 if(!This->session_wrapper){
2162 This->session_wrapper = AudioSessionWrapper_Create(This);
2163 if(!This->session_wrapper){
2164 OSSpinLockUnlock(&This->lock);
2165 return E_OUTOFMEMORY;
2167 }else
2168 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2170 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2171 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2172 if(!This->session_wrapper){
2173 This->session_wrapper = AudioSessionWrapper_Create(This);
2174 if(!This->session_wrapper){
2175 OSSpinLockUnlock(&This->lock);
2176 return E_OUTOFMEMORY;
2178 }else
2179 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2181 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2182 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2183 if(!This->session_wrapper){
2184 This->session_wrapper = AudioSessionWrapper_Create(This);
2185 if(!This->session_wrapper){
2186 OSSpinLockUnlock(&This->lock);
2187 return E_OUTOFMEMORY;
2189 }else
2190 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2192 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2195 if(*ppv){
2196 OSSpinLockUnlock(&This->lock);
2197 return S_OK;
2200 OSSpinLockUnlock(&This->lock);
2202 FIXME("stub %s\n", debugstr_guid(riid));
2203 return E_NOINTERFACE;
2206 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
2207 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
2209 ACImpl *This = impl_from_IAudioClient3(iface);
2211 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
2213 if(!offload_capable)
2214 return E_INVALIDARG;
2216 *offload_capable = FALSE;
2218 return S_OK;
2221 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
2222 const AudioClientProperties *prop)
2224 ACImpl *This = impl_from_IAudioClient3(iface);
2225 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
2227 TRACE("(%p)->(%p)\n", This, prop);
2229 if(!legacy_prop)
2230 return E_POINTER;
2232 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
2233 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2234 legacy_prop->bIsOffload,
2235 legacy_prop->eCategory,
2236 prop->Options);
2237 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
2238 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
2239 legacy_prop->bIsOffload,
2240 legacy_prop->eCategory);
2241 }else{
2242 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
2243 return E_INVALIDARG;
2247 if(legacy_prop->bIsOffload)
2248 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
2250 return S_OK;
2253 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
2254 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
2255 REFERENCE_TIME *max_duration)
2257 ACImpl *This = impl_from_IAudioClient3(iface);
2259 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
2261 return E_NOTIMPL;
2264 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
2265 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
2266 UINT32 *min_period_frames, UINT32 *max_period_frames)
2268 ACImpl *This = impl_from_IAudioClient3(iface);
2270 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
2271 min_period_frames, max_period_frames);
2273 return E_NOTIMPL;
2276 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
2277 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
2279 ACImpl *This = impl_from_IAudioClient3(iface);
2281 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
2283 return E_NOTIMPL;
2286 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
2287 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
2288 const GUID *session_guid)
2290 ACImpl *This = impl_from_IAudioClient3(iface);
2292 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
2294 return E_NOTIMPL;
2297 static const IAudioClient3Vtbl AudioClient3_Vtbl =
2299 AudioClient_QueryInterface,
2300 AudioClient_AddRef,
2301 AudioClient_Release,
2302 AudioClient_Initialize,
2303 AudioClient_GetBufferSize,
2304 AudioClient_GetStreamLatency,
2305 AudioClient_GetCurrentPadding,
2306 AudioClient_IsFormatSupported,
2307 AudioClient_GetMixFormat,
2308 AudioClient_GetDevicePeriod,
2309 AudioClient_Start,
2310 AudioClient_Stop,
2311 AudioClient_Reset,
2312 AudioClient_SetEventHandle,
2313 AudioClient_GetService,
2314 AudioClient_IsOffloadCapable,
2315 AudioClient_SetClientProperties,
2316 AudioClient_GetBufferSizeLimits,
2317 AudioClient_GetSharedModeEnginePeriod,
2318 AudioClient_GetCurrentSharedModeEnginePeriod,
2319 AudioClient_InitializeSharedAudioStream,
2322 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2323 IAudioRenderClient *iface, REFIID riid, void **ppv)
2325 ACImpl *This = impl_from_IAudioRenderClient(iface);
2326 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2328 if(!ppv)
2329 return E_POINTER;
2330 *ppv = NULL;
2332 if(IsEqualIID(riid, &IID_IUnknown) ||
2333 IsEqualIID(riid, &IID_IAudioRenderClient))
2334 *ppv = iface;
2335 else if(IsEqualIID(riid, &IID_IMarshal))
2336 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2338 if(*ppv){
2339 IUnknown_AddRef((IUnknown*)*ppv);
2340 return S_OK;
2343 WARN("Unknown interface %s\n", debugstr_guid(riid));
2344 return E_NOINTERFACE;
2347 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2349 ACImpl *This = impl_from_IAudioRenderClient(iface);
2350 return AudioClient_AddRef(&This->IAudioClient3_iface);
2353 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2355 ACImpl *This = impl_from_IAudioRenderClient(iface);
2356 return AudioClient_Release(&This->IAudioClient3_iface);
2359 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2360 UINT32 frames, BYTE **data)
2362 ACImpl *This = impl_from_IAudioRenderClient(iface);
2363 UINT32 pad;
2364 HRESULT hr;
2366 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2368 if(!data)
2369 return E_POINTER;
2370 *data = NULL;
2372 OSSpinLockLock(&This->lock);
2374 if(This->getbuf_last){
2375 OSSpinLockUnlock(&This->lock);
2376 return AUDCLNT_E_OUT_OF_ORDER;
2379 if(!frames){
2380 OSSpinLockUnlock(&This->lock);
2381 return S_OK;
2384 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
2385 if(FAILED(hr)){
2386 OSSpinLockUnlock(&This->lock);
2387 return hr;
2390 if(pad + frames > This->bufsize_frames){
2391 OSSpinLockUnlock(&This->lock);
2392 return AUDCLNT_E_BUFFER_TOO_LARGE;
2395 if(This->wri_offs_frames + frames > This->bufsize_frames){
2396 if(This->tmp_buffer_frames < frames){
2397 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2398 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, frames * This->fmt->nBlockAlign);
2399 if(!This->tmp_buffer){
2400 OSSpinLockUnlock(&This->lock);
2401 return E_OUTOFMEMORY;
2403 This->tmp_buffer_frames = frames;
2405 *data = This->tmp_buffer;
2406 This->getbuf_last = -frames;
2407 }else{
2408 *data = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2409 This->getbuf_last = frames;
2412 silence_buffer(This, *data, frames);
2414 OSSpinLockUnlock(&This->lock);
2416 return S_OK;
2419 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2420 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
2422 ACImpl *This = impl_from_IAudioRenderClient(iface);
2423 BYTE *buffer;
2425 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
2427 OSSpinLockLock(&This->lock);
2429 if(!frames){
2430 This->getbuf_last = 0;
2431 OSSpinLockUnlock(&This->lock);
2432 return S_OK;
2435 if(!This->getbuf_last){
2436 OSSpinLockUnlock(&This->lock);
2437 return AUDCLNT_E_OUT_OF_ORDER;
2440 if(frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2441 OSSpinLockUnlock(&This->lock);
2442 return AUDCLNT_E_INVALID_SIZE;
2445 if(This->getbuf_last >= 0)
2446 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2447 else
2448 buffer = This->tmp_buffer;
2450 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2451 silence_buffer(This, buffer, frames);
2453 if(This->getbuf_last < 0)
2454 ca_wrap_buffer(This->local_buffer,
2455 This->wri_offs_frames * This->fmt->nBlockAlign,
2456 This->bufsize_frames * This->fmt->nBlockAlign,
2457 buffer, frames * This->fmt->nBlockAlign);
2460 This->wri_offs_frames += frames;
2461 This->wri_offs_frames %= This->bufsize_frames;
2462 This->held_frames += frames;
2463 This->written_frames += frames;
2464 This->getbuf_last = 0;
2466 OSSpinLockUnlock(&This->lock);
2468 return S_OK;
2471 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2472 AudioRenderClient_QueryInterface,
2473 AudioRenderClient_AddRef,
2474 AudioRenderClient_Release,
2475 AudioRenderClient_GetBuffer,
2476 AudioRenderClient_ReleaseBuffer
2479 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2480 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2482 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2483 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2485 if(!ppv)
2486 return E_POINTER;
2487 *ppv = NULL;
2489 if(IsEqualIID(riid, &IID_IUnknown) ||
2490 IsEqualIID(riid, &IID_IAudioCaptureClient))
2491 *ppv = iface;
2492 else if(IsEqualIID(riid, &IID_IMarshal))
2493 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2495 if(*ppv){
2496 IUnknown_AddRef((IUnknown*)*ppv);
2497 return S_OK;
2500 WARN("Unknown interface %s\n", debugstr_guid(riid));
2501 return E_NOINTERFACE;
2504 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2506 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2507 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2510 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2512 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2513 return IAudioClient3_Release(&This->IAudioClient3_iface);
2516 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2517 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2518 UINT64 *qpcpos)
2520 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2521 UINT32 chunk_bytes, chunk_frames;
2523 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2524 devpos, qpcpos);
2526 if(!data)
2527 return E_POINTER;
2529 *data = NULL;
2531 if(!frames || !flags)
2532 return E_POINTER;
2534 OSSpinLockLock(&This->lock);
2536 if(This->getbuf_last){
2537 OSSpinLockUnlock(&This->lock);
2538 return AUDCLNT_E_OUT_OF_ORDER;
2541 capture_resample(This);
2543 if(This->held_frames < This->period_frames){
2544 *frames = 0;
2545 OSSpinLockUnlock(&This->lock);
2546 return AUDCLNT_S_BUFFER_EMPTY;
2549 *flags = 0;
2551 chunk_frames = This->bufsize_frames - This->lcl_offs_frames;
2552 if(chunk_frames < This->period_frames){
2553 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2554 if(!This->tmp_buffer)
2555 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->period_frames * This->fmt->nBlockAlign);
2556 *data = This->tmp_buffer;
2557 memcpy(*data, This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign, chunk_bytes);
2558 memcpy((*data) + chunk_bytes, This->local_buffer, This->period_frames * This->fmt->nBlockAlign - chunk_bytes);
2559 }else
2560 *data = This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
2562 This->getbuf_last = *frames = This->period_frames;
2564 if(devpos)
2565 *devpos = This->written_frames;
2566 if(qpcpos){ /* fixme: qpc of recording time */
2567 LARGE_INTEGER stamp, freq;
2568 QueryPerformanceCounter(&stamp);
2569 QueryPerformanceFrequency(&freq);
2570 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2573 OSSpinLockUnlock(&This->lock);
2575 return S_OK;
2578 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2579 IAudioCaptureClient *iface, UINT32 done)
2581 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2583 TRACE("(%p)->(%u)\n", This, done);
2585 OSSpinLockLock(&This->lock);
2587 if(!done){
2588 This->getbuf_last = 0;
2589 OSSpinLockUnlock(&This->lock);
2590 return S_OK;
2593 if(!This->getbuf_last){
2594 OSSpinLockUnlock(&This->lock);
2595 return AUDCLNT_E_OUT_OF_ORDER;
2598 if(This->getbuf_last != done){
2599 OSSpinLockUnlock(&This->lock);
2600 return AUDCLNT_E_INVALID_SIZE;
2603 This->written_frames += done;
2604 This->held_frames -= done;
2605 This->lcl_offs_frames += done;
2606 This->lcl_offs_frames %= This->bufsize_frames;
2607 This->getbuf_last = 0;
2609 OSSpinLockUnlock(&This->lock);
2611 return S_OK;
2614 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2615 IAudioCaptureClient *iface, UINT32 *frames)
2617 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2619 TRACE("(%p)->(%p)\n", This, frames);
2621 if(!frames)
2622 return E_POINTER;
2624 OSSpinLockLock(&This->lock);
2626 capture_resample(This);
2628 if(This->held_frames >= This->period_frames)
2629 *frames = This->period_frames;
2630 else
2631 *frames = 0;
2633 OSSpinLockUnlock(&This->lock);
2635 return S_OK;
2638 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2640 AudioCaptureClient_QueryInterface,
2641 AudioCaptureClient_AddRef,
2642 AudioCaptureClient_Release,
2643 AudioCaptureClient_GetBuffer,
2644 AudioCaptureClient_ReleaseBuffer,
2645 AudioCaptureClient_GetNextPacketSize
2648 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2649 REFIID riid, void **ppv)
2651 ACImpl *This = impl_from_IAudioClock(iface);
2653 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2655 if(!ppv)
2656 return E_POINTER;
2657 *ppv = NULL;
2659 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2660 *ppv = iface;
2661 else if(IsEqualIID(riid, &IID_IAudioClock2))
2662 *ppv = &This->IAudioClock2_iface;
2663 if(*ppv){
2664 IUnknown_AddRef((IUnknown*)*ppv);
2665 return S_OK;
2668 WARN("Unknown interface %s\n", debugstr_guid(riid));
2669 return E_NOINTERFACE;
2672 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2674 ACImpl *This = impl_from_IAudioClock(iface);
2675 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2678 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2680 ACImpl *This = impl_from_IAudioClock(iface);
2681 return IAudioClient3_Release(&This->IAudioClient3_iface);
2684 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2686 ACImpl *This = impl_from_IAudioClock(iface);
2688 TRACE("(%p)->(%p)\n", This, freq);
2690 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2691 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2692 else
2693 *freq = This->fmt->nSamplesPerSec;
2695 return S_OK;
2698 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2699 UINT64 *pos, UINT64 *qpctime)
2701 *pos = This->written_frames - This->held_frames;
2703 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2704 *pos *= This->fmt->nBlockAlign;
2706 if(qpctime){
2707 LARGE_INTEGER stamp, freq;
2708 QueryPerformanceCounter(&stamp);
2709 QueryPerformanceFrequency(&freq);
2710 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2713 return S_OK;
2716 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2717 UINT64 *qpctime)
2719 ACImpl *This = impl_from_IAudioClock(iface);
2720 HRESULT hr;
2722 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2724 if(!pos)
2725 return E_POINTER;
2727 OSSpinLockLock(&This->lock);
2729 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2731 OSSpinLockUnlock(&This->lock);
2733 return hr;
2736 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2737 DWORD *chars)
2739 ACImpl *This = impl_from_IAudioClock(iface);
2741 TRACE("(%p)->(%p)\n", This, chars);
2743 if(!chars)
2744 return E_POINTER;
2746 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2748 return S_OK;
2751 static const IAudioClockVtbl AudioClock_Vtbl =
2753 AudioClock_QueryInterface,
2754 AudioClock_AddRef,
2755 AudioClock_Release,
2756 AudioClock_GetFrequency,
2757 AudioClock_GetPosition,
2758 AudioClock_GetCharacteristics
2761 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2762 REFIID riid, void **ppv)
2764 ACImpl *This = impl_from_IAudioClock2(iface);
2765 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2768 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2770 ACImpl *This = impl_from_IAudioClock2(iface);
2771 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2774 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2776 ACImpl *This = impl_from_IAudioClock2(iface);
2777 return IAudioClient3_Release(&This->IAudioClient3_iface);
2780 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2781 UINT64 *pos, UINT64 *qpctime)
2783 ACImpl *This = impl_from_IAudioClock2(iface);
2785 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2787 return E_NOTIMPL;
2790 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2792 AudioClock2_QueryInterface,
2793 AudioClock2_AddRef,
2794 AudioClock2_Release,
2795 AudioClock2_GetDevicePosition
2798 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2800 AudioSessionWrapper *ret;
2802 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2803 sizeof(AudioSessionWrapper));
2804 if(!ret)
2805 return NULL;
2807 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2808 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2809 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2811 ret->ref = 1;
2813 ret->client = client;
2814 if(client){
2815 ret->session = client->session;
2816 IAudioClient3_AddRef(&client->IAudioClient3_iface);
2819 return ret;
2822 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2823 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2825 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2827 if(!ppv)
2828 return E_POINTER;
2829 *ppv = NULL;
2831 if(IsEqualIID(riid, &IID_IUnknown) ||
2832 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2833 IsEqualIID(riid, &IID_IAudioSessionControl2))
2834 *ppv = iface;
2835 if(*ppv){
2836 IUnknown_AddRef((IUnknown*)*ppv);
2837 return S_OK;
2840 WARN("Unknown interface %s\n", debugstr_guid(riid));
2841 return E_NOINTERFACE;
2844 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2846 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2847 ULONG ref;
2848 ref = InterlockedIncrement(&This->ref);
2849 TRACE("(%p) Refcount now %u\n", This, ref);
2850 return ref;
2853 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2855 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2856 ULONG ref;
2857 ref = InterlockedDecrement(&This->ref);
2858 TRACE("(%p) Refcount now %u\n", This, ref);
2859 if(!ref){
2860 if(This->client){
2861 OSSpinLockLock(&This->client->lock);
2862 This->client->session_wrapper = NULL;
2863 OSSpinLockUnlock(&This->client->lock);
2864 AudioClient_Release(&This->client->IAudioClient3_iface);
2866 HeapFree(GetProcessHeap(), 0, This);
2868 return ref;
2871 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2872 AudioSessionState *state)
2874 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2875 ACImpl *client;
2877 TRACE("(%p)->(%p)\n", This, state);
2879 if(!state)
2880 return NULL_PTR_ERR;
2882 EnterCriticalSection(&g_sessions_lock);
2884 if(list_empty(&This->session->clients)){
2885 *state = AudioSessionStateExpired;
2886 LeaveCriticalSection(&g_sessions_lock);
2887 return S_OK;
2890 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2891 OSSpinLockLock(&client->lock);
2892 if(client->playing){
2893 *state = AudioSessionStateActive;
2894 OSSpinLockUnlock(&client->lock);
2895 LeaveCriticalSection(&g_sessions_lock);
2896 return S_OK;
2898 OSSpinLockUnlock(&client->lock);
2901 LeaveCriticalSection(&g_sessions_lock);
2903 *state = AudioSessionStateInactive;
2905 return S_OK;
2908 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2909 IAudioSessionControl2 *iface, WCHAR **name)
2911 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2913 FIXME("(%p)->(%p) - stub\n", This, name);
2915 return E_NOTIMPL;
2918 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2919 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2921 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2923 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2925 return E_NOTIMPL;
2928 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2929 IAudioSessionControl2 *iface, WCHAR **path)
2931 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2933 FIXME("(%p)->(%p) - stub\n", This, path);
2935 return E_NOTIMPL;
2938 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2939 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2941 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2943 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2945 return E_NOTIMPL;
2948 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2949 IAudioSessionControl2 *iface, GUID *group)
2951 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2953 FIXME("(%p)->(%p) - stub\n", This, group);
2955 return E_NOTIMPL;
2958 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2959 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2961 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2963 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2964 debugstr_guid(session));
2966 return E_NOTIMPL;
2969 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2970 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2972 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2974 FIXME("(%p)->(%p) - stub\n", This, events);
2976 return S_OK;
2979 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2980 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2982 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2984 FIXME("(%p)->(%p) - stub\n", This, events);
2986 return S_OK;
2989 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2990 IAudioSessionControl2 *iface, WCHAR **id)
2992 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2994 FIXME("(%p)->(%p) - stub\n", This, id);
2996 return E_NOTIMPL;
2999 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3000 IAudioSessionControl2 *iface, WCHAR **id)
3002 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3004 FIXME("(%p)->(%p) - stub\n", This, id);
3006 return E_NOTIMPL;
3009 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3010 IAudioSessionControl2 *iface, DWORD *pid)
3012 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3014 TRACE("(%p)->(%p)\n", This, pid);
3016 if(!pid)
3017 return E_POINTER;
3019 *pid = GetCurrentProcessId();
3021 return S_OK;
3024 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3025 IAudioSessionControl2 *iface)
3027 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3029 TRACE("(%p)\n", This);
3031 return S_FALSE;
3034 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3035 IAudioSessionControl2 *iface, BOOL optout)
3037 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3039 TRACE("(%p)->(%d)\n", This, optout);
3041 return S_OK;
3044 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3046 AudioSessionControl_QueryInterface,
3047 AudioSessionControl_AddRef,
3048 AudioSessionControl_Release,
3049 AudioSessionControl_GetState,
3050 AudioSessionControl_GetDisplayName,
3051 AudioSessionControl_SetDisplayName,
3052 AudioSessionControl_GetIconPath,
3053 AudioSessionControl_SetIconPath,
3054 AudioSessionControl_GetGroupingParam,
3055 AudioSessionControl_SetGroupingParam,
3056 AudioSessionControl_RegisterAudioSessionNotification,
3057 AudioSessionControl_UnregisterAudioSessionNotification,
3058 AudioSessionControl_GetSessionIdentifier,
3059 AudioSessionControl_GetSessionInstanceIdentifier,
3060 AudioSessionControl_GetProcessId,
3061 AudioSessionControl_IsSystemSoundsSession,
3062 AudioSessionControl_SetDuckingPreference
3065 /* index == -1 means set all channels, otherwise sets only the given channel */
3066 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
3068 Float32 level;
3069 OSStatus sc;
3071 if(This->session->mute)
3072 level = 0.;
3073 else{
3074 if(index == (UINT32)-1){
3075 UINT32 i;
3076 level = 1.;
3077 for(i = 0; i < This->fmt->nChannels; ++i){
3078 Float32 tmp;
3079 tmp = This->session->master_vol *
3080 This->session->channel_vols[i] * This->vols[i];
3081 level = tmp < level ? tmp : level;
3083 }else
3084 level = This->session->master_vol *
3085 This->session->channel_vols[index] * This->vols[index];
3088 sc = AudioUnitSetParameter(This->unit, kHALOutputParam_Volume,
3089 kAudioUnitScope_Global, 0, level, 0);
3090 if(sc != noErr)
3091 WARN("Couldn't set volume: %x\n", (int)sc);
3093 return S_OK;
3096 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
3098 HRESULT ret = S_OK;
3099 ACImpl *client;
3101 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
3102 HRESULT hr;
3103 hr = ca_setvol(client, index);
3104 if(FAILED(hr))
3105 ret = hr;
3108 return ret;
3111 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3112 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3114 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3116 if(!ppv)
3117 return E_POINTER;
3118 *ppv = NULL;
3120 if(IsEqualIID(riid, &IID_IUnknown) ||
3121 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3122 *ppv = iface;
3123 if(*ppv){
3124 IUnknown_AddRef((IUnknown*)*ppv);
3125 return S_OK;
3128 WARN("Unknown interface %s\n", debugstr_guid(riid));
3129 return E_NOINTERFACE;
3132 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3134 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3135 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3138 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3140 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3141 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3144 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3145 ISimpleAudioVolume *iface, float level, const GUID *context)
3147 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3148 AudioSession *session = This->session;
3149 HRESULT ret;
3151 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3153 if(level < 0.f || level > 1.f)
3154 return E_INVALIDARG;
3156 if(context)
3157 FIXME("Notifications not supported yet\n");
3159 EnterCriticalSection(&session->lock);
3161 session->master_vol = level;
3163 ret = ca_session_setvol(session, -1);
3165 LeaveCriticalSection(&session->lock);
3167 return ret;
3170 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3171 ISimpleAudioVolume *iface, float *level)
3173 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3174 AudioSession *session = This->session;
3176 TRACE("(%p)->(%p)\n", session, level);
3178 if(!level)
3179 return NULL_PTR_ERR;
3181 *level = session->master_vol;
3183 return S_OK;
3186 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3187 BOOL mute, const GUID *context)
3189 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3190 AudioSession *session = This->session;
3192 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3194 if(context)
3195 FIXME("Notifications not supported yet\n");
3197 EnterCriticalSection(&session->lock);
3199 session->mute = mute;
3201 ca_session_setvol(session, -1);
3203 LeaveCriticalSection(&session->lock);
3205 return S_OK;
3208 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3209 BOOL *mute)
3211 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3212 AudioSession *session = This->session;
3214 TRACE("(%p)->(%p)\n", session, mute);
3216 if(!mute)
3217 return NULL_PTR_ERR;
3219 *mute = session->mute;
3221 return S_OK;
3224 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3226 SimpleAudioVolume_QueryInterface,
3227 SimpleAudioVolume_AddRef,
3228 SimpleAudioVolume_Release,
3229 SimpleAudioVolume_SetMasterVolume,
3230 SimpleAudioVolume_GetMasterVolume,
3231 SimpleAudioVolume_SetMute,
3232 SimpleAudioVolume_GetMute
3235 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3236 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3238 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3240 if(!ppv)
3241 return E_POINTER;
3242 *ppv = NULL;
3244 if(IsEqualIID(riid, &IID_IUnknown) ||
3245 IsEqualIID(riid, &IID_IAudioStreamVolume))
3246 *ppv = iface;
3247 if(*ppv){
3248 IUnknown_AddRef((IUnknown*)*ppv);
3249 return S_OK;
3252 WARN("Unknown interface %s\n", debugstr_guid(riid));
3253 return E_NOINTERFACE;
3256 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3258 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3259 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3262 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3264 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3265 return IAudioClient3_Release(&This->IAudioClient3_iface);
3268 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3269 IAudioStreamVolume *iface, UINT32 *out)
3271 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3273 TRACE("(%p)->(%p)\n", This, out);
3275 if(!out)
3276 return E_POINTER;
3278 *out = This->fmt->nChannels;
3280 return S_OK;
3283 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3284 IAudioStreamVolume *iface, UINT32 index, float level)
3286 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3287 HRESULT ret;
3289 TRACE("(%p)->(%d, %f)\n", This, index, level);
3291 if(level < 0.f || level > 1.f)
3292 return E_INVALIDARG;
3294 if(index >= This->fmt->nChannels)
3295 return E_INVALIDARG;
3297 OSSpinLockLock(&This->lock);
3299 This->vols[index] = level;
3301 WARN("CoreAudio doesn't support per-channel volume control\n");
3302 ret = ca_setvol(This, index);
3304 OSSpinLockUnlock(&This->lock);
3306 return ret;
3309 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3310 IAudioStreamVolume *iface, UINT32 index, float *level)
3312 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3314 TRACE("(%p)->(%d, %p)\n", This, index, level);
3316 if(!level)
3317 return E_POINTER;
3319 if(index >= This->fmt->nChannels)
3320 return E_INVALIDARG;
3322 *level = This->vols[index];
3324 return S_OK;
3327 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3328 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3330 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3331 int i;
3332 HRESULT ret;
3334 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3336 if(!levels)
3337 return E_POINTER;
3339 if(count != This->fmt->nChannels)
3340 return E_INVALIDARG;
3342 OSSpinLockLock(&This->lock);
3344 for(i = 0; i < count; ++i)
3345 This->vols[i] = levels[i];
3347 ret = ca_setvol(This, -1);
3349 OSSpinLockUnlock(&This->lock);
3351 return ret;
3354 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3355 IAudioStreamVolume *iface, UINT32 count, float *levels)
3357 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3358 int i;
3360 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3362 if(!levels)
3363 return E_POINTER;
3365 if(count != This->fmt->nChannels)
3366 return E_INVALIDARG;
3368 OSSpinLockLock(&This->lock);
3370 for(i = 0; i < count; ++i)
3371 levels[i] = This->vols[i];
3373 OSSpinLockUnlock(&This->lock);
3375 return S_OK;
3378 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3380 AudioStreamVolume_QueryInterface,
3381 AudioStreamVolume_AddRef,
3382 AudioStreamVolume_Release,
3383 AudioStreamVolume_GetChannelCount,
3384 AudioStreamVolume_SetChannelVolume,
3385 AudioStreamVolume_GetChannelVolume,
3386 AudioStreamVolume_SetAllVolumes,
3387 AudioStreamVolume_GetAllVolumes
3390 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3391 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3393 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3395 if(!ppv)
3396 return E_POINTER;
3397 *ppv = NULL;
3399 if(IsEqualIID(riid, &IID_IUnknown) ||
3400 IsEqualIID(riid, &IID_IChannelAudioVolume))
3401 *ppv = iface;
3402 if(*ppv){
3403 IUnknown_AddRef((IUnknown*)*ppv);
3404 return S_OK;
3407 WARN("Unknown interface %s\n", debugstr_guid(riid));
3408 return E_NOINTERFACE;
3411 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3413 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3414 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3417 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3419 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3420 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3423 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3424 IChannelAudioVolume *iface, UINT32 *out)
3426 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3427 AudioSession *session = This->session;
3429 TRACE("(%p)->(%p)\n", session, out);
3431 if(!out)
3432 return NULL_PTR_ERR;
3434 *out = session->channel_count;
3436 return S_OK;
3439 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3440 IChannelAudioVolume *iface, UINT32 index, float level,
3441 const GUID *context)
3443 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3444 AudioSession *session = This->session;
3445 HRESULT ret;
3447 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3448 wine_dbgstr_guid(context));
3450 if(level < 0.f || level > 1.f)
3451 return E_INVALIDARG;
3453 if(index >= session->channel_count)
3454 return E_INVALIDARG;
3456 if(context)
3457 FIXME("Notifications not supported yet\n");
3459 EnterCriticalSection(&session->lock);
3461 session->channel_vols[index] = level;
3463 WARN("CoreAudio doesn't support per-channel volume control\n");
3464 ret = ca_session_setvol(session, index);
3466 LeaveCriticalSection(&session->lock);
3468 return ret;
3471 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3472 IChannelAudioVolume *iface, UINT32 index, float *level)
3474 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3475 AudioSession *session = This->session;
3477 TRACE("(%p)->(%d, %p)\n", session, index, level);
3479 if(!level)
3480 return NULL_PTR_ERR;
3482 if(index >= session->channel_count)
3483 return E_INVALIDARG;
3485 *level = session->channel_vols[index];
3487 return S_OK;
3490 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3491 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3492 const GUID *context)
3494 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3495 AudioSession *session = This->session;
3496 int i;
3497 HRESULT ret;
3499 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3500 wine_dbgstr_guid(context));
3502 if(!levels)
3503 return NULL_PTR_ERR;
3505 if(count != session->channel_count)
3506 return E_INVALIDARG;
3508 if(context)
3509 FIXME("Notifications not supported yet\n");
3511 EnterCriticalSection(&session->lock);
3513 for(i = 0; i < count; ++i)
3514 session->channel_vols[i] = levels[i];
3516 ret = ca_session_setvol(session, -1);
3518 LeaveCriticalSection(&session->lock);
3520 return ret;
3523 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3524 IChannelAudioVolume *iface, UINT32 count, float *levels)
3526 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3527 AudioSession *session = This->session;
3528 int i;
3530 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3532 if(!levels)
3533 return NULL_PTR_ERR;
3535 if(count != session->channel_count)
3536 return E_INVALIDARG;
3538 for(i = 0; i < count; ++i)
3539 levels[i] = session->channel_vols[i];
3541 return S_OK;
3544 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3546 ChannelAudioVolume_QueryInterface,
3547 ChannelAudioVolume_AddRef,
3548 ChannelAudioVolume_Release,
3549 ChannelAudioVolume_GetChannelCount,
3550 ChannelAudioVolume_SetChannelVolume,
3551 ChannelAudioVolume_GetChannelVolume,
3552 ChannelAudioVolume_SetAllVolumes,
3553 ChannelAudioVolume_GetAllVolumes
3556 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3557 REFIID riid, void **ppv)
3559 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3561 if(!ppv)
3562 return E_POINTER;
3563 *ppv = NULL;
3565 if(IsEqualIID(riid, &IID_IUnknown) ||
3566 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3567 IsEqualIID(riid, &IID_IAudioSessionManager2))
3568 *ppv = iface;
3569 if(*ppv){
3570 IUnknown_AddRef((IUnknown*)*ppv);
3571 return S_OK;
3574 WARN("Unknown interface %s\n", debugstr_guid(riid));
3575 return E_NOINTERFACE;
3578 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3580 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3581 ULONG ref;
3582 ref = InterlockedIncrement(&This->ref);
3583 TRACE("(%p) Refcount now %u\n", This, ref);
3584 return ref;
3587 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3589 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3590 ULONG ref;
3591 ref = InterlockedDecrement(&This->ref);
3592 TRACE("(%p) Refcount now %u\n", This, ref);
3593 if(!ref)
3594 HeapFree(GetProcessHeap(), 0, This);
3595 return ref;
3598 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3599 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3600 IAudioSessionControl **out)
3602 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3603 AudioSession *session;
3604 AudioSessionWrapper *wrapper;
3605 HRESULT hr;
3607 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3608 flags, out);
3610 hr = get_audio_session(session_guid, This->device, 0, &session);
3611 if(FAILED(hr))
3612 return hr;
3614 wrapper = AudioSessionWrapper_Create(NULL);
3615 if(!wrapper)
3616 return E_OUTOFMEMORY;
3618 wrapper->session = session;
3620 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3622 return S_OK;
3625 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3626 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3627 ISimpleAudioVolume **out)
3629 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3630 AudioSession *session;
3631 AudioSessionWrapper *wrapper;
3632 HRESULT hr;
3634 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3635 flags, out);
3637 hr = get_audio_session(session_guid, This->device, 0, &session);
3638 if(FAILED(hr))
3639 return hr;
3641 wrapper = AudioSessionWrapper_Create(NULL);
3642 if(!wrapper)
3643 return E_OUTOFMEMORY;
3645 wrapper->session = session;
3647 *out = &wrapper->ISimpleAudioVolume_iface;
3649 return S_OK;
3652 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3653 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3655 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3656 FIXME("(%p)->(%p) - stub\n", This, out);
3657 return E_NOTIMPL;
3660 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3661 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3663 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3664 FIXME("(%p)->(%p) - stub\n", This, notification);
3665 return E_NOTIMPL;
3668 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3669 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3671 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3672 FIXME("(%p)->(%p) - stub\n", This, notification);
3673 return E_NOTIMPL;
3676 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3677 IAudioSessionManager2 *iface, const WCHAR *session_id,
3678 IAudioVolumeDuckNotification *notification)
3680 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3681 FIXME("(%p)->(%p) - stub\n", This, notification);
3682 return E_NOTIMPL;
3685 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3686 IAudioSessionManager2 *iface,
3687 IAudioVolumeDuckNotification *notification)
3689 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3690 FIXME("(%p)->(%p) - stub\n", This, notification);
3691 return E_NOTIMPL;
3694 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3696 AudioSessionManager_QueryInterface,
3697 AudioSessionManager_AddRef,
3698 AudioSessionManager_Release,
3699 AudioSessionManager_GetAudioSessionControl,
3700 AudioSessionManager_GetSimpleAudioVolume,
3701 AudioSessionManager_GetSessionEnumerator,
3702 AudioSessionManager_RegisterSessionNotification,
3703 AudioSessionManager_UnregisterSessionNotification,
3704 AudioSessionManager_RegisterDuckNotification,
3705 AudioSessionManager_UnregisterDuckNotification
3708 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3709 IAudioSessionManager2 **out)
3711 SessionMgr *This;
3713 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3714 if(!This)
3715 return E_OUTOFMEMORY;
3717 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3718 This->device = device;
3719 This->ref = 1;
3721 *out = &This->IAudioSessionManager2_iface;
3723 return S_OK;