wined3d: Move swapchain buffer discarding to wined3d_cs_exec_present().
[wine.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobacfa01c3d62d69d421a6bf339a9c2e703b38e7fa
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 #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H
76 /* Define new AudioComponent Manager functions for OSX 10.5 */
77 typedef Component AudioComponent;
78 typedef ComponentDescription AudioComponentDescription;
79 typedef ComponentInstance AudioComponentInstance;
81 static inline AudioComponent AudioComponentFindNext(AudioComponent ac, AudioComponentDescription *desc)
83 return FindNextComponent(ac, desc);
86 static inline OSStatus AudioComponentInstanceNew(AudioComponent ac, AudioComponentInstance *aci)
88 return OpenAComponent(ac, aci);
91 static inline OSStatus AudioComponentInstanceDispose(AudioComponentInstance aci)
93 return CloseComponent(aci);
95 #endif
97 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
99 static const REFERENCE_TIME DefaultPeriod = 100000;
100 static const REFERENCE_TIME MinimumPeriod = 50000;
102 struct ACImpl;
103 typedef struct ACImpl ACImpl;
105 typedef struct _AudioSession {
106 GUID guid;
107 struct list clients;
109 IMMDevice *device;
111 float master_vol;
112 UINT32 channel_count;
113 float *channel_vols;
114 BOOL mute;
116 CRITICAL_SECTION lock;
118 struct list entry;
119 } AudioSession;
121 typedef struct _AudioSessionWrapper {
122 IAudioSessionControl2 IAudioSessionControl2_iface;
123 IChannelAudioVolume IChannelAudioVolume_iface;
124 ISimpleAudioVolume ISimpleAudioVolume_iface;
126 LONG ref;
128 ACImpl *client;
129 AudioSession *session;
130 } AudioSessionWrapper;
132 struct ACImpl {
133 IAudioClient IAudioClient_iface;
134 IAudioRenderClient IAudioRenderClient_iface;
135 IAudioCaptureClient IAudioCaptureClient_iface;
136 IAudioClock IAudioClock_iface;
137 IAudioClock2 IAudioClock2_iface;
138 IAudioStreamVolume IAudioStreamVolume_iface;
140 LONG ref;
142 IMMDevice *parent;
143 IUnknown *pUnkFTMarshal;
145 WAVEFORMATEX *fmt;
147 EDataFlow dataflow;
148 DWORD flags;
149 AUDCLNT_SHAREMODE share;
150 HANDLE event;
151 float *vols;
153 BOOL initted;
154 AudioDeviceID adevid;
155 AudioObjectPropertyScope scope;
156 AudioConverterRef converter;
157 AudioComponentInstance unit;
158 AudioStreamBasicDescription dev_desc; /* audio unit format, not necessarily the same as fmt */
159 HANDLE timer;
160 UINT32 period_ms, bufsize_frames, period_frames;
161 UINT64 written_frames;
162 UINT32 lcl_offs_frames, wri_offs_frames, held_frames, tmp_buffer_frames;
163 UINT32 cap_bufsize_frames, cap_offs_frames, cap_held_frames, wrap_bufsize_frames, resamp_bufsize_frames;
164 INT32 getbuf_last;
165 BOOL playing;
166 BYTE *cap_buffer, *wrap_buffer, *resamp_buffer, *local_buffer, *tmp_buffer;
168 AudioSession *session;
169 AudioSessionWrapper *session_wrapper;
171 struct list entry;
173 OSSpinLock lock;
176 static const IAudioClientVtbl AudioClient_Vtbl;
177 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
178 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
179 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
180 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
181 static const IAudioClockVtbl AudioClock_Vtbl;
182 static const IAudioClock2Vtbl AudioClock2_Vtbl;
183 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
184 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
185 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
187 typedef struct _SessionMgr {
188 IAudioSessionManager2 IAudioSessionManager2_iface;
190 LONG ref;
192 IMMDevice *device;
193 } SessionMgr;
195 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
196 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
197 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v','\\','d','e','v','i','c','e','s',0};
198 static const WCHAR guidW[] = {'g','u','i','d',0};
200 static HANDLE g_timer_q;
202 static CRITICAL_SECTION g_sessions_lock;
203 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
205 0, 0, &g_sessions_lock,
206 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
209 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
210 static struct list g_sessions = LIST_INIT(g_sessions);
212 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
213 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
215 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
220 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
222 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
225 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
227 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
230 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
232 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
235 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
237 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
240 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
242 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
245 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
247 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
250 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
252 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
255 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
257 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
260 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
262 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
265 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
267 switch (reason)
269 case DLL_PROCESS_ATTACH:
270 g_timer_q = CreateTimerQueue();
271 if(!g_timer_q)
272 return FALSE;
273 break;
275 case DLL_PROCESS_DETACH:
276 if (reserved) break;
277 DeleteCriticalSection(&g_sessions_lock);
278 break;
280 return TRUE;
283 /* From <dlls/mmdevapi/mmdevapi.h> */
284 enum DriverPriority {
285 Priority_Unavailable = 0,
286 Priority_Low,
287 Priority_Neutral,
288 Priority_Preferred
291 int WINAPI AUDDRV_GetPriority(void)
293 return Priority_Neutral;
296 static HRESULT osstatus_to_hresult(OSStatus sc)
298 switch(sc){
299 case kAudioFormatUnsupportedDataFormatError:
300 case kAudioFormatUnknownFormatError:
301 case kAudioDeviceUnsupportedFormatError:
302 return AUDCLNT_E_UNSUPPORTED_FORMAT;
303 case kAudioHardwareBadDeviceError:
304 return AUDCLNT_E_DEVICE_INVALIDATED;
306 return E_FAIL;
309 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
310 GUID *guid)
312 HKEY key;
313 BOOL opened = FALSE;
314 LONG lr;
316 if(!drv_key){
317 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
318 NULL, &drv_key, NULL);
319 if(lr != ERROR_SUCCESS){
320 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
321 return;
323 opened = TRUE;
326 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
327 NULL, &key, NULL);
328 if(lr != ERROR_SUCCESS){
329 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
330 goto exit;
333 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
334 sizeof(GUID));
335 if(lr != ERROR_SUCCESS)
336 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
338 RegCloseKey(key);
339 exit:
340 if(opened)
341 RegCloseKey(drv_key);
344 static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid)
346 HKEY key = NULL, dev_key;
347 DWORD type, size = sizeof(*guid);
348 WCHAR key_name[256];
350 static const WCHAR key_fmt[] = {'%','u',0};
352 if(flow == eCapture)
353 key_name[0] = '1';
354 else
355 key_name[0] = '0';
356 key_name[1] = ',';
358 sprintfW(key_name + 2, key_fmt, device);
360 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
361 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
362 if(RegQueryValueExW(dev_key, guidW, 0, &type,
363 (BYTE*)guid, &size) == ERROR_SUCCESS){
364 if(type == REG_BINARY){
365 RegCloseKey(dev_key);
366 RegCloseKey(key);
367 return;
369 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
370 wine_dbgstr_w(key_name), type);
372 RegCloseKey(dev_key);
376 CoCreateGuid(guid);
378 set_device_guid(flow, key, key_name, guid);
380 if(key)
381 RegCloseKey(key);
384 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
385 GUID **guids, UINT *num, UINT *def_index)
387 UInt32 devsize, size;
388 AudioDeviceID *devices;
389 AudioDeviceID default_id;
390 AudioObjectPropertyAddress addr;
391 OSStatus sc;
392 int i, ndevices;
394 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
396 addr.mScope = kAudioObjectPropertyScopeGlobal;
397 addr.mElement = kAudioObjectPropertyElementMaster;
398 if(flow == eRender)
399 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
400 else if(flow == eCapture)
401 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
402 else
403 return E_INVALIDARG;
405 size = sizeof(default_id);
406 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
407 NULL, &size, &default_id);
408 if(sc != noErr){
409 WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc);
410 default_id = -1;
413 addr.mSelector = kAudioHardwarePropertyDevices;
414 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
415 NULL, &devsize);
416 if(sc != noErr){
417 WARN("Getting _Devices property size failed: %x\n", (int)sc);
418 return osstatus_to_hresult(sc);
421 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
422 if(!devices)
423 return E_OUTOFMEMORY;
425 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
426 &devsize, devices);
427 if(sc != noErr){
428 WARN("Getting _Devices property failed: %x\n", (int)sc);
429 HeapFree(GetProcessHeap(), 0, devices);
430 return osstatus_to_hresult(sc);
433 ndevices = devsize / sizeof(AudioDeviceID);
435 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
436 if(!*ids){
437 HeapFree(GetProcessHeap(), 0, devices);
438 return E_OUTOFMEMORY;
441 *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
442 if(!*guids){
443 HeapFree(GetProcessHeap(), 0, *ids);
444 HeapFree(GetProcessHeap(), 0, devices);
445 return E_OUTOFMEMORY;
448 *num = 0;
449 *def_index = (UINT)-1;
450 for(i = 0; i < ndevices; ++i){
451 AudioBufferList *buffers;
452 CFStringRef name;
453 SIZE_T len;
454 int j;
456 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
457 if(flow == eRender)
458 addr.mScope = kAudioDevicePropertyScopeOutput;
459 else
460 addr.mScope = kAudioDevicePropertyScopeInput;
461 addr.mElement = 0;
462 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
463 if(sc != noErr){
464 WARN("Unable to get _StreamConfiguration property size for "
465 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
466 continue;
469 buffers = HeapAlloc(GetProcessHeap(), 0, size);
470 if(!buffers){
471 HeapFree(GetProcessHeap(), 0, devices);
472 for(j = 0; j < *num; ++j)
473 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
474 HeapFree(GetProcessHeap(), 0, *guids);
475 HeapFree(GetProcessHeap(), 0, *ids);
476 return E_OUTOFMEMORY;
479 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
480 &size, buffers);
481 if(sc != noErr){
482 WARN("Unable to get _StreamConfiguration property for "
483 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
484 HeapFree(GetProcessHeap(), 0, buffers);
485 continue;
488 /* check that there's at least one channel in this device before
489 * we claim it as usable */
490 for(j = 0; j < buffers->mNumberBuffers; ++j)
491 if(buffers->mBuffers[j].mNumberChannels > 0)
492 break;
493 if(j >= buffers->mNumberBuffers){
494 HeapFree(GetProcessHeap(), 0, buffers);
495 continue;
498 HeapFree(GetProcessHeap(), 0, buffers);
500 size = sizeof(name);
501 addr.mSelector = kAudioObjectPropertyName;
502 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
503 &size, &name);
504 if(sc != noErr){
505 WARN("Unable to get _Name property for device %u: %x\n",
506 (unsigned int)devices[i], (int)sc);
507 continue;
510 len = CFStringGetLength(name) + 1;
511 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
512 if(!(*ids)[*num]){
513 CFRelease(name);
514 HeapFree(GetProcessHeap(), 0, devices);
515 for(j = 0; j < *num; ++j)
516 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
517 HeapFree(GetProcessHeap(), 0, *ids);
518 HeapFree(GetProcessHeap(), 0, *guids);
519 return E_OUTOFMEMORY;
521 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
522 ((*ids)[*num])[len - 1] = 0;
523 CFRelease(name);
525 get_device_guid(flow, devices[i], &(*guids)[*num]);
527 if(*def_index == (UINT)-1 && devices[i] == default_id)
528 *def_index = *num;
530 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
531 (unsigned int)devices[i], (*def_index == *num) ? " (default)" : "");
533 (*num)++;
536 if(*def_index == (UINT)-1)
537 *def_index = 0;
539 HeapFree(GetProcessHeap(), 0, devices);
541 return S_OK;
544 static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow)
546 HKEY devices_key;
547 UINT i = 0;
548 WCHAR key_name[256];
549 DWORD key_name_size;
551 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
552 ERR("No devices in registry?\n");
553 return FALSE;
556 while(1){
557 HKEY key;
558 DWORD size, type;
559 GUID reg_guid;
561 key_name_size = sizeof(key_name);
562 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
563 NULL, NULL, NULL) != ERROR_SUCCESS)
564 break;
566 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
567 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
568 continue;
571 size = sizeof(reg_guid);
572 if(RegQueryValueExW(key, guidW, 0, &type,
573 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
574 if(IsEqualGUID(&reg_guid, guid)){
575 RegCloseKey(key);
576 RegCloseKey(devices_key);
578 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
580 if(key_name[0] == '0')
581 *flow = eRender;
582 else if(key_name[0] == '1')
583 *flow = eCapture;
584 else{
585 ERR("Unknown device type: %c\n", key_name[0]);
586 return FALSE;
589 *id = strtoulW(key_name + 2, NULL, 10);
591 return TRUE;
595 RegCloseKey(key);
598 RegCloseKey(devices_key);
600 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
602 return FALSE;
605 static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
607 AudioComponentInstance unit;
608 AudioComponent comp;
609 AudioComponentDescription desc;
610 OSStatus sc;
612 memset(&desc, 0, sizeof(desc));
613 desc.componentType = kAudioUnitType_Output;
614 desc.componentSubType = kAudioUnitSubType_HALOutput;
615 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
617 if(!(comp = AudioComponentFindNext(NULL, &desc))){
618 WARN("AudioComponentFindNext failed\n");
619 return NULL;
622 sc = AudioComponentInstanceNew(comp, &unit);
623 if(sc != noErr){
624 WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
625 return NULL;
628 if(dataflow == eCapture){
629 UInt32 enableio;
631 enableio = 1;
632 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
633 kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
634 if(sc != noErr){
635 WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
636 AudioComponentInstanceDispose(unit);
637 return NULL;
640 enableio = 0;
641 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
642 kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
643 if(sc != noErr){
644 WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
645 AudioComponentInstanceDispose(unit);
646 return NULL;
650 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
651 kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
652 if(sc != noErr){
653 WARN("Couldn't set audio unit device\n");
654 AudioComponentInstanceDispose(unit);
655 return NULL;
658 return unit;
661 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
663 ACImpl *This;
664 AudioDeviceID adevid;
665 EDataFlow dataflow;
666 HRESULT hr;
668 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
670 if(!get_deviceid_by_guid(guid, &adevid, &dataflow))
671 return AUDCLNT_E_DEVICE_INVALIDATED;
673 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
674 if(!This)
675 return E_OUTOFMEMORY;
677 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
678 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
679 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
680 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
681 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
682 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
684 This->dataflow = dataflow;
686 if(dataflow == eRender)
687 This->scope = kAudioDevicePropertyScopeOutput;
688 else if(dataflow == eCapture)
689 This->scope = kAudioDevicePropertyScopeInput;
690 else{
691 HeapFree(GetProcessHeap(), 0, This);
692 return E_INVALIDARG;
695 This->lock = 0;
697 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface, &This->pUnkFTMarshal);
698 if (FAILED(hr)) {
699 HeapFree(GetProcessHeap(), 0, This);
700 return hr;
703 This->parent = dev;
704 IMMDevice_AddRef(This->parent);
706 This->adevid = adevid;
708 if(!(This->unit = get_audiounit(This->dataflow, This->adevid))){
709 HeapFree(GetProcessHeap(), 0, This);
710 return AUDCLNT_E_DEVICE_INVALIDATED;
713 *out = &This->IAudioClient_iface;
714 IAudioClient_AddRef(&This->IAudioClient_iface);
716 return S_OK;
719 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
720 REFIID riid, void **ppv)
722 ACImpl *This = impl_from_IAudioClient(iface);
723 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
725 if(!ppv)
726 return E_POINTER;
727 *ppv = NULL;
728 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
729 *ppv = iface;
730 else if(IsEqualIID(riid, &IID_IMarshal))
731 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
733 if(*ppv){
734 IUnknown_AddRef((IUnknown*)*ppv);
735 return S_OK;
737 WARN("Unknown interface %s\n", debugstr_guid(riid));
738 return E_NOINTERFACE;
741 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
743 ACImpl *This = impl_from_IAudioClient(iface);
744 ULONG ref;
745 ref = InterlockedIncrement(&This->ref);
746 TRACE("(%p) Refcount now %u\n", This, ref);
747 return ref;
750 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
752 ACImpl *This = impl_from_IAudioClient(iface);
753 ULONG ref;
754 ref = InterlockedDecrement(&This->ref);
755 TRACE("(%p) Refcount now %u\n", This, ref);
756 if(!ref){
757 if(This->timer){
758 HANDLE event;
759 BOOL wait;
760 event = CreateEventW(NULL, TRUE, FALSE, NULL);
761 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
762 wait = wait && GetLastError() == ERROR_IO_PENDING;
763 if(event && wait)
764 WaitForSingleObject(event, INFINITE);
765 CloseHandle(event);
767 AudioOutputUnitStop(This->unit);
768 AudioComponentInstanceDispose(This->unit);
769 if(This->converter)
770 AudioConverterDispose(This->converter);
771 if(This->session){
772 EnterCriticalSection(&g_sessions_lock);
773 list_remove(&This->entry);
774 LeaveCriticalSection(&g_sessions_lock);
776 HeapFree(GetProcessHeap(), 0, This->vols);
777 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
778 HeapFree(GetProcessHeap(), 0, This->cap_buffer);
779 HeapFree(GetProcessHeap(), 0, This->local_buffer);
780 free(This->wrap_buffer);
781 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
782 CoTaskMemFree(This->fmt);
783 IMMDevice_Release(This->parent);
784 IUnknown_Release(This->pUnkFTMarshal);
785 HeapFree(GetProcessHeap(), 0, This);
787 return ref;
790 static void dump_fmt(const WAVEFORMATEX *fmt)
792 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
793 switch(fmt->wFormatTag){
794 case WAVE_FORMAT_PCM:
795 TRACE("WAVE_FORMAT_PCM");
796 break;
797 case WAVE_FORMAT_IEEE_FLOAT:
798 TRACE("WAVE_FORMAT_IEEE_FLOAT");
799 break;
800 case WAVE_FORMAT_EXTENSIBLE:
801 TRACE("WAVE_FORMAT_EXTENSIBLE");
802 break;
803 default:
804 TRACE("Unknown");
805 break;
807 TRACE(")\n");
809 TRACE("nChannels: %u\n", fmt->nChannels);
810 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
811 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
812 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
813 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
814 TRACE("cbSize: %u\n", fmt->cbSize);
816 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
817 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
818 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
819 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
820 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
824 static DWORD get_channel_mask(unsigned int channels)
826 switch(channels){
827 case 0:
828 return 0;
829 case 1:
830 return KSAUDIO_SPEAKER_MONO;
831 case 2:
832 return KSAUDIO_SPEAKER_STEREO;
833 case 3:
834 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
835 case 4:
836 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
837 case 5:
838 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
839 case 6:
840 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
841 case 7:
842 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
843 case 8:
844 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
846 FIXME("Unknown speaker configuration: %u\n", channels);
847 return 0;
850 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
852 WAVEFORMATEX *ret;
853 size_t size;
855 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
856 size = sizeof(WAVEFORMATEXTENSIBLE);
857 else
858 size = sizeof(WAVEFORMATEX);
860 ret = CoTaskMemAlloc(size);
861 if(!ret)
862 return NULL;
864 memcpy(ret, fmt, size);
866 ret->cbSize = size - sizeof(WAVEFORMATEX);
868 return ret;
871 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
872 const WAVEFORMATEX *fmt)
874 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
876 desc->mFormatFlags = 0;
878 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
879 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
880 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
881 desc->mFormatID = kAudioFormatLinearPCM;
882 if(fmt->wBitsPerSample > 8)
883 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
884 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
885 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
886 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
887 desc->mFormatID = kAudioFormatLinearPCM;
888 desc->mFormatFlags = kAudioFormatFlagIsFloat;
889 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
890 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
891 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
892 desc->mFormatID = kAudioFormatULaw;
893 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
894 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
895 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
896 desc->mFormatID = kAudioFormatALaw;
897 }else
898 return AUDCLNT_E_UNSUPPORTED_FORMAT;
900 desc->mSampleRate = fmt->nSamplesPerSec;
901 desc->mBytesPerPacket = fmt->nBlockAlign;
902 desc->mFramesPerPacket = 1;
903 desc->mBytesPerFrame = fmt->nBlockAlign;
904 desc->mChannelsPerFrame = fmt->nChannels;
905 desc->mBitsPerChannel = fmt->wBitsPerSample;
906 desc->mReserved = 0;
908 return S_OK;
911 static void session_init_vols(AudioSession *session, UINT channels)
913 if(session->channel_count < channels){
914 UINT i;
916 if(session->channel_vols)
917 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
918 session->channel_vols, sizeof(float) * channels);
919 else
920 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
921 sizeof(float) * channels);
922 if(!session->channel_vols)
923 return;
925 for(i = session->channel_count; i < channels; ++i)
926 session->channel_vols[i] = 1.f;
928 session->channel_count = channels;
932 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
933 UINT num_channels)
935 AudioSession *ret;
937 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
938 if(!ret)
939 return NULL;
941 memcpy(&ret->guid, guid, sizeof(GUID));
943 ret->device = device;
945 list_init(&ret->clients);
947 list_add_head(&g_sessions, &ret->entry);
949 InitializeCriticalSection(&ret->lock);
950 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
952 session_init_vols(ret, num_channels);
954 ret->master_vol = 1.f;
956 return ret;
959 /* if channels == 0, then this will return or create a session with
960 * matching dataflow and GUID. otherwise, channels must also match */
961 static HRESULT get_audio_session(const GUID *sessionguid,
962 IMMDevice *device, UINT channels, AudioSession **out)
964 AudioSession *session;
966 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
967 *out = create_session(&GUID_NULL, device, channels);
968 if(!*out)
969 return E_OUTOFMEMORY;
971 return S_OK;
974 *out = NULL;
975 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
976 if(session->device == device &&
977 IsEqualGUID(sessionguid, &session->guid)){
978 session_init_vols(session, channels);
979 *out = session;
980 break;
984 if(!*out){
985 *out = create_session(sessionguid, device, channels);
986 if(!*out)
987 return E_OUTOFMEMORY;
990 return S_OK;
993 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
994 BYTE *src, UINT32 src_bytes)
996 UINT32 chunk_bytes = dst_bytes - dst_offs;
998 if(chunk_bytes < src_bytes){
999 memcpy(dst + dst_offs, src, chunk_bytes);
1000 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
1001 }else
1002 memcpy(dst + dst_offs, src, src_bytes);
1005 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1007 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1008 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1009 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1010 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1011 This->fmt->wBitsPerSample == 8)
1012 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1013 else
1014 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1017 /* CA is pulling data from us */
1018 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
1019 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1020 AudioBufferList *data)
1022 ACImpl *This = user;
1023 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
1025 OSSpinLockLock(&This->lock);
1027 if(This->playing){
1028 lcl_offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1029 to_copy_frames = min(nframes, This->held_frames);
1030 to_copy_bytes = to_copy_frames * This->fmt->nBlockAlign;
1032 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) * This->fmt->nBlockAlign;
1034 if(to_copy_bytes > chunk_bytes){
1035 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, chunk_bytes);
1036 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, This->local_buffer, to_copy_bytes - chunk_bytes);
1037 }else
1038 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, to_copy_bytes);
1040 This->lcl_offs_frames += to_copy_frames;
1041 This->lcl_offs_frames %= This->bufsize_frames;
1042 This->held_frames -= to_copy_frames;
1043 }else
1044 to_copy_bytes = to_copy_frames = 0;
1046 if(nframes > to_copy_frames)
1047 silence_buffer(This, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
1049 OSSpinLockUnlock(&This->lock);
1051 return noErr;
1054 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1056 if(left <= right)
1057 return right - left;
1058 return bufsize - (left - right);
1061 /* place data from cap_buffer into provided AudioBufferList */
1062 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1063 AudioStreamPacketDescription **packets, void *user)
1065 ACImpl *This = user;
1067 *nframes = min(*nframes, This->cap_held_frames);
1068 if(!*nframes){
1069 data->mBuffers[0].mData = NULL;
1070 data->mBuffers[0].mDataByteSize = 0;
1071 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1072 return noErr;
1075 data->mBuffers[0].mDataByteSize = *nframes * This->fmt->nBlockAlign;
1076 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1078 if(This->cap_offs_frames + *nframes > This->cap_bufsize_frames){
1079 UINT32 chunk_frames = This->cap_bufsize_frames - This->cap_offs_frames;
1081 if(This->wrap_bufsize_frames < *nframes){
1082 free(This->wrap_buffer);
1083 This->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1084 This->wrap_bufsize_frames = *nframes;
1087 memcpy(This->wrap_buffer, This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign,
1088 chunk_frames * This->fmt->nBlockAlign);
1089 memcpy(This->wrap_buffer + chunk_frames * This->fmt->nBlockAlign, This->cap_buffer,
1090 (*nframes - chunk_frames) * This->fmt->nBlockAlign);
1092 data->mBuffers[0].mData = This->wrap_buffer;
1093 }else
1094 data->mBuffers[0].mData = This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign;
1096 This->cap_offs_frames += *nframes;
1097 This->cap_offs_frames %= This->cap_bufsize_frames;
1098 This->cap_held_frames -= *nframes;
1100 if(packets)
1101 *packets = NULL;
1103 return noErr;
1106 static void capture_resample(ACImpl *This)
1108 UINT32 resamp_period_frames = MulDiv(This->period_frames, This->dev_desc.mSampleRate, This->fmt->nSamplesPerSec);
1109 OSStatus sc;
1111 /* the resampling process often needs more source frames than we'd
1112 * guess from a straight conversion using the sample rate ratio. so
1113 * only convert if we have extra source data. */
1114 while(This->cap_held_frames > resamp_period_frames * 2){
1115 AudioBufferList converted_list;
1116 UInt32 wanted_frames = This->period_frames;
1118 converted_list.mNumberBuffers = 1;
1119 converted_list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1120 converted_list.mBuffers[0].mDataByteSize = wanted_frames * This->fmt->nBlockAlign;
1122 if(This->resamp_bufsize_frames < wanted_frames){
1123 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
1124 This->resamp_buffer = HeapAlloc(GetProcessHeap(), 0, converted_list.mBuffers[0].mDataByteSize);
1125 This->resamp_bufsize_frames = wanted_frames;
1128 converted_list.mBuffers[0].mData = This->resamp_buffer;
1130 sc = AudioConverterFillComplexBuffer(This->converter, feed_cb,
1131 This, &wanted_frames, &converted_list, NULL);
1132 if(sc != noErr){
1133 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1134 break;
1137 ca_wrap_buffer(This->local_buffer,
1138 This->wri_offs_frames * This->fmt->nBlockAlign,
1139 This->bufsize_frames * This->fmt->nBlockAlign,
1140 This->resamp_buffer, wanted_frames * This->fmt->nBlockAlign);
1142 This->wri_offs_frames += wanted_frames;
1143 This->wri_offs_frames %= This->bufsize_frames;
1144 if(This->held_frames + wanted_frames > This->bufsize_frames){
1145 This->lcl_offs_frames += buf_ptr_diff(This->lcl_offs_frames,
1146 This->wri_offs_frames, This->bufsize_frames);
1147 This->held_frames = This->bufsize_frames;
1148 }else
1149 This->held_frames += wanted_frames;
1153 /* we need to trigger CA to pull data from the device and give it to us
1155 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
1157 * raw data is resampled from cap_buffer into resamp_buffer in period-size
1158 * chunks and copied to local_buffer
1160 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
1161 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1162 AudioBufferList *data)
1164 ACImpl *This = user;
1165 AudioBufferList list;
1166 OSStatus sc;
1167 UINT32 cap_wri_offs_frames;
1169 OSSpinLockLock(&This->lock);
1171 cap_wri_offs_frames = (This->cap_offs_frames + This->cap_held_frames) % This->cap_bufsize_frames;
1173 list.mNumberBuffers = 1;
1174 list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1175 list.mBuffers[0].mDataByteSize = nframes * This->fmt->nBlockAlign;
1177 if(!This->playing || cap_wri_offs_frames + nframes > This->cap_bufsize_frames){
1178 if(This->wrap_bufsize_frames < nframes){
1179 free(This->wrap_buffer);
1180 This->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
1181 This->wrap_bufsize_frames = nframes;
1184 list.mBuffers[0].mData = This->wrap_buffer;
1185 }else
1186 list.mBuffers[0].mData = This->cap_buffer + cap_wri_offs_frames * This->fmt->nBlockAlign;
1188 sc = AudioUnitRender(This->unit, flags, ts, bus, nframes, &list);
1189 if(sc != noErr){
1190 OSSpinLockUnlock(&This->lock);
1191 return sc;
1194 if(This->playing){
1195 if(list.mBuffers[0].mData == This->wrap_buffer){
1196 ca_wrap_buffer(This->cap_buffer,
1197 cap_wri_offs_frames * This->fmt->nBlockAlign,
1198 This->cap_bufsize_frames * This->fmt->nBlockAlign,
1199 This->wrap_buffer, list.mBuffers[0].mDataByteSize);
1202 This->cap_held_frames += list.mBuffers[0].mDataByteSize / This->fmt->nBlockAlign;
1203 if(This->cap_held_frames > This->cap_bufsize_frames){
1204 This->cap_offs_frames += This->cap_held_frames % This->cap_bufsize_frames;
1205 This->cap_offs_frames %= This->cap_bufsize_frames;
1206 This->cap_held_frames = This->cap_bufsize_frames;
1210 OSSpinLockUnlock(&This->lock);
1211 return noErr;
1214 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
1216 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
1217 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
1218 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
1219 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
1220 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
1221 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
1224 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
1225 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
1226 AudioConverterRef *converter)
1228 OSStatus sc;
1229 HRESULT hr;
1231 if(dataflow == eCapture){
1232 AudioStreamBasicDescription desc;
1233 UInt32 size;
1234 Float64 rate;
1235 fenv_t fenv;
1236 BOOL fenv_stored = TRUE;
1238 hr = ca_get_audiodesc(&desc, fmt);
1239 if(FAILED(hr))
1240 return hr;
1241 dump_adesc("requested", &desc);
1243 /* input-only units can't perform sample rate conversion, so we have to
1244 * set up our own AudioConverter to support arbitrary sample rates. */
1245 size = sizeof(*dev_desc);
1246 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
1247 kAudioUnitScope_Input, 1, dev_desc, &size);
1248 if(sc != noErr){
1249 WARN("Couldn't get unit format: %x\n", (int)sc);
1250 return osstatus_to_hresult(sc);
1252 dump_adesc("hardware", dev_desc);
1254 rate = dev_desc->mSampleRate;
1255 *dev_desc = desc;
1256 dev_desc->mSampleRate = rate;
1258 dump_adesc("final", dev_desc);
1259 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1260 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
1261 if(sc != noErr){
1262 WARN("Couldn't set unit format: %x\n", (int)sc);
1263 return osstatus_to_hresult(sc);
1266 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
1267 if(feholdexcept(&fenv)){
1268 WARN("Failed to store fenv state\n");
1269 fenv_stored = FALSE;
1272 sc = AudioConverterNew(dev_desc, &desc, converter);
1274 if(fenv_stored && fesetenv(&fenv))
1275 WARN("Failed to restore fenv state\n");
1277 if(sc != noErr){
1278 WARN("Couldn't create audio converter: %x\n", (int)sc);
1279 return osstatus_to_hresult(sc);
1281 }else{
1282 hr = ca_get_audiodesc(dev_desc, fmt);
1283 if(FAILED(hr))
1284 return hr;
1286 dump_adesc("final", dev_desc);
1287 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1288 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
1289 if(sc != noErr){
1290 WARN("Couldn't set format: %x\n", (int)sc);
1291 return osstatus_to_hresult(sc);
1295 return S_OK;
1298 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1299 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1300 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1301 const GUID *sessionguid)
1303 ACImpl *This = impl_from_IAudioClient(iface);
1304 HRESULT hr;
1305 OSStatus sc;
1306 int i;
1308 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1309 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1311 if(!fmt)
1312 return E_POINTER;
1314 dump_fmt(fmt);
1316 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1317 return E_INVALIDARG;
1319 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1320 AUDCLNT_STREAMFLAGS_LOOPBACK |
1321 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1322 AUDCLNT_STREAMFLAGS_NOPERSIST |
1323 AUDCLNT_STREAMFLAGS_RATEADJUST |
1324 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1325 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1326 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1327 TRACE("Unknown flags: %08x\n", flags);
1328 return E_INVALIDARG;
1331 if(mode == AUDCLNT_SHAREMODE_SHARED){
1332 period = DefaultPeriod;
1333 if( duration < 3 * period)
1334 duration = 3 * period;
1335 }else{
1336 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1337 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1338 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1339 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1342 if(!period)
1343 period = DefaultPeriod; /* not minimum */
1344 if(period < MinimumPeriod || period > 5000000)
1345 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1346 if(duration > 20000000) /* the smaller the period, the lower this limit */
1347 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1348 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1349 if(duration != period)
1350 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1351 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1352 return AUDCLNT_E_DEVICE_IN_USE;
1353 }else{
1354 if( duration < 8 * period)
1355 duration = 8 * period; /* may grow above 2s */
1359 OSSpinLockLock(&This->lock);
1361 if(This->initted){
1362 OSSpinLockUnlock(&This->lock);
1363 return AUDCLNT_E_ALREADY_INITIALIZED;
1366 This->fmt = clone_format(fmt);
1367 if(!This->fmt){
1368 OSSpinLockUnlock(&This->lock);
1369 return E_OUTOFMEMORY;
1372 This->period_ms = period / 10000;
1373 This->period_frames = MulDiv(period, This->fmt->nSamplesPerSec, 10000000);
1375 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1376 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1377 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1379 hr = ca_setup_audiounit(This->dataflow, This->unit, This->fmt, &This->dev_desc, &This->converter);
1380 if(FAILED(hr)){
1381 CoTaskMemFree(This->fmt);
1382 This->fmt = NULL;
1383 OSSpinLockUnlock(&This->lock);
1384 return hr;
1387 if(This->dataflow == eCapture){
1388 AURenderCallbackStruct input;
1390 memset(&input, 0, sizeof(input));
1391 input.inputProc = &ca_capture_cb;
1392 input.inputProcRefCon = This;
1394 sc = AudioUnitSetProperty(This->unit, kAudioOutputUnitProperty_SetInputCallback,
1395 kAudioUnitScope_Output, 1, &input, sizeof(input));
1396 if(sc != noErr){
1397 WARN("Couldn't set callback: %x\n", (int)sc);
1398 AudioConverterDispose(This->converter);
1399 This->converter = NULL;
1400 CoTaskMemFree(This->fmt);
1401 This->fmt = NULL;
1402 OSSpinLockUnlock(&This->lock);
1403 return osstatus_to_hresult(sc);
1405 }else{
1406 AURenderCallbackStruct input;
1408 memset(&input, 0, sizeof(input));
1409 input.inputProc = &ca_render_cb;
1410 input.inputProcRefCon = This;
1412 sc = AudioUnitSetProperty(This->unit, kAudioUnitProperty_SetRenderCallback,
1413 kAudioUnitScope_Input, 0, &input, sizeof(input));
1414 if(sc != noErr){
1415 WARN("Couldn't set callback: %x\n", (int)sc);
1416 CoTaskMemFree(This->fmt);
1417 This->fmt = NULL;
1418 OSSpinLockUnlock(&This->lock);
1419 return osstatus_to_hresult(sc);
1423 sc = AudioUnitInitialize(This->unit);
1424 if(sc != noErr){
1425 WARN("Couldn't initialize: %x\n", (int)sc);
1426 if(This->converter){
1427 AudioConverterDispose(This->converter);
1428 This->converter = NULL;
1430 CoTaskMemFree(This->fmt);
1431 This->fmt = NULL;
1432 OSSpinLockUnlock(&This->lock);
1433 return osstatus_to_hresult(sc);
1436 /* we play audio continuously because AudioOutputUnitStart sometimes takes
1437 * a while to return */
1438 sc = AudioOutputUnitStart(This->unit);
1439 if(sc != noErr){
1440 WARN("Unit failed to start: %x\n", (int)sc);
1441 if(This->converter){
1442 AudioConverterDispose(This->converter);
1443 This->converter = NULL;
1445 CoTaskMemFree(This->fmt);
1446 This->fmt = NULL;
1447 OSSpinLockUnlock(&This->lock);
1448 return osstatus_to_hresult(sc);
1451 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_frames * fmt->nBlockAlign);
1452 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1454 if(This->dataflow == eCapture){
1455 This->cap_bufsize_frames = MulDiv(duration, This->dev_desc.mSampleRate, 10000000);
1456 This->cap_buffer = HeapAlloc(GetProcessHeap(), 0, This->cap_bufsize_frames * This->fmt->nBlockAlign);
1459 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1460 if(!This->vols){
1461 CoTaskMemFree(This->fmt);
1462 This->fmt = NULL;
1463 OSSpinLockUnlock(&This->lock);
1464 return E_OUTOFMEMORY;
1467 for(i = 0; i < fmt->nChannels; ++i)
1468 This->vols[i] = 1.f;
1470 This->share = mode;
1471 This->flags = flags;
1473 EnterCriticalSection(&g_sessions_lock);
1475 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1476 &This->session);
1477 if(FAILED(hr)){
1478 LeaveCriticalSection(&g_sessions_lock);
1479 CoTaskMemFree(This->fmt);
1480 This->fmt = NULL;
1481 HeapFree(GetProcessHeap(), 0, This->vols);
1482 This->vols = NULL;
1483 OSSpinLockUnlock(&This->lock);
1484 return E_INVALIDARG;
1487 list_add_tail(&This->session->clients, &This->entry);
1489 LeaveCriticalSection(&g_sessions_lock);
1491 ca_setvol(This, -1);
1493 This->initted = TRUE;
1495 OSSpinLockUnlock(&This->lock);
1497 return S_OK;
1500 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1501 UINT32 *frames)
1503 ACImpl *This = impl_from_IAudioClient(iface);
1505 TRACE("(%p)->(%p)\n", This, frames);
1507 if(!frames)
1508 return E_POINTER;
1510 OSSpinLockLock(&This->lock);
1512 if(!This->initted){
1513 OSSpinLockUnlock(&This->lock);
1514 return AUDCLNT_E_NOT_INITIALIZED;
1517 *frames = This->bufsize_frames;
1519 OSSpinLockUnlock(&This->lock);
1521 return S_OK;
1524 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1526 AudioObjectPropertyAddress addr;
1527 AudioStreamID *ids;
1528 UInt32 size;
1529 OSStatus sc;
1530 int nstreams, i;
1532 addr.mScope = This->scope;
1533 addr.mElement = 0;
1534 addr.mSelector = kAudioDevicePropertyStreams;
1536 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1537 &size);
1538 if(sc != noErr){
1539 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1540 return osstatus_to_hresult(sc);
1543 ids = HeapAlloc(GetProcessHeap(), 0, size);
1544 if(!ids)
1545 return E_OUTOFMEMORY;
1547 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1548 if(sc != noErr){
1549 WARN("Unable to get _Streams property: %x\n", (int)sc);
1550 HeapFree(GetProcessHeap(), 0, ids);
1551 return osstatus_to_hresult(sc);
1554 nstreams = size / sizeof(AudioStreamID);
1555 *max = 0;
1557 addr.mSelector = kAudioStreamPropertyLatency;
1558 for(i = 0; i < nstreams; ++i){
1559 UInt32 latency;
1561 size = sizeof(latency);
1562 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1563 &size, &latency);
1564 if(sc != noErr){
1565 WARN("Unable to get _Latency property: %x\n", (int)sc);
1566 continue;
1569 if(latency > *max)
1570 *max = latency;
1573 HeapFree(GetProcessHeap(), 0, ids);
1575 return S_OK;
1578 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1579 REFERENCE_TIME *out)
1581 ACImpl *This = impl_from_IAudioClient(iface);
1582 UInt32 latency, stream_latency, size;
1583 AudioObjectPropertyAddress addr;
1584 OSStatus sc;
1585 HRESULT hr;
1587 TRACE("(%p)->(%p)\n", This, out);
1589 if(!out)
1590 return E_POINTER;
1592 OSSpinLockLock(&This->lock);
1594 if(!This->initted){
1595 OSSpinLockUnlock(&This->lock);
1596 return AUDCLNT_E_NOT_INITIALIZED;
1599 addr.mScope = This->scope;
1600 addr.mSelector = kAudioDevicePropertyLatency;
1601 addr.mElement = 0;
1603 size = sizeof(latency);
1604 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1605 &size, &latency);
1606 if(sc != noErr){
1607 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1608 OSSpinLockUnlock(&This->lock);
1609 return osstatus_to_hresult(sc);
1612 hr = ca_get_max_stream_latency(This, &stream_latency);
1613 if(FAILED(hr)){
1614 OSSpinLockUnlock(&This->lock);
1615 return hr;
1618 latency += stream_latency;
1619 /* pretend we process audio in Period chunks, so max latency includes
1620 * the period time */
1621 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1622 + This->period_ms * 10000;
1624 OSSpinLockUnlock(&This->lock);
1626 return S_OK;
1629 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1630 UINT32 *numpad)
1632 if(!This->initted)
1633 return AUDCLNT_E_NOT_INITIALIZED;
1635 if(This->dataflow == eCapture)
1636 capture_resample(This);
1638 *numpad = This->held_frames;
1640 return S_OK;
1643 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1644 UINT32 *numpad)
1646 ACImpl *This = impl_from_IAudioClient(iface);
1647 HRESULT hr;
1649 TRACE("(%p)->(%p)\n", This, numpad);
1651 if(!numpad)
1652 return E_POINTER;
1654 OSSpinLockLock(&This->lock);
1656 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1658 OSSpinLockUnlock(&This->lock);
1660 return hr;
1663 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1664 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1665 WAVEFORMATEX **outpwfx)
1667 ACImpl *This = impl_from_IAudioClient(iface);
1668 AudioStreamBasicDescription dev_desc;
1669 AudioConverterRef converter;
1670 AudioComponentInstance unit;
1671 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1672 HRESULT hr;
1674 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1676 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1677 return E_POINTER;
1679 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1680 return E_INVALIDARG;
1682 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1683 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1684 return E_INVALIDARG;
1686 dump_fmt(pwfx);
1688 if(outpwfx){
1689 *outpwfx = NULL;
1690 if(mode != AUDCLNT_SHAREMODE_SHARED)
1691 outpwfx = NULL;
1694 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1695 if(pwfx->nAvgBytesPerSec == 0 ||
1696 pwfx->nBlockAlign == 0 ||
1697 fmtex->Samples.wValidBitsPerSample > pwfx->wBitsPerSample)
1698 return E_INVALIDARG;
1699 if(fmtex->Samples.wValidBitsPerSample < pwfx->wBitsPerSample)
1700 goto unsupported;
1701 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1702 if(fmtex->dwChannelMask == 0 ||
1703 fmtex->dwChannelMask & SPEAKER_RESERVED)
1704 goto unsupported;
1708 if(pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample / 8 ||
1709 pwfx->nAvgBytesPerSec != pwfx->nBlockAlign * pwfx->nSamplesPerSec)
1710 goto unsupported;
1712 if(pwfx->nChannels == 0)
1713 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1715 unit = get_audiounit(This->dataflow, This->adevid);
1717 converter = NULL;
1718 hr = ca_setup_audiounit(This->dataflow, unit, pwfx, &dev_desc, &converter);
1719 AudioComponentInstanceDispose(unit);
1720 if(FAILED(hr))
1721 goto unsupported;
1723 if(converter)
1724 AudioConverterDispose(converter);
1726 return S_OK;
1728 unsupported:
1729 if(outpwfx){
1730 hr = IAudioClient_GetMixFormat(&This->IAudioClient_iface, outpwfx);
1731 if(FAILED(hr))
1732 return hr;
1733 return S_FALSE;
1736 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1739 static DWORD ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
1741 int i;
1742 DWORD mask = 0;
1744 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
1745 switch (layout->mChannelDescriptions[i].mChannelLabel) {
1746 default: FIXME("Unhandled channel 0x%x\n", layout->mChannelDescriptions[i].mChannelLabel); break;
1747 case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
1748 case kAudioChannelLabel_Mono:
1749 case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
1750 case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
1751 case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
1752 case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
1753 case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
1754 case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
1755 case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
1756 case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
1757 case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
1758 case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
1759 case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
1760 case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
1761 case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
1762 case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
1763 case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
1764 case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
1765 case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
1769 return mask;
1772 /* For most hardware on Windows, users must choose a configuration with an even
1773 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1774 * channels, but those channels are still reported to applications from
1775 * GetMixFormat! Some applications behave badly if given an odd number of
1776 * channels (e.g. 2.1). Here, we find the nearest configuration that Windows
1777 * would report for a given channel layout. */
1778 static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
1780 DWORD ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
1782 TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
1784 if (ca_layout->mNumberChannelDescriptions == 1)
1786 fmt->Format.nChannels = 1;
1787 fmt->dwChannelMask = ca_mask;
1788 return;
1791 /* compare against known configurations and find smallest configuration
1792 * which is a superset of the given speakers */
1794 if (ca_layout->mNumberChannelDescriptions <= 2 &&
1795 (ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
1797 fmt->Format.nChannels = 2;
1798 fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1799 return;
1802 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1803 (ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
1805 fmt->Format.nChannels = 4;
1806 fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
1807 return;
1810 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1811 (ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
1813 fmt->Format.nChannels = 4;
1814 fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
1815 return;
1818 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1819 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
1821 fmt->Format.nChannels = 6;
1822 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
1823 return;
1826 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1827 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
1829 fmt->Format.nChannels = 6;
1830 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
1831 return;
1834 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1835 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
1837 fmt->Format.nChannels = 8;
1838 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
1839 return;
1842 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1843 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
1845 fmt->Format.nChannels = 8;
1846 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
1847 return;
1850 /* oddball format, report truthfully */
1851 fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
1852 fmt->dwChannelMask = ca_mask;
1855 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1856 WAVEFORMATEX **pwfx)
1858 ACImpl *This = impl_from_IAudioClient(iface);
1859 WAVEFORMATEXTENSIBLE *fmt;
1860 OSStatus sc;
1861 UInt32 size;
1862 Float64 rate;
1863 AudioBufferList *buffers;
1864 AudioChannelLayout *layout;
1865 AudioObjectPropertyAddress addr;
1866 int i;
1868 TRACE("(%p)->(%p)\n", This, pwfx);
1870 if(!pwfx)
1871 return E_POINTER;
1872 *pwfx = NULL;
1874 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1875 if(!fmt)
1876 return E_OUTOFMEMORY;
1878 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1880 addr.mScope = This->scope;
1881 addr.mElement = 0;
1882 addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
1884 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1885 if(sc == noErr){
1886 layout = HeapAlloc(GetProcessHeap(), 0, size);
1888 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, layout);
1889 if(sc == noErr){
1890 TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
1891 layout->mChannelLayoutTag, layout->mChannelBitmap, layout->mNumberChannelDescriptions);
1893 if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
1894 convert_channel_layout(layout, fmt);
1895 }else{
1896 WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n", layout->mChannelLayoutTag);
1897 fmt->Format.nChannels = 0;
1899 }else{
1900 TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1901 fmt->Format.nChannels = 0;
1904 HeapFree(GetProcessHeap(), 0, layout);
1905 }else{
1906 TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1907 fmt->Format.nChannels = 0;
1910 if(fmt->Format.nChannels == 0){
1911 addr.mScope = This->scope;
1912 addr.mElement = 0;
1913 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1915 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1916 if(sc != noErr){
1917 CoTaskMemFree(fmt);
1918 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
1919 return osstatus_to_hresult(sc);
1922 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1923 if(!buffers){
1924 CoTaskMemFree(fmt);
1925 return E_OUTOFMEMORY;
1928 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1929 &size, buffers);
1930 if(sc != noErr){
1931 CoTaskMemFree(fmt);
1932 HeapFree(GetProcessHeap(), 0, buffers);
1933 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
1934 return osstatus_to_hresult(sc);
1937 fmt->Format.nChannels = 0;
1938 for(i = 0; i < buffers->mNumberBuffers; ++i)
1939 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1941 HeapFree(GetProcessHeap(), 0, buffers);
1943 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1946 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1947 size = sizeof(Float64);
1948 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1949 if(sc != noErr){
1950 CoTaskMemFree(fmt);
1951 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
1952 return osstatus_to_hresult(sc);
1954 fmt->Format.nSamplesPerSec = rate;
1956 fmt->Format.wBitsPerSample = 32;
1957 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1959 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1960 fmt->Format.nChannels) / 8;
1961 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1962 fmt->Format.nBlockAlign;
1964 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1965 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1967 *pwfx = (WAVEFORMATEX*)fmt;
1968 dump_fmt(*pwfx);
1970 return S_OK;
1973 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1974 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1976 ACImpl *This = impl_from_IAudioClient(iface);
1978 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1980 if(!defperiod && !minperiod)
1981 return E_POINTER;
1983 if(defperiod)
1984 *defperiod = DefaultPeriod;
1985 if(minperiod)
1986 *minperiod = MinimumPeriod;
1988 return S_OK;
1991 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1993 ACImpl *This = user;
1995 if(This->event)
1996 SetEvent(This->event);
1999 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2001 ACImpl *This = impl_from_IAudioClient(iface);
2003 TRACE("(%p)\n", This);
2005 OSSpinLockLock(&This->lock);
2007 if(!This->initted){
2008 OSSpinLockUnlock(&This->lock);
2009 return AUDCLNT_E_NOT_INITIALIZED;
2012 if(This->playing){
2013 OSSpinLockUnlock(&This->lock);
2014 return AUDCLNT_E_NOT_STOPPED;
2017 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2018 OSSpinLockUnlock(&This->lock);
2019 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2022 if(This->event && !This->timer)
2023 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
2024 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
2025 This->timer = NULL;
2026 OSSpinLockUnlock(&This->lock);
2027 WARN("Unable to create timer: %u\n", GetLastError());
2028 return E_OUTOFMEMORY;
2031 This->playing = TRUE;
2033 OSSpinLockUnlock(&This->lock);
2035 return S_OK;
2038 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2040 ACImpl *This = impl_from_IAudioClient(iface);
2042 TRACE("(%p)\n", This);
2044 OSSpinLockLock(&This->lock);
2046 if(!This->initted){
2047 OSSpinLockUnlock(&This->lock);
2048 return AUDCLNT_E_NOT_INITIALIZED;
2051 if(!This->playing){
2052 OSSpinLockUnlock(&This->lock);
2053 return S_FALSE;
2056 This->playing = FALSE;
2058 OSSpinLockUnlock(&This->lock);
2060 return S_OK;
2063 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2065 ACImpl *This = impl_from_IAudioClient(iface);
2067 TRACE("(%p)\n", This);
2069 OSSpinLockLock(&This->lock);
2071 if(!This->initted){
2072 OSSpinLockUnlock(&This->lock);
2073 return AUDCLNT_E_NOT_INITIALIZED;
2076 if(This->playing){
2077 OSSpinLockUnlock(&This->lock);
2078 return AUDCLNT_E_NOT_STOPPED;
2081 if(This->getbuf_last){
2082 OSSpinLockUnlock(&This->lock);
2083 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2086 if(This->dataflow == eRender){
2087 This->written_frames = 0;
2088 }else{
2089 This->written_frames += This->held_frames;
2092 This->held_frames = 0;
2093 This->lcl_offs_frames = 0;
2094 This->wri_offs_frames = 0;
2095 This->cap_offs_frames = 0;
2096 This->cap_held_frames = 0;
2098 OSSpinLockUnlock(&This->lock);
2100 return S_OK;
2103 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2104 HANDLE event)
2106 ACImpl *This = impl_from_IAudioClient(iface);
2108 TRACE("(%p)->(%p)\n", This, event);
2110 if(!event)
2111 return E_INVALIDARG;
2113 OSSpinLockLock(&This->lock);
2115 if(!This->initted){
2116 OSSpinLockUnlock(&This->lock);
2117 return AUDCLNT_E_NOT_INITIALIZED;
2120 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2121 OSSpinLockUnlock(&This->lock);
2122 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2125 if (This->event){
2126 OSSpinLockUnlock(&This->lock);
2127 FIXME("called twice\n");
2128 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2131 This->event = event;
2133 OSSpinLockUnlock(&This->lock);
2135 return S_OK;
2138 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2139 void **ppv)
2141 ACImpl *This = impl_from_IAudioClient(iface);
2143 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2145 if(!ppv)
2146 return E_POINTER;
2147 *ppv = NULL;
2149 OSSpinLockLock(&This->lock);
2151 if(!This->initted){
2152 OSSpinLockUnlock(&This->lock);
2153 return AUDCLNT_E_NOT_INITIALIZED;
2156 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2157 if(This->dataflow != eRender){
2158 OSSpinLockUnlock(&This->lock);
2159 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2161 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2162 *ppv = &This->IAudioRenderClient_iface;
2163 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2164 if(This->dataflow != eCapture){
2165 OSSpinLockUnlock(&This->lock);
2166 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2168 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2169 *ppv = &This->IAudioCaptureClient_iface;
2170 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2171 IAudioClock_AddRef(&This->IAudioClock_iface);
2172 *ppv = &This->IAudioClock_iface;
2173 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2174 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2175 *ppv = &This->IAudioStreamVolume_iface;
2176 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2177 if(!This->session_wrapper){
2178 This->session_wrapper = AudioSessionWrapper_Create(This);
2179 if(!This->session_wrapper){
2180 OSSpinLockUnlock(&This->lock);
2181 return E_OUTOFMEMORY;
2183 }else
2184 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2186 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2187 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2188 if(!This->session_wrapper){
2189 This->session_wrapper = AudioSessionWrapper_Create(This);
2190 if(!This->session_wrapper){
2191 OSSpinLockUnlock(&This->lock);
2192 return E_OUTOFMEMORY;
2194 }else
2195 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2197 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2198 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2199 if(!This->session_wrapper){
2200 This->session_wrapper = AudioSessionWrapper_Create(This);
2201 if(!This->session_wrapper){
2202 OSSpinLockUnlock(&This->lock);
2203 return E_OUTOFMEMORY;
2205 }else
2206 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2208 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2211 if(*ppv){
2212 OSSpinLockUnlock(&This->lock);
2213 return S_OK;
2216 OSSpinLockUnlock(&This->lock);
2218 FIXME("stub %s\n", debugstr_guid(riid));
2219 return E_NOINTERFACE;
2222 static const IAudioClientVtbl AudioClient_Vtbl =
2224 AudioClient_QueryInterface,
2225 AudioClient_AddRef,
2226 AudioClient_Release,
2227 AudioClient_Initialize,
2228 AudioClient_GetBufferSize,
2229 AudioClient_GetStreamLatency,
2230 AudioClient_GetCurrentPadding,
2231 AudioClient_IsFormatSupported,
2232 AudioClient_GetMixFormat,
2233 AudioClient_GetDevicePeriod,
2234 AudioClient_Start,
2235 AudioClient_Stop,
2236 AudioClient_Reset,
2237 AudioClient_SetEventHandle,
2238 AudioClient_GetService
2241 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2242 IAudioRenderClient *iface, REFIID riid, void **ppv)
2244 ACImpl *This = impl_from_IAudioRenderClient(iface);
2245 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2247 if(!ppv)
2248 return E_POINTER;
2249 *ppv = NULL;
2251 if(IsEqualIID(riid, &IID_IUnknown) ||
2252 IsEqualIID(riid, &IID_IAudioRenderClient))
2253 *ppv = iface;
2254 else if(IsEqualIID(riid, &IID_IMarshal))
2255 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2257 if(*ppv){
2258 IUnknown_AddRef((IUnknown*)*ppv);
2259 return S_OK;
2262 WARN("Unknown interface %s\n", debugstr_guid(riid));
2263 return E_NOINTERFACE;
2266 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2268 ACImpl *This = impl_from_IAudioRenderClient(iface);
2269 return AudioClient_AddRef(&This->IAudioClient_iface);
2272 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2274 ACImpl *This = impl_from_IAudioRenderClient(iface);
2275 return AudioClient_Release(&This->IAudioClient_iface);
2278 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2279 UINT32 frames, BYTE **data)
2281 ACImpl *This = impl_from_IAudioRenderClient(iface);
2282 UINT32 pad;
2283 HRESULT hr;
2285 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2287 if(!data)
2288 return E_POINTER;
2289 *data = NULL;
2291 OSSpinLockLock(&This->lock);
2293 if(This->getbuf_last){
2294 OSSpinLockUnlock(&This->lock);
2295 return AUDCLNT_E_OUT_OF_ORDER;
2298 if(!frames){
2299 OSSpinLockUnlock(&This->lock);
2300 return S_OK;
2303 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
2304 if(FAILED(hr)){
2305 OSSpinLockUnlock(&This->lock);
2306 return hr;
2309 if(pad + frames > This->bufsize_frames){
2310 OSSpinLockUnlock(&This->lock);
2311 return AUDCLNT_E_BUFFER_TOO_LARGE;
2314 if(This->wri_offs_frames + frames > This->bufsize_frames){
2315 if(This->tmp_buffer_frames < frames){
2316 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2317 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, frames * This->fmt->nBlockAlign);
2318 if(!This->tmp_buffer){
2319 OSSpinLockUnlock(&This->lock);
2320 return E_OUTOFMEMORY;
2322 This->tmp_buffer_frames = frames;
2324 *data = This->tmp_buffer;
2325 This->getbuf_last = -frames;
2326 }else{
2327 *data = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2328 This->getbuf_last = frames;
2331 silence_buffer(This, *data, frames);
2333 OSSpinLockUnlock(&This->lock);
2335 return S_OK;
2338 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2339 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
2341 ACImpl *This = impl_from_IAudioRenderClient(iface);
2342 BYTE *buffer;
2344 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
2346 OSSpinLockLock(&This->lock);
2348 if(!frames){
2349 This->getbuf_last = 0;
2350 OSSpinLockUnlock(&This->lock);
2351 return S_OK;
2354 if(!This->getbuf_last){
2355 OSSpinLockUnlock(&This->lock);
2356 return AUDCLNT_E_OUT_OF_ORDER;
2359 if(frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2360 OSSpinLockUnlock(&This->lock);
2361 return AUDCLNT_E_INVALID_SIZE;
2364 if(This->getbuf_last >= 0)
2365 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2366 else
2367 buffer = This->tmp_buffer;
2369 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2370 silence_buffer(This, buffer, frames);
2372 if(This->getbuf_last < 0)
2373 ca_wrap_buffer(This->local_buffer,
2374 This->wri_offs_frames * This->fmt->nBlockAlign,
2375 This->bufsize_frames * This->fmt->nBlockAlign,
2376 buffer, frames * This->fmt->nBlockAlign);
2379 This->wri_offs_frames += frames;
2380 This->wri_offs_frames %= This->bufsize_frames;
2381 This->held_frames += frames;
2382 This->written_frames += frames;
2383 This->getbuf_last = 0;
2385 OSSpinLockUnlock(&This->lock);
2387 return S_OK;
2390 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2391 AudioRenderClient_QueryInterface,
2392 AudioRenderClient_AddRef,
2393 AudioRenderClient_Release,
2394 AudioRenderClient_GetBuffer,
2395 AudioRenderClient_ReleaseBuffer
2398 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2399 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2401 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2402 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2404 if(!ppv)
2405 return E_POINTER;
2406 *ppv = NULL;
2408 if(IsEqualIID(riid, &IID_IUnknown) ||
2409 IsEqualIID(riid, &IID_IAudioCaptureClient))
2410 *ppv = iface;
2411 else if(IsEqualIID(riid, &IID_IMarshal))
2412 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2414 if(*ppv){
2415 IUnknown_AddRef((IUnknown*)*ppv);
2416 return S_OK;
2419 WARN("Unknown interface %s\n", debugstr_guid(riid));
2420 return E_NOINTERFACE;
2423 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2425 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2426 return IAudioClient_AddRef(&This->IAudioClient_iface);
2429 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2431 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2432 return IAudioClient_Release(&This->IAudioClient_iface);
2435 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2436 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2437 UINT64 *qpcpos)
2439 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2440 UINT32 chunk_bytes, chunk_frames;
2442 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2443 devpos, qpcpos);
2445 if(!data)
2446 return E_POINTER;
2448 *data = NULL;
2450 if(!frames || !flags)
2451 return E_POINTER;
2453 OSSpinLockLock(&This->lock);
2455 if(This->getbuf_last){
2456 OSSpinLockUnlock(&This->lock);
2457 return AUDCLNT_E_OUT_OF_ORDER;
2460 capture_resample(This);
2462 if(This->held_frames < This->period_frames){
2463 *frames = 0;
2464 OSSpinLockUnlock(&This->lock);
2465 return AUDCLNT_S_BUFFER_EMPTY;
2468 *flags = 0;
2470 chunk_frames = This->bufsize_frames - This->lcl_offs_frames;
2471 if(chunk_frames < This->period_frames){
2472 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2473 if(!This->tmp_buffer)
2474 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->period_frames * This->fmt->nBlockAlign);
2475 *data = This->tmp_buffer;
2476 memcpy(*data, This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign, chunk_bytes);
2477 memcpy((*data) + chunk_bytes, This->local_buffer, This->period_frames * This->fmt->nBlockAlign - chunk_bytes);
2478 }else
2479 *data = This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
2481 This->getbuf_last = *frames = This->period_frames;
2483 if(devpos)
2484 *devpos = This->written_frames;
2485 if(qpcpos){ /* fixme: qpc of recording time */
2486 LARGE_INTEGER stamp, freq;
2487 QueryPerformanceCounter(&stamp);
2488 QueryPerformanceFrequency(&freq);
2489 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2492 OSSpinLockUnlock(&This->lock);
2494 return S_OK;
2497 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2498 IAudioCaptureClient *iface, UINT32 done)
2500 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2502 TRACE("(%p)->(%u)\n", This, done);
2504 OSSpinLockLock(&This->lock);
2506 if(!done){
2507 This->getbuf_last = 0;
2508 OSSpinLockUnlock(&This->lock);
2509 return S_OK;
2512 if(!This->getbuf_last){
2513 OSSpinLockUnlock(&This->lock);
2514 return AUDCLNT_E_OUT_OF_ORDER;
2517 if(This->getbuf_last != done){
2518 OSSpinLockUnlock(&This->lock);
2519 return AUDCLNT_E_INVALID_SIZE;
2522 This->written_frames += done;
2523 This->held_frames -= done;
2524 This->lcl_offs_frames += done;
2525 This->lcl_offs_frames %= This->bufsize_frames;
2526 This->getbuf_last = 0;
2528 OSSpinLockUnlock(&This->lock);
2530 return S_OK;
2533 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2534 IAudioCaptureClient *iface, UINT32 *frames)
2536 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2538 TRACE("(%p)->(%p)\n", This, frames);
2540 if(!frames)
2541 return E_POINTER;
2543 OSSpinLockLock(&This->lock);
2545 capture_resample(This);
2547 if(This->held_frames >= This->period_frames)
2548 *frames = This->period_frames;
2549 else
2550 *frames = 0;
2552 OSSpinLockUnlock(&This->lock);
2554 return S_OK;
2557 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2559 AudioCaptureClient_QueryInterface,
2560 AudioCaptureClient_AddRef,
2561 AudioCaptureClient_Release,
2562 AudioCaptureClient_GetBuffer,
2563 AudioCaptureClient_ReleaseBuffer,
2564 AudioCaptureClient_GetNextPacketSize
2567 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2568 REFIID riid, void **ppv)
2570 ACImpl *This = impl_from_IAudioClock(iface);
2572 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2574 if(!ppv)
2575 return E_POINTER;
2576 *ppv = NULL;
2578 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2579 *ppv = iface;
2580 else if(IsEqualIID(riid, &IID_IAudioClock2))
2581 *ppv = &This->IAudioClock2_iface;
2582 if(*ppv){
2583 IUnknown_AddRef((IUnknown*)*ppv);
2584 return S_OK;
2587 WARN("Unknown interface %s\n", debugstr_guid(riid));
2588 return E_NOINTERFACE;
2591 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2593 ACImpl *This = impl_from_IAudioClock(iface);
2594 return IAudioClient_AddRef(&This->IAudioClient_iface);
2597 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2599 ACImpl *This = impl_from_IAudioClock(iface);
2600 return IAudioClient_Release(&This->IAudioClient_iface);
2603 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2605 ACImpl *This = impl_from_IAudioClock(iface);
2607 TRACE("(%p)->(%p)\n", This, freq);
2609 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2610 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2611 else
2612 *freq = This->fmt->nSamplesPerSec;
2614 return S_OK;
2617 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2618 UINT64 *pos, UINT64 *qpctime)
2620 *pos = This->written_frames - This->held_frames;
2622 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2623 *pos *= This->fmt->nBlockAlign;
2625 if(qpctime){
2626 LARGE_INTEGER stamp, freq;
2627 QueryPerformanceCounter(&stamp);
2628 QueryPerformanceFrequency(&freq);
2629 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2632 return S_OK;
2635 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2636 UINT64 *qpctime)
2638 ACImpl *This = impl_from_IAudioClock(iface);
2639 HRESULT hr;
2641 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2643 if(!pos)
2644 return E_POINTER;
2646 OSSpinLockLock(&This->lock);
2648 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2650 OSSpinLockUnlock(&This->lock);
2652 return hr;
2655 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2656 DWORD *chars)
2658 ACImpl *This = impl_from_IAudioClock(iface);
2660 TRACE("(%p)->(%p)\n", This, chars);
2662 if(!chars)
2663 return E_POINTER;
2665 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2667 return S_OK;
2670 static const IAudioClockVtbl AudioClock_Vtbl =
2672 AudioClock_QueryInterface,
2673 AudioClock_AddRef,
2674 AudioClock_Release,
2675 AudioClock_GetFrequency,
2676 AudioClock_GetPosition,
2677 AudioClock_GetCharacteristics
2680 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2681 REFIID riid, void **ppv)
2683 ACImpl *This = impl_from_IAudioClock2(iface);
2684 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2687 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2689 ACImpl *This = impl_from_IAudioClock2(iface);
2690 return IAudioClient_AddRef(&This->IAudioClient_iface);
2693 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2695 ACImpl *This = impl_from_IAudioClock2(iface);
2696 return IAudioClient_Release(&This->IAudioClient_iface);
2699 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2700 UINT64 *pos, UINT64 *qpctime)
2702 ACImpl *This = impl_from_IAudioClock2(iface);
2704 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2706 return E_NOTIMPL;
2709 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2711 AudioClock2_QueryInterface,
2712 AudioClock2_AddRef,
2713 AudioClock2_Release,
2714 AudioClock2_GetDevicePosition
2717 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2719 AudioSessionWrapper *ret;
2721 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2722 sizeof(AudioSessionWrapper));
2723 if(!ret)
2724 return NULL;
2726 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2727 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2728 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2730 ret->ref = 1;
2732 ret->client = client;
2733 if(client){
2734 ret->session = client->session;
2735 AudioClient_AddRef(&client->IAudioClient_iface);
2738 return ret;
2741 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2742 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2744 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2746 if(!ppv)
2747 return E_POINTER;
2748 *ppv = NULL;
2750 if(IsEqualIID(riid, &IID_IUnknown) ||
2751 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2752 IsEqualIID(riid, &IID_IAudioSessionControl2))
2753 *ppv = iface;
2754 if(*ppv){
2755 IUnknown_AddRef((IUnknown*)*ppv);
2756 return S_OK;
2759 WARN("Unknown interface %s\n", debugstr_guid(riid));
2760 return E_NOINTERFACE;
2763 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2765 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2766 ULONG ref;
2767 ref = InterlockedIncrement(&This->ref);
2768 TRACE("(%p) Refcount now %u\n", This, ref);
2769 return ref;
2772 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2774 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2775 ULONG ref;
2776 ref = InterlockedDecrement(&This->ref);
2777 TRACE("(%p) Refcount now %u\n", This, ref);
2778 if(!ref){
2779 if(This->client){
2780 OSSpinLockLock(&This->client->lock);
2781 This->client->session_wrapper = NULL;
2782 OSSpinLockUnlock(&This->client->lock);
2783 AudioClient_Release(&This->client->IAudioClient_iface);
2785 HeapFree(GetProcessHeap(), 0, This);
2787 return ref;
2790 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2791 AudioSessionState *state)
2793 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2794 ACImpl *client;
2796 TRACE("(%p)->(%p)\n", This, state);
2798 if(!state)
2799 return NULL_PTR_ERR;
2801 EnterCriticalSection(&g_sessions_lock);
2803 if(list_empty(&This->session->clients)){
2804 *state = AudioSessionStateExpired;
2805 LeaveCriticalSection(&g_sessions_lock);
2806 return S_OK;
2809 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2810 OSSpinLockLock(&client->lock);
2811 if(client->playing){
2812 *state = AudioSessionStateActive;
2813 OSSpinLockUnlock(&client->lock);
2814 LeaveCriticalSection(&g_sessions_lock);
2815 return S_OK;
2817 OSSpinLockUnlock(&client->lock);
2820 LeaveCriticalSection(&g_sessions_lock);
2822 *state = AudioSessionStateInactive;
2824 return S_OK;
2827 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2828 IAudioSessionControl2 *iface, WCHAR **name)
2830 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2832 FIXME("(%p)->(%p) - stub\n", This, name);
2834 return E_NOTIMPL;
2837 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2838 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2840 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2842 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2844 return E_NOTIMPL;
2847 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2848 IAudioSessionControl2 *iface, WCHAR **path)
2850 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2852 FIXME("(%p)->(%p) - stub\n", This, path);
2854 return E_NOTIMPL;
2857 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2858 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2860 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2862 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2864 return E_NOTIMPL;
2867 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2868 IAudioSessionControl2 *iface, GUID *group)
2870 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2872 FIXME("(%p)->(%p) - stub\n", This, group);
2874 return E_NOTIMPL;
2877 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2878 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2880 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2882 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2883 debugstr_guid(session));
2885 return E_NOTIMPL;
2888 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2889 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2891 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2893 FIXME("(%p)->(%p) - stub\n", This, events);
2895 return S_OK;
2898 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2899 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2901 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2903 FIXME("(%p)->(%p) - stub\n", This, events);
2905 return S_OK;
2908 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2909 IAudioSessionControl2 *iface, WCHAR **id)
2911 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2913 FIXME("(%p)->(%p) - stub\n", This, id);
2915 return E_NOTIMPL;
2918 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2919 IAudioSessionControl2 *iface, WCHAR **id)
2921 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2923 FIXME("(%p)->(%p) - stub\n", This, id);
2925 return E_NOTIMPL;
2928 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2929 IAudioSessionControl2 *iface, DWORD *pid)
2931 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2933 TRACE("(%p)->(%p)\n", This, pid);
2935 if(!pid)
2936 return E_POINTER;
2938 *pid = GetCurrentProcessId();
2940 return S_OK;
2943 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2944 IAudioSessionControl2 *iface)
2946 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2948 TRACE("(%p)\n", This);
2950 return S_FALSE;
2953 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2954 IAudioSessionControl2 *iface, BOOL optout)
2956 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2958 TRACE("(%p)->(%d)\n", This, optout);
2960 return S_OK;
2963 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2965 AudioSessionControl_QueryInterface,
2966 AudioSessionControl_AddRef,
2967 AudioSessionControl_Release,
2968 AudioSessionControl_GetState,
2969 AudioSessionControl_GetDisplayName,
2970 AudioSessionControl_SetDisplayName,
2971 AudioSessionControl_GetIconPath,
2972 AudioSessionControl_SetIconPath,
2973 AudioSessionControl_GetGroupingParam,
2974 AudioSessionControl_SetGroupingParam,
2975 AudioSessionControl_RegisterAudioSessionNotification,
2976 AudioSessionControl_UnregisterAudioSessionNotification,
2977 AudioSessionControl_GetSessionIdentifier,
2978 AudioSessionControl_GetSessionInstanceIdentifier,
2979 AudioSessionControl_GetProcessId,
2980 AudioSessionControl_IsSystemSoundsSession,
2981 AudioSessionControl_SetDuckingPreference
2984 /* index == -1 means set all channels, otherwise sets only the given channel */
2985 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2987 Float32 level;
2988 OSStatus sc;
2990 if(This->session->mute)
2991 level = 0.;
2992 else{
2993 if(index == (UINT32)-1){
2994 UINT32 i;
2995 level = 1.;
2996 for(i = 0; i < This->fmt->nChannels; ++i){
2997 Float32 tmp;
2998 tmp = This->session->master_vol *
2999 This->session->channel_vols[i] * This->vols[i];
3000 level = tmp < level ? tmp : level;
3002 }else
3003 level = This->session->master_vol *
3004 This->session->channel_vols[index] * This->vols[index];
3007 sc = AudioUnitSetParameter(This->unit, kHALOutputParam_Volume,
3008 kAudioUnitScope_Global, 0, level, 0);
3009 if(sc != noErr)
3010 WARN("Couldn't set volume: %x\n", (int)sc);
3012 return S_OK;
3015 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
3017 HRESULT ret = S_OK;
3018 ACImpl *client;
3020 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
3021 HRESULT hr;
3022 hr = ca_setvol(client, index);
3023 if(FAILED(hr))
3024 ret = hr;
3027 return ret;
3030 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3031 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3033 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3035 if(!ppv)
3036 return E_POINTER;
3037 *ppv = NULL;
3039 if(IsEqualIID(riid, &IID_IUnknown) ||
3040 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3041 *ppv = iface;
3042 if(*ppv){
3043 IUnknown_AddRef((IUnknown*)*ppv);
3044 return S_OK;
3047 WARN("Unknown interface %s\n", debugstr_guid(riid));
3048 return E_NOINTERFACE;
3051 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3053 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3054 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3057 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3059 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3060 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3063 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3064 ISimpleAudioVolume *iface, float level, const GUID *context)
3066 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3067 AudioSession *session = This->session;
3068 HRESULT ret;
3070 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3072 if(level < 0.f || level > 1.f)
3073 return E_INVALIDARG;
3075 if(context)
3076 FIXME("Notifications not supported yet\n");
3078 EnterCriticalSection(&session->lock);
3080 session->master_vol = level;
3082 ret = ca_session_setvol(session, -1);
3084 LeaveCriticalSection(&session->lock);
3086 return ret;
3089 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3090 ISimpleAudioVolume *iface, float *level)
3092 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3093 AudioSession *session = This->session;
3095 TRACE("(%p)->(%p)\n", session, level);
3097 if(!level)
3098 return NULL_PTR_ERR;
3100 *level = session->master_vol;
3102 return S_OK;
3105 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3106 BOOL mute, const GUID *context)
3108 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3109 AudioSession *session = This->session;
3111 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3113 if(context)
3114 FIXME("Notifications not supported yet\n");
3116 EnterCriticalSection(&session->lock);
3118 session->mute = mute;
3120 ca_session_setvol(session, -1);
3122 LeaveCriticalSection(&session->lock);
3124 return S_OK;
3127 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3128 BOOL *mute)
3130 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3131 AudioSession *session = This->session;
3133 TRACE("(%p)->(%p)\n", session, mute);
3135 if(!mute)
3136 return NULL_PTR_ERR;
3138 *mute = session->mute;
3140 return S_OK;
3143 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3145 SimpleAudioVolume_QueryInterface,
3146 SimpleAudioVolume_AddRef,
3147 SimpleAudioVolume_Release,
3148 SimpleAudioVolume_SetMasterVolume,
3149 SimpleAudioVolume_GetMasterVolume,
3150 SimpleAudioVolume_SetMute,
3151 SimpleAudioVolume_GetMute
3154 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3155 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3157 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3159 if(!ppv)
3160 return E_POINTER;
3161 *ppv = NULL;
3163 if(IsEqualIID(riid, &IID_IUnknown) ||
3164 IsEqualIID(riid, &IID_IAudioStreamVolume))
3165 *ppv = iface;
3166 if(*ppv){
3167 IUnknown_AddRef((IUnknown*)*ppv);
3168 return S_OK;
3171 WARN("Unknown interface %s\n", debugstr_guid(riid));
3172 return E_NOINTERFACE;
3175 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3177 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3178 return IAudioClient_AddRef(&This->IAudioClient_iface);
3181 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3183 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3184 return IAudioClient_Release(&This->IAudioClient_iface);
3187 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3188 IAudioStreamVolume *iface, UINT32 *out)
3190 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3192 TRACE("(%p)->(%p)\n", This, out);
3194 if(!out)
3195 return E_POINTER;
3197 *out = This->fmt->nChannels;
3199 return S_OK;
3202 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3203 IAudioStreamVolume *iface, UINT32 index, float level)
3205 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3206 HRESULT ret;
3208 TRACE("(%p)->(%d, %f)\n", This, index, level);
3210 if(level < 0.f || level > 1.f)
3211 return E_INVALIDARG;
3213 if(index >= This->fmt->nChannels)
3214 return E_INVALIDARG;
3216 OSSpinLockLock(&This->lock);
3218 This->vols[index] = level;
3220 WARN("CoreAudio doesn't support per-channel volume control\n");
3221 ret = ca_setvol(This, index);
3223 OSSpinLockUnlock(&This->lock);
3225 return ret;
3228 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3229 IAudioStreamVolume *iface, UINT32 index, float *level)
3231 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3233 TRACE("(%p)->(%d, %p)\n", This, index, level);
3235 if(!level)
3236 return E_POINTER;
3238 if(index >= This->fmt->nChannels)
3239 return E_INVALIDARG;
3241 *level = This->vols[index];
3243 return S_OK;
3246 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3247 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3249 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3250 int i;
3251 HRESULT ret;
3253 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3255 if(!levels)
3256 return E_POINTER;
3258 if(count != This->fmt->nChannels)
3259 return E_INVALIDARG;
3261 OSSpinLockLock(&This->lock);
3263 for(i = 0; i < count; ++i)
3264 This->vols[i] = levels[i];
3266 ret = ca_setvol(This, -1);
3268 OSSpinLockUnlock(&This->lock);
3270 return ret;
3273 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3274 IAudioStreamVolume *iface, UINT32 count, float *levels)
3276 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3277 int i;
3279 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3281 if(!levels)
3282 return E_POINTER;
3284 if(count != This->fmt->nChannels)
3285 return E_INVALIDARG;
3287 OSSpinLockLock(&This->lock);
3289 for(i = 0; i < count; ++i)
3290 levels[i] = This->vols[i];
3292 OSSpinLockUnlock(&This->lock);
3294 return S_OK;
3297 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3299 AudioStreamVolume_QueryInterface,
3300 AudioStreamVolume_AddRef,
3301 AudioStreamVolume_Release,
3302 AudioStreamVolume_GetChannelCount,
3303 AudioStreamVolume_SetChannelVolume,
3304 AudioStreamVolume_GetChannelVolume,
3305 AudioStreamVolume_SetAllVolumes,
3306 AudioStreamVolume_GetAllVolumes
3309 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3310 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3312 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3314 if(!ppv)
3315 return E_POINTER;
3316 *ppv = NULL;
3318 if(IsEqualIID(riid, &IID_IUnknown) ||
3319 IsEqualIID(riid, &IID_IChannelAudioVolume))
3320 *ppv = iface;
3321 if(*ppv){
3322 IUnknown_AddRef((IUnknown*)*ppv);
3323 return S_OK;
3326 WARN("Unknown interface %s\n", debugstr_guid(riid));
3327 return E_NOINTERFACE;
3330 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3332 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3333 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3336 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3338 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3339 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3342 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3343 IChannelAudioVolume *iface, UINT32 *out)
3345 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3346 AudioSession *session = This->session;
3348 TRACE("(%p)->(%p)\n", session, out);
3350 if(!out)
3351 return NULL_PTR_ERR;
3353 *out = session->channel_count;
3355 return S_OK;
3358 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3359 IChannelAudioVolume *iface, UINT32 index, float level,
3360 const GUID *context)
3362 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3363 AudioSession *session = This->session;
3364 HRESULT ret;
3366 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3367 wine_dbgstr_guid(context));
3369 if(level < 0.f || level > 1.f)
3370 return E_INVALIDARG;
3372 if(index >= session->channel_count)
3373 return E_INVALIDARG;
3375 if(context)
3376 FIXME("Notifications not supported yet\n");
3378 EnterCriticalSection(&session->lock);
3380 session->channel_vols[index] = level;
3382 WARN("CoreAudio doesn't support per-channel volume control\n");
3383 ret = ca_session_setvol(session, index);
3385 LeaveCriticalSection(&session->lock);
3387 return ret;
3390 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3391 IChannelAudioVolume *iface, UINT32 index, float *level)
3393 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3394 AudioSession *session = This->session;
3396 TRACE("(%p)->(%d, %p)\n", session, index, level);
3398 if(!level)
3399 return NULL_PTR_ERR;
3401 if(index >= session->channel_count)
3402 return E_INVALIDARG;
3404 *level = session->channel_vols[index];
3406 return S_OK;
3409 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3410 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3411 const GUID *context)
3413 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3414 AudioSession *session = This->session;
3415 int i;
3416 HRESULT ret;
3418 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3419 wine_dbgstr_guid(context));
3421 if(!levels)
3422 return NULL_PTR_ERR;
3424 if(count != session->channel_count)
3425 return E_INVALIDARG;
3427 if(context)
3428 FIXME("Notifications not supported yet\n");
3430 EnterCriticalSection(&session->lock);
3432 for(i = 0; i < count; ++i)
3433 session->channel_vols[i] = levels[i];
3435 ret = ca_session_setvol(session, -1);
3437 LeaveCriticalSection(&session->lock);
3439 return ret;
3442 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3443 IChannelAudioVolume *iface, UINT32 count, float *levels)
3445 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3446 AudioSession *session = This->session;
3447 int i;
3449 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3451 if(!levels)
3452 return NULL_PTR_ERR;
3454 if(count != session->channel_count)
3455 return E_INVALIDARG;
3457 for(i = 0; i < count; ++i)
3458 levels[i] = session->channel_vols[i];
3460 return S_OK;
3463 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3465 ChannelAudioVolume_QueryInterface,
3466 ChannelAudioVolume_AddRef,
3467 ChannelAudioVolume_Release,
3468 ChannelAudioVolume_GetChannelCount,
3469 ChannelAudioVolume_SetChannelVolume,
3470 ChannelAudioVolume_GetChannelVolume,
3471 ChannelAudioVolume_SetAllVolumes,
3472 ChannelAudioVolume_GetAllVolumes
3475 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3476 REFIID riid, void **ppv)
3478 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3480 if(!ppv)
3481 return E_POINTER;
3482 *ppv = NULL;
3484 if(IsEqualIID(riid, &IID_IUnknown) ||
3485 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3486 IsEqualIID(riid, &IID_IAudioSessionManager2))
3487 *ppv = iface;
3488 if(*ppv){
3489 IUnknown_AddRef((IUnknown*)*ppv);
3490 return S_OK;
3493 WARN("Unknown interface %s\n", debugstr_guid(riid));
3494 return E_NOINTERFACE;
3497 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3499 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3500 ULONG ref;
3501 ref = InterlockedIncrement(&This->ref);
3502 TRACE("(%p) Refcount now %u\n", This, ref);
3503 return ref;
3506 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3508 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3509 ULONG ref;
3510 ref = InterlockedDecrement(&This->ref);
3511 TRACE("(%p) Refcount now %u\n", This, ref);
3512 if(!ref)
3513 HeapFree(GetProcessHeap(), 0, This);
3514 return ref;
3517 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3518 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3519 IAudioSessionControl **out)
3521 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3522 AudioSession *session;
3523 AudioSessionWrapper *wrapper;
3524 HRESULT hr;
3526 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3527 flags, out);
3529 hr = get_audio_session(session_guid, This->device, 0, &session);
3530 if(FAILED(hr))
3531 return hr;
3533 wrapper = AudioSessionWrapper_Create(NULL);
3534 if(!wrapper)
3535 return E_OUTOFMEMORY;
3537 wrapper->session = session;
3539 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3541 return S_OK;
3544 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3545 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3546 ISimpleAudioVolume **out)
3548 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3549 AudioSession *session;
3550 AudioSessionWrapper *wrapper;
3551 HRESULT hr;
3553 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3554 flags, out);
3556 hr = get_audio_session(session_guid, This->device, 0, &session);
3557 if(FAILED(hr))
3558 return hr;
3560 wrapper = AudioSessionWrapper_Create(NULL);
3561 if(!wrapper)
3562 return E_OUTOFMEMORY;
3564 wrapper->session = session;
3566 *out = &wrapper->ISimpleAudioVolume_iface;
3568 return S_OK;
3571 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3572 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3574 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3575 FIXME("(%p)->(%p) - stub\n", This, out);
3576 return E_NOTIMPL;
3579 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3580 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3582 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3583 FIXME("(%p)->(%p) - stub\n", This, notification);
3584 return E_NOTIMPL;
3587 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3588 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3590 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3591 FIXME("(%p)->(%p) - stub\n", This, notification);
3592 return E_NOTIMPL;
3595 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3596 IAudioSessionManager2 *iface, const WCHAR *session_id,
3597 IAudioVolumeDuckNotification *notification)
3599 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3600 FIXME("(%p)->(%p) - stub\n", This, notification);
3601 return E_NOTIMPL;
3604 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3605 IAudioSessionManager2 *iface,
3606 IAudioVolumeDuckNotification *notification)
3608 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3609 FIXME("(%p)->(%p) - stub\n", This, notification);
3610 return E_NOTIMPL;
3613 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3615 AudioSessionManager_QueryInterface,
3616 AudioSessionManager_AddRef,
3617 AudioSessionManager_Release,
3618 AudioSessionManager_GetAudioSessionControl,
3619 AudioSessionManager_GetSimpleAudioVolume,
3620 AudioSessionManager_GetSessionEnumerator,
3621 AudioSessionManager_RegisterSessionNotification,
3622 AudioSessionManager_UnregisterSessionNotification,
3623 AudioSessionManager_RegisterDuckNotification,
3624 AudioSessionManager_UnregisterDuckNotification
3627 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3628 IAudioSessionManager2 **out)
3630 SessionMgr *This;
3632 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3633 if(!This)
3634 return E_OUTOFMEMORY;
3636 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3637 This->device = device;
3638 This->ref = 1;
3640 *out = &This->IAudioSessionManager2_iface;
3642 return S_OK;