mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobbc153d92511c3fc97cea881a565e31cdebfb5415
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 IAudioClient3 IAudioClient3_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 IAudioClient3Vtbl AudioClient3_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_IAudioClient3(IAudioClient3 *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_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->IAudioClient3_iface.lpVtbl = &AudioClient3_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->IAudioClient3_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 = (IAudioClient *)&This->IAudioClient3_iface;
714 IAudioClient3_AddRef(&This->IAudioClient3_iface);
716 return S_OK;
719 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
720 REFIID riid, void **ppv)
722 ACImpl *This = impl_from_IAudioClient3(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) ||
729 IsEqualIID(riid, &IID_IAudioClient) ||
730 IsEqualIID(riid, &IID_IAudioClient2) ||
731 IsEqualIID(riid, &IID_IAudioClient3))
732 *ppv = iface;
733 else if(IsEqualIID(riid, &IID_IMarshal))
734 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
736 if(*ppv){
737 IUnknown_AddRef((IUnknown*)*ppv);
738 return S_OK;
740 WARN("Unknown interface %s\n", debugstr_guid(riid));
741 return E_NOINTERFACE;
744 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
746 ACImpl *This = impl_from_IAudioClient3(iface);
747 ULONG ref;
748 ref = InterlockedIncrement(&This->ref);
749 TRACE("(%p) Refcount now %u\n", This, ref);
750 return ref;
753 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
755 ACImpl *This = impl_from_IAudioClient3(iface);
756 ULONG ref;
757 ref = InterlockedDecrement(&This->ref);
758 TRACE("(%p) Refcount now %u\n", This, ref);
759 if(!ref){
760 if(This->timer){
761 HANDLE event;
762 BOOL wait;
763 event = CreateEventW(NULL, TRUE, FALSE, NULL);
764 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
765 wait = wait && GetLastError() == ERROR_IO_PENDING;
766 if(event && wait)
767 WaitForSingleObject(event, INFINITE);
768 CloseHandle(event);
770 AudioOutputUnitStop(This->unit);
771 AudioComponentInstanceDispose(This->unit);
772 if(This->converter)
773 AudioConverterDispose(This->converter);
774 if(This->session){
775 EnterCriticalSection(&g_sessions_lock);
776 list_remove(&This->entry);
777 LeaveCriticalSection(&g_sessions_lock);
779 HeapFree(GetProcessHeap(), 0, This->vols);
780 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
781 HeapFree(GetProcessHeap(), 0, This->cap_buffer);
782 HeapFree(GetProcessHeap(), 0, This->local_buffer);
783 free(This->wrap_buffer);
784 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
785 CoTaskMemFree(This->fmt);
786 IMMDevice_Release(This->parent);
787 IUnknown_Release(This->pUnkFTMarshal);
788 HeapFree(GetProcessHeap(), 0, This);
790 return ref;
793 static void dump_fmt(const WAVEFORMATEX *fmt)
795 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
796 switch(fmt->wFormatTag){
797 case WAVE_FORMAT_PCM:
798 TRACE("WAVE_FORMAT_PCM");
799 break;
800 case WAVE_FORMAT_IEEE_FLOAT:
801 TRACE("WAVE_FORMAT_IEEE_FLOAT");
802 break;
803 case WAVE_FORMAT_EXTENSIBLE:
804 TRACE("WAVE_FORMAT_EXTENSIBLE");
805 break;
806 default:
807 TRACE("Unknown");
808 break;
810 TRACE(")\n");
812 TRACE("nChannels: %u\n", fmt->nChannels);
813 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
814 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
815 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
816 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
817 TRACE("cbSize: %u\n", fmt->cbSize);
819 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
820 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
821 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
822 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
823 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
827 static DWORD get_channel_mask(unsigned int channels)
829 switch(channels){
830 case 0:
831 return 0;
832 case 1:
833 return KSAUDIO_SPEAKER_MONO;
834 case 2:
835 return KSAUDIO_SPEAKER_STEREO;
836 case 3:
837 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
838 case 4:
839 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
840 case 5:
841 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
842 case 6:
843 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
844 case 7:
845 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
846 case 8:
847 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
849 FIXME("Unknown speaker configuration: %u\n", channels);
850 return 0;
853 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
855 WAVEFORMATEX *ret;
856 size_t size;
858 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
859 size = sizeof(WAVEFORMATEXTENSIBLE);
860 else
861 size = sizeof(WAVEFORMATEX);
863 ret = CoTaskMemAlloc(size);
864 if(!ret)
865 return NULL;
867 memcpy(ret, fmt, size);
869 ret->cbSize = size - sizeof(WAVEFORMATEX);
871 return ret;
874 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
875 const WAVEFORMATEX *fmt)
877 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
879 desc->mFormatFlags = 0;
881 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
882 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
883 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
884 desc->mFormatID = kAudioFormatLinearPCM;
885 if(fmt->wBitsPerSample > 8)
886 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
887 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
888 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
889 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
890 desc->mFormatID = kAudioFormatLinearPCM;
891 desc->mFormatFlags = kAudioFormatFlagIsFloat;
892 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
893 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
894 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
895 desc->mFormatID = kAudioFormatULaw;
896 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
897 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
898 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
899 desc->mFormatID = kAudioFormatALaw;
900 }else
901 return AUDCLNT_E_UNSUPPORTED_FORMAT;
903 desc->mSampleRate = fmt->nSamplesPerSec;
904 desc->mBytesPerPacket = fmt->nBlockAlign;
905 desc->mFramesPerPacket = 1;
906 desc->mBytesPerFrame = fmt->nBlockAlign;
907 desc->mChannelsPerFrame = fmt->nChannels;
908 desc->mBitsPerChannel = fmt->wBitsPerSample;
909 desc->mReserved = 0;
911 return S_OK;
914 static void session_init_vols(AudioSession *session, UINT channels)
916 if(session->channel_count < channels){
917 UINT i;
919 if(session->channel_vols)
920 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
921 session->channel_vols, sizeof(float) * channels);
922 else
923 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
924 sizeof(float) * channels);
925 if(!session->channel_vols)
926 return;
928 for(i = session->channel_count; i < channels; ++i)
929 session->channel_vols[i] = 1.f;
931 session->channel_count = channels;
935 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
936 UINT num_channels)
938 AudioSession *ret;
940 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
941 if(!ret)
942 return NULL;
944 memcpy(&ret->guid, guid, sizeof(GUID));
946 ret->device = device;
948 list_init(&ret->clients);
950 list_add_head(&g_sessions, &ret->entry);
952 InitializeCriticalSection(&ret->lock);
953 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
955 session_init_vols(ret, num_channels);
957 ret->master_vol = 1.f;
959 return ret;
962 /* if channels == 0, then this will return or create a session with
963 * matching dataflow and GUID. otherwise, channels must also match */
964 static HRESULT get_audio_session(const GUID *sessionguid,
965 IMMDevice *device, UINT channels, AudioSession **out)
967 AudioSession *session;
969 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
970 *out = create_session(&GUID_NULL, device, channels);
971 if(!*out)
972 return E_OUTOFMEMORY;
974 return S_OK;
977 *out = NULL;
978 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
979 if(session->device == device &&
980 IsEqualGUID(sessionguid, &session->guid)){
981 session_init_vols(session, channels);
982 *out = session;
983 break;
987 if(!*out){
988 *out = create_session(sessionguid, device, channels);
989 if(!*out)
990 return E_OUTOFMEMORY;
993 return S_OK;
996 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
997 BYTE *src, UINT32 src_bytes)
999 UINT32 chunk_bytes = dst_bytes - dst_offs;
1001 if(chunk_bytes < src_bytes){
1002 memcpy(dst + dst_offs, src, chunk_bytes);
1003 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
1004 }else
1005 memcpy(dst + dst_offs, src, src_bytes);
1008 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1010 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1011 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1012 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1013 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1014 This->fmt->wBitsPerSample == 8)
1015 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1016 else
1017 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1020 /* CA is pulling data from us */
1021 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
1022 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1023 AudioBufferList *data)
1025 ACImpl *This = user;
1026 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
1028 OSSpinLockLock(&This->lock);
1030 if(This->playing){
1031 lcl_offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1032 to_copy_frames = min(nframes, This->held_frames);
1033 to_copy_bytes = to_copy_frames * This->fmt->nBlockAlign;
1035 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) * This->fmt->nBlockAlign;
1037 if(to_copy_bytes > chunk_bytes){
1038 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, chunk_bytes);
1039 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, This->local_buffer, to_copy_bytes - chunk_bytes);
1040 }else
1041 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, to_copy_bytes);
1043 This->lcl_offs_frames += to_copy_frames;
1044 This->lcl_offs_frames %= This->bufsize_frames;
1045 This->held_frames -= to_copy_frames;
1046 }else
1047 to_copy_bytes = to_copy_frames = 0;
1049 if(nframes > to_copy_frames)
1050 silence_buffer(This, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
1052 OSSpinLockUnlock(&This->lock);
1054 return noErr;
1057 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1059 if(left <= right)
1060 return right - left;
1061 return bufsize - (left - right);
1064 /* place data from cap_buffer into provided AudioBufferList */
1065 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1066 AudioStreamPacketDescription **packets, void *user)
1068 ACImpl *This = user;
1070 *nframes = min(*nframes, This->cap_held_frames);
1071 if(!*nframes){
1072 data->mBuffers[0].mData = NULL;
1073 data->mBuffers[0].mDataByteSize = 0;
1074 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1075 return noErr;
1078 data->mBuffers[0].mDataByteSize = *nframes * This->fmt->nBlockAlign;
1079 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1081 if(This->cap_offs_frames + *nframes > This->cap_bufsize_frames){
1082 UINT32 chunk_frames = This->cap_bufsize_frames - This->cap_offs_frames;
1084 if(This->wrap_bufsize_frames < *nframes){
1085 free(This->wrap_buffer);
1086 This->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1087 This->wrap_bufsize_frames = *nframes;
1090 memcpy(This->wrap_buffer, This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign,
1091 chunk_frames * This->fmt->nBlockAlign);
1092 memcpy(This->wrap_buffer + chunk_frames * This->fmt->nBlockAlign, This->cap_buffer,
1093 (*nframes - chunk_frames) * This->fmt->nBlockAlign);
1095 data->mBuffers[0].mData = This->wrap_buffer;
1096 }else
1097 data->mBuffers[0].mData = This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign;
1099 This->cap_offs_frames += *nframes;
1100 This->cap_offs_frames %= This->cap_bufsize_frames;
1101 This->cap_held_frames -= *nframes;
1103 if(packets)
1104 *packets = NULL;
1106 return noErr;
1109 static void capture_resample(ACImpl *This)
1111 UINT32 resamp_period_frames = MulDiv(This->period_frames, This->dev_desc.mSampleRate, This->fmt->nSamplesPerSec);
1112 OSStatus sc;
1114 /* the resampling process often needs more source frames than we'd
1115 * guess from a straight conversion using the sample rate ratio. so
1116 * only convert if we have extra source data. */
1117 while(This->cap_held_frames > resamp_period_frames * 2){
1118 AudioBufferList converted_list;
1119 UInt32 wanted_frames = This->period_frames;
1121 converted_list.mNumberBuffers = 1;
1122 converted_list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1123 converted_list.mBuffers[0].mDataByteSize = wanted_frames * This->fmt->nBlockAlign;
1125 if(This->resamp_bufsize_frames < wanted_frames){
1126 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
1127 This->resamp_buffer = HeapAlloc(GetProcessHeap(), 0, converted_list.mBuffers[0].mDataByteSize);
1128 This->resamp_bufsize_frames = wanted_frames;
1131 converted_list.mBuffers[0].mData = This->resamp_buffer;
1133 sc = AudioConverterFillComplexBuffer(This->converter, feed_cb,
1134 This, &wanted_frames, &converted_list, NULL);
1135 if(sc != noErr){
1136 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1137 break;
1140 ca_wrap_buffer(This->local_buffer,
1141 This->wri_offs_frames * This->fmt->nBlockAlign,
1142 This->bufsize_frames * This->fmt->nBlockAlign,
1143 This->resamp_buffer, wanted_frames * This->fmt->nBlockAlign);
1145 This->wri_offs_frames += wanted_frames;
1146 This->wri_offs_frames %= This->bufsize_frames;
1147 if(This->held_frames + wanted_frames > This->bufsize_frames){
1148 This->lcl_offs_frames += buf_ptr_diff(This->lcl_offs_frames,
1149 This->wri_offs_frames, This->bufsize_frames);
1150 This->held_frames = This->bufsize_frames;
1151 }else
1152 This->held_frames += wanted_frames;
1156 /* we need to trigger CA to pull data from the device and give it to us
1158 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
1160 * raw data is resampled from cap_buffer into resamp_buffer in period-size
1161 * chunks and copied to local_buffer
1163 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
1164 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1165 AudioBufferList *data)
1167 ACImpl *This = user;
1168 AudioBufferList list;
1169 OSStatus sc;
1170 UINT32 cap_wri_offs_frames;
1172 OSSpinLockLock(&This->lock);
1174 cap_wri_offs_frames = (This->cap_offs_frames + This->cap_held_frames) % This->cap_bufsize_frames;
1176 list.mNumberBuffers = 1;
1177 list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1178 list.mBuffers[0].mDataByteSize = nframes * This->fmt->nBlockAlign;
1180 if(!This->playing || cap_wri_offs_frames + nframes > This->cap_bufsize_frames){
1181 if(This->wrap_bufsize_frames < nframes){
1182 free(This->wrap_buffer);
1183 This->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
1184 This->wrap_bufsize_frames = nframes;
1187 list.mBuffers[0].mData = This->wrap_buffer;
1188 }else
1189 list.mBuffers[0].mData = This->cap_buffer + cap_wri_offs_frames * This->fmt->nBlockAlign;
1191 sc = AudioUnitRender(This->unit, flags, ts, bus, nframes, &list);
1192 if(sc != noErr){
1193 OSSpinLockUnlock(&This->lock);
1194 return sc;
1197 if(This->playing){
1198 if(list.mBuffers[0].mData == This->wrap_buffer){
1199 ca_wrap_buffer(This->cap_buffer,
1200 cap_wri_offs_frames * This->fmt->nBlockAlign,
1201 This->cap_bufsize_frames * This->fmt->nBlockAlign,
1202 This->wrap_buffer, list.mBuffers[0].mDataByteSize);
1205 This->cap_held_frames += list.mBuffers[0].mDataByteSize / This->fmt->nBlockAlign;
1206 if(This->cap_held_frames > This->cap_bufsize_frames){
1207 This->cap_offs_frames += This->cap_held_frames % This->cap_bufsize_frames;
1208 This->cap_offs_frames %= This->cap_bufsize_frames;
1209 This->cap_held_frames = This->cap_bufsize_frames;
1213 OSSpinLockUnlock(&This->lock);
1214 return noErr;
1217 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
1219 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
1220 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
1221 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
1222 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
1223 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
1224 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
1227 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
1228 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
1229 AudioConverterRef *converter)
1231 OSStatus sc;
1232 HRESULT hr;
1234 if(dataflow == eCapture){
1235 AudioStreamBasicDescription desc;
1236 UInt32 size;
1237 Float64 rate;
1238 fenv_t fenv;
1239 BOOL fenv_stored = TRUE;
1241 hr = ca_get_audiodesc(&desc, fmt);
1242 if(FAILED(hr))
1243 return hr;
1244 dump_adesc("requested", &desc);
1246 /* input-only units can't perform sample rate conversion, so we have to
1247 * set up our own AudioConverter to support arbitrary sample rates. */
1248 size = sizeof(*dev_desc);
1249 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
1250 kAudioUnitScope_Input, 1, dev_desc, &size);
1251 if(sc != noErr){
1252 WARN("Couldn't get unit format: %x\n", (int)sc);
1253 return osstatus_to_hresult(sc);
1255 dump_adesc("hardware", dev_desc);
1257 rate = dev_desc->mSampleRate;
1258 *dev_desc = desc;
1259 dev_desc->mSampleRate = rate;
1261 dump_adesc("final", dev_desc);
1262 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1263 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
1264 if(sc != noErr){
1265 WARN("Couldn't set unit format: %x\n", (int)sc);
1266 return osstatus_to_hresult(sc);
1269 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
1270 if(feholdexcept(&fenv)){
1271 WARN("Failed to store fenv state\n");
1272 fenv_stored = FALSE;
1275 sc = AudioConverterNew(dev_desc, &desc, converter);
1277 if(fenv_stored && fesetenv(&fenv))
1278 WARN("Failed to restore fenv state\n");
1280 if(sc != noErr){
1281 WARN("Couldn't create audio converter: %x\n", (int)sc);
1282 return osstatus_to_hresult(sc);
1284 }else{
1285 hr = ca_get_audiodesc(dev_desc, fmt);
1286 if(FAILED(hr))
1287 return hr;
1289 dump_adesc("final", dev_desc);
1290 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1291 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
1292 if(sc != noErr){
1293 WARN("Couldn't set format: %x\n", (int)sc);
1294 return osstatus_to_hresult(sc);
1298 return S_OK;
1301 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
1302 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1303 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1304 const GUID *sessionguid)
1306 ACImpl *This = impl_from_IAudioClient3(iface);
1307 HRESULT hr;
1308 OSStatus sc;
1309 int i;
1311 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1312 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1314 if(!fmt)
1315 return E_POINTER;
1317 dump_fmt(fmt);
1319 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1320 return E_INVALIDARG;
1322 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1323 AUDCLNT_STREAMFLAGS_LOOPBACK |
1324 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1325 AUDCLNT_STREAMFLAGS_NOPERSIST |
1326 AUDCLNT_STREAMFLAGS_RATEADJUST |
1327 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1328 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1329 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
1330 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
1331 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
1332 FIXME("Unknown flags: %08x\n", flags);
1333 return E_INVALIDARG;
1336 if(mode == AUDCLNT_SHAREMODE_SHARED){
1337 period = DefaultPeriod;
1338 if( duration < 3 * period)
1339 duration = 3 * period;
1340 }else{
1341 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1342 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1343 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1344 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1347 if(!period)
1348 period = DefaultPeriod; /* not minimum */
1349 if(period < MinimumPeriod || period > 5000000)
1350 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1351 if(duration > 20000000) /* the smaller the period, the lower this limit */
1352 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1353 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1354 if(duration != period)
1355 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1356 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1357 return AUDCLNT_E_DEVICE_IN_USE;
1358 }else{
1359 if( duration < 8 * period)
1360 duration = 8 * period; /* may grow above 2s */
1364 OSSpinLockLock(&This->lock);
1366 if(This->initted){
1367 OSSpinLockUnlock(&This->lock);
1368 return AUDCLNT_E_ALREADY_INITIALIZED;
1371 This->fmt = clone_format(fmt);
1372 if(!This->fmt){
1373 OSSpinLockUnlock(&This->lock);
1374 return E_OUTOFMEMORY;
1377 This->period_ms = period / 10000;
1378 This->period_frames = MulDiv(period, This->fmt->nSamplesPerSec, 10000000);
1380 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1381 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1382 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1384 hr = ca_setup_audiounit(This->dataflow, This->unit, This->fmt, &This->dev_desc, &This->converter);
1385 if(FAILED(hr)){
1386 CoTaskMemFree(This->fmt);
1387 This->fmt = NULL;
1388 OSSpinLockUnlock(&This->lock);
1389 return hr;
1392 if(This->dataflow == eCapture){
1393 AURenderCallbackStruct input;
1395 memset(&input, 0, sizeof(input));
1396 input.inputProc = &ca_capture_cb;
1397 input.inputProcRefCon = This;
1399 sc = AudioUnitSetProperty(This->unit, kAudioOutputUnitProperty_SetInputCallback,
1400 kAudioUnitScope_Output, 1, &input, sizeof(input));
1401 if(sc != noErr){
1402 WARN("Couldn't set callback: %x\n", (int)sc);
1403 AudioConverterDispose(This->converter);
1404 This->converter = NULL;
1405 CoTaskMemFree(This->fmt);
1406 This->fmt = NULL;
1407 OSSpinLockUnlock(&This->lock);
1408 return osstatus_to_hresult(sc);
1410 }else{
1411 AURenderCallbackStruct input;
1413 memset(&input, 0, sizeof(input));
1414 input.inputProc = &ca_render_cb;
1415 input.inputProcRefCon = This;
1417 sc = AudioUnitSetProperty(This->unit, kAudioUnitProperty_SetRenderCallback,
1418 kAudioUnitScope_Input, 0, &input, sizeof(input));
1419 if(sc != noErr){
1420 WARN("Couldn't set callback: %x\n", (int)sc);
1421 CoTaskMemFree(This->fmt);
1422 This->fmt = NULL;
1423 OSSpinLockUnlock(&This->lock);
1424 return osstatus_to_hresult(sc);
1428 sc = AudioUnitInitialize(This->unit);
1429 if(sc != noErr){
1430 WARN("Couldn't initialize: %x\n", (int)sc);
1431 if(This->converter){
1432 AudioConverterDispose(This->converter);
1433 This->converter = NULL;
1435 CoTaskMemFree(This->fmt);
1436 This->fmt = NULL;
1437 OSSpinLockUnlock(&This->lock);
1438 return osstatus_to_hresult(sc);
1441 /* we play audio continuously because AudioOutputUnitStart sometimes takes
1442 * a while to return */
1443 sc = AudioOutputUnitStart(This->unit);
1444 if(sc != noErr){
1445 WARN("Unit failed to start: %x\n", (int)sc);
1446 if(This->converter){
1447 AudioConverterDispose(This->converter);
1448 This->converter = NULL;
1450 CoTaskMemFree(This->fmt);
1451 This->fmt = NULL;
1452 OSSpinLockUnlock(&This->lock);
1453 return osstatus_to_hresult(sc);
1456 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_frames * fmt->nBlockAlign);
1457 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1459 if(This->dataflow == eCapture){
1460 This->cap_bufsize_frames = MulDiv(duration, This->dev_desc.mSampleRate, 10000000);
1461 This->cap_buffer = HeapAlloc(GetProcessHeap(), 0, This->cap_bufsize_frames * This->fmt->nBlockAlign);
1464 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1465 if(!This->vols){
1466 CoTaskMemFree(This->fmt);
1467 This->fmt = NULL;
1468 OSSpinLockUnlock(&This->lock);
1469 return E_OUTOFMEMORY;
1472 for(i = 0; i < fmt->nChannels; ++i)
1473 This->vols[i] = 1.f;
1475 This->share = mode;
1476 This->flags = flags;
1478 EnterCriticalSection(&g_sessions_lock);
1480 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1481 &This->session);
1482 if(FAILED(hr)){
1483 LeaveCriticalSection(&g_sessions_lock);
1484 CoTaskMemFree(This->fmt);
1485 This->fmt = NULL;
1486 HeapFree(GetProcessHeap(), 0, This->vols);
1487 This->vols = NULL;
1488 OSSpinLockUnlock(&This->lock);
1489 return E_INVALIDARG;
1492 list_add_tail(&This->session->clients, &This->entry);
1494 LeaveCriticalSection(&g_sessions_lock);
1496 ca_setvol(This, -1);
1498 This->initted = TRUE;
1500 OSSpinLockUnlock(&This->lock);
1502 return S_OK;
1505 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
1506 UINT32 *frames)
1508 ACImpl *This = impl_from_IAudioClient3(iface);
1510 TRACE("(%p)->(%p)\n", This, frames);
1512 if(!frames)
1513 return E_POINTER;
1515 OSSpinLockLock(&This->lock);
1517 if(!This->initted){
1518 OSSpinLockUnlock(&This->lock);
1519 return AUDCLNT_E_NOT_INITIALIZED;
1522 *frames = This->bufsize_frames;
1524 OSSpinLockUnlock(&This->lock);
1526 return S_OK;
1529 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1531 AudioObjectPropertyAddress addr;
1532 AudioStreamID *ids;
1533 UInt32 size;
1534 OSStatus sc;
1535 int nstreams, i;
1537 addr.mScope = This->scope;
1538 addr.mElement = 0;
1539 addr.mSelector = kAudioDevicePropertyStreams;
1541 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1542 &size);
1543 if(sc != noErr){
1544 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1545 return osstatus_to_hresult(sc);
1548 ids = HeapAlloc(GetProcessHeap(), 0, size);
1549 if(!ids)
1550 return E_OUTOFMEMORY;
1552 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1553 if(sc != noErr){
1554 WARN("Unable to get _Streams property: %x\n", (int)sc);
1555 HeapFree(GetProcessHeap(), 0, ids);
1556 return osstatus_to_hresult(sc);
1559 nstreams = size / sizeof(AudioStreamID);
1560 *max = 0;
1562 addr.mSelector = kAudioStreamPropertyLatency;
1563 for(i = 0; i < nstreams; ++i){
1564 UInt32 latency;
1566 size = sizeof(latency);
1567 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1568 &size, &latency);
1569 if(sc != noErr){
1570 WARN("Unable to get _Latency property: %x\n", (int)sc);
1571 continue;
1574 if(latency > *max)
1575 *max = latency;
1578 HeapFree(GetProcessHeap(), 0, ids);
1580 return S_OK;
1583 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
1584 REFERENCE_TIME *out)
1586 ACImpl *This = impl_from_IAudioClient3(iface);
1587 UInt32 latency, stream_latency, size;
1588 AudioObjectPropertyAddress addr;
1589 OSStatus sc;
1590 HRESULT hr;
1592 TRACE("(%p)->(%p)\n", This, out);
1594 if(!out)
1595 return E_POINTER;
1597 OSSpinLockLock(&This->lock);
1599 if(!This->initted){
1600 OSSpinLockUnlock(&This->lock);
1601 return AUDCLNT_E_NOT_INITIALIZED;
1604 addr.mScope = This->scope;
1605 addr.mSelector = kAudioDevicePropertyLatency;
1606 addr.mElement = 0;
1608 size = sizeof(latency);
1609 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1610 &size, &latency);
1611 if(sc != noErr){
1612 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1613 OSSpinLockUnlock(&This->lock);
1614 return osstatus_to_hresult(sc);
1617 hr = ca_get_max_stream_latency(This, &stream_latency);
1618 if(FAILED(hr)){
1619 OSSpinLockUnlock(&This->lock);
1620 return hr;
1623 latency += stream_latency;
1624 /* pretend we process audio in Period chunks, so max latency includes
1625 * the period time */
1626 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1627 + This->period_ms * 10000;
1629 OSSpinLockUnlock(&This->lock);
1631 return S_OK;
1634 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1635 UINT32 *numpad)
1637 if(!This->initted)
1638 return AUDCLNT_E_NOT_INITIALIZED;
1640 if(This->dataflow == eCapture)
1641 capture_resample(This);
1643 *numpad = This->held_frames;
1645 return S_OK;
1648 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
1649 UINT32 *numpad)
1651 ACImpl *This = impl_from_IAudioClient3(iface);
1652 HRESULT hr;
1654 TRACE("(%p)->(%p)\n", This, numpad);
1656 if(!numpad)
1657 return E_POINTER;
1659 OSSpinLockLock(&This->lock);
1661 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1663 OSSpinLockUnlock(&This->lock);
1665 return hr;
1668 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
1669 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1670 WAVEFORMATEX **outpwfx)
1672 ACImpl *This = impl_from_IAudioClient3(iface);
1673 AudioStreamBasicDescription dev_desc;
1674 AudioConverterRef converter;
1675 AudioComponentInstance unit;
1676 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1677 HRESULT hr;
1679 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1681 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1682 return E_POINTER;
1684 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1685 return E_INVALIDARG;
1687 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1688 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1689 return E_INVALIDARG;
1691 dump_fmt(pwfx);
1693 if(outpwfx){
1694 *outpwfx = NULL;
1695 if(mode != AUDCLNT_SHAREMODE_SHARED)
1696 outpwfx = NULL;
1699 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1700 if(pwfx->nAvgBytesPerSec == 0 ||
1701 pwfx->nBlockAlign == 0 ||
1702 fmtex->Samples.wValidBitsPerSample > pwfx->wBitsPerSample)
1703 return E_INVALIDARG;
1704 if(fmtex->Samples.wValidBitsPerSample < pwfx->wBitsPerSample)
1705 goto unsupported;
1706 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1707 if(fmtex->dwChannelMask == 0 ||
1708 fmtex->dwChannelMask & SPEAKER_RESERVED)
1709 goto unsupported;
1713 if(pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample / 8 ||
1714 pwfx->nAvgBytesPerSec != pwfx->nBlockAlign * pwfx->nSamplesPerSec)
1715 goto unsupported;
1717 if(pwfx->nChannels == 0)
1718 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1720 unit = get_audiounit(This->dataflow, This->adevid);
1722 converter = NULL;
1723 hr = ca_setup_audiounit(This->dataflow, unit, pwfx, &dev_desc, &converter);
1724 AudioComponentInstanceDispose(unit);
1725 if(FAILED(hr))
1726 goto unsupported;
1728 if(converter)
1729 AudioConverterDispose(converter);
1731 return S_OK;
1733 unsupported:
1734 if(outpwfx){
1735 hr = IAudioClient3_GetMixFormat(&This->IAudioClient3_iface, outpwfx);
1736 if(FAILED(hr))
1737 return hr;
1738 return S_FALSE;
1741 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1744 static DWORD ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
1746 int i;
1747 DWORD mask = 0;
1749 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
1750 switch (layout->mChannelDescriptions[i].mChannelLabel) {
1751 default: FIXME("Unhandled channel 0x%x\n", (unsigned int)layout->mChannelDescriptions[i].mChannelLabel); break;
1752 case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
1753 case kAudioChannelLabel_Mono:
1754 case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
1755 case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
1756 case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
1757 case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
1758 case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
1759 case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
1760 case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
1761 case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
1762 case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
1763 case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
1764 case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
1765 case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
1766 case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
1767 case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
1768 case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
1769 case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
1770 case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
1774 return mask;
1777 /* For most hardware on Windows, users must choose a configuration with an even
1778 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1779 * channels, but those channels are still reported to applications from
1780 * GetMixFormat! Some applications behave badly if given an odd number of
1781 * channels (e.g. 2.1). Here, we find the nearest configuration that Windows
1782 * would report for a given channel layout. */
1783 static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
1785 DWORD ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
1787 TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
1789 if (ca_layout->mNumberChannelDescriptions == 1)
1791 fmt->Format.nChannels = 1;
1792 fmt->dwChannelMask = ca_mask;
1793 return;
1796 /* compare against known configurations and find smallest configuration
1797 * which is a superset of the given speakers */
1799 if (ca_layout->mNumberChannelDescriptions <= 2 &&
1800 (ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
1802 fmt->Format.nChannels = 2;
1803 fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1804 return;
1807 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1808 (ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
1810 fmt->Format.nChannels = 4;
1811 fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
1812 return;
1815 if (ca_layout->mNumberChannelDescriptions <= 4 &&
1816 (ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
1818 fmt->Format.nChannels = 4;
1819 fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
1820 return;
1823 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1824 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
1826 fmt->Format.nChannels = 6;
1827 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
1828 return;
1831 if (ca_layout->mNumberChannelDescriptions <= 6 &&
1832 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
1834 fmt->Format.nChannels = 6;
1835 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
1836 return;
1839 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1840 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
1842 fmt->Format.nChannels = 8;
1843 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
1844 return;
1847 if (ca_layout->mNumberChannelDescriptions <= 8 &&
1848 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
1850 fmt->Format.nChannels = 8;
1851 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
1852 return;
1855 /* oddball format, report truthfully */
1856 fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
1857 fmt->dwChannelMask = ca_mask;
1860 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1861 WAVEFORMATEX **pwfx)
1863 ACImpl *This = impl_from_IAudioClient3(iface);
1864 WAVEFORMATEXTENSIBLE *fmt;
1865 OSStatus sc;
1866 UInt32 size;
1867 Float64 rate;
1868 AudioBufferList *buffers;
1869 AudioChannelLayout *layout;
1870 AudioObjectPropertyAddress addr;
1871 int i;
1873 TRACE("(%p)->(%p)\n", This, pwfx);
1875 if(!pwfx)
1876 return E_POINTER;
1877 *pwfx = NULL;
1879 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1880 if(!fmt)
1881 return E_OUTOFMEMORY;
1883 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1885 addr.mScope = This->scope;
1886 addr.mElement = 0;
1887 addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
1889 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1890 if(sc == noErr){
1891 layout = HeapAlloc(GetProcessHeap(), 0, size);
1893 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, layout);
1894 if(sc == noErr){
1895 TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
1896 (unsigned int)layout->mChannelLayoutTag, (unsigned int)layout->mChannelBitmap,
1897 (unsigned int)layout->mNumberChannelDescriptions);
1899 if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
1900 convert_channel_layout(layout, fmt);
1901 }else{
1902 WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n", (unsigned int)layout->mChannelLayoutTag);
1903 fmt->Format.nChannels = 0;
1905 }else{
1906 TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1907 fmt->Format.nChannels = 0;
1910 HeapFree(GetProcessHeap(), 0, layout);
1911 }else{
1912 TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
1913 fmt->Format.nChannels = 0;
1916 if(fmt->Format.nChannels == 0){
1917 addr.mScope = This->scope;
1918 addr.mElement = 0;
1919 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1921 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1922 if(sc != noErr){
1923 CoTaskMemFree(fmt);
1924 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
1925 return osstatus_to_hresult(sc);
1928 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1929 if(!buffers){
1930 CoTaskMemFree(fmt);
1931 return E_OUTOFMEMORY;
1934 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1935 &size, buffers);
1936 if(sc != noErr){
1937 CoTaskMemFree(fmt);
1938 HeapFree(GetProcessHeap(), 0, buffers);
1939 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
1940 return osstatus_to_hresult(sc);
1943 fmt->Format.nChannels = 0;
1944 for(i = 0; i < buffers->mNumberBuffers; ++i)
1945 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1947 HeapFree(GetProcessHeap(), 0, buffers);
1949 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1952 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1953 size = sizeof(Float64);
1954 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1955 if(sc != noErr){
1956 CoTaskMemFree(fmt);
1957 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
1958 return osstatus_to_hresult(sc);
1960 fmt->Format.nSamplesPerSec = rate;
1962 fmt->Format.wBitsPerSample = 32;
1963 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1965 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1966 fmt->Format.nChannels) / 8;
1967 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1968 fmt->Format.nBlockAlign;
1970 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1971 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1973 *pwfx = (WAVEFORMATEX*)fmt;
1974 dump_fmt(*pwfx);
1976 return S_OK;
1979 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1980 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1982 ACImpl *This = impl_from_IAudioClient3(iface);
1984 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1986 if(!defperiod && !minperiod)
1987 return E_POINTER;
1989 if(defperiod)
1990 *defperiod = DefaultPeriod;
1991 if(minperiod)
1992 *minperiod = MinimumPeriod;
1994 return S_OK;
1997 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1999 ACImpl *This = user;
2001 if(This->event)
2002 SetEvent(This->event);
2005 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
2007 ACImpl *This = impl_from_IAudioClient3(iface);
2009 TRACE("(%p)\n", This);
2011 OSSpinLockLock(&This->lock);
2013 if(!This->initted){
2014 OSSpinLockUnlock(&This->lock);
2015 return AUDCLNT_E_NOT_INITIALIZED;
2018 if(This->playing){
2019 OSSpinLockUnlock(&This->lock);
2020 return AUDCLNT_E_NOT_STOPPED;
2023 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2024 OSSpinLockUnlock(&This->lock);
2025 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2028 if(This->event && !This->timer)
2029 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
2030 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
2031 This->timer = NULL;
2032 OSSpinLockUnlock(&This->lock);
2033 WARN("Unable to create timer: %u\n", GetLastError());
2034 return E_OUTOFMEMORY;
2037 This->playing = TRUE;
2039 OSSpinLockUnlock(&This->lock);
2041 return S_OK;
2044 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
2046 ACImpl *This = impl_from_IAudioClient3(iface);
2048 TRACE("(%p)\n", This);
2050 OSSpinLockLock(&This->lock);
2052 if(!This->initted){
2053 OSSpinLockUnlock(&This->lock);
2054 return AUDCLNT_E_NOT_INITIALIZED;
2057 if(!This->playing){
2058 OSSpinLockUnlock(&This->lock);
2059 return S_FALSE;
2062 This->playing = FALSE;
2064 OSSpinLockUnlock(&This->lock);
2066 return S_OK;
2069 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
2071 ACImpl *This = impl_from_IAudioClient3(iface);
2073 TRACE("(%p)\n", This);
2075 OSSpinLockLock(&This->lock);
2077 if(!This->initted){
2078 OSSpinLockUnlock(&This->lock);
2079 return AUDCLNT_E_NOT_INITIALIZED;
2082 if(This->playing){
2083 OSSpinLockUnlock(&This->lock);
2084 return AUDCLNT_E_NOT_STOPPED;
2087 if(This->getbuf_last){
2088 OSSpinLockUnlock(&This->lock);
2089 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2092 if(This->dataflow == eRender){
2093 This->written_frames = 0;
2094 }else{
2095 This->written_frames += This->held_frames;
2098 This->held_frames = 0;
2099 This->lcl_offs_frames = 0;
2100 This->wri_offs_frames = 0;
2101 This->cap_offs_frames = 0;
2102 This->cap_held_frames = 0;
2104 OSSpinLockUnlock(&This->lock);
2106 return S_OK;
2109 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
2110 HANDLE event)
2112 ACImpl *This = impl_from_IAudioClient3(iface);
2114 TRACE("(%p)->(%p)\n", This, event);
2116 if(!event)
2117 return E_INVALIDARG;
2119 OSSpinLockLock(&This->lock);
2121 if(!This->initted){
2122 OSSpinLockUnlock(&This->lock);
2123 return AUDCLNT_E_NOT_INITIALIZED;
2126 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2127 OSSpinLockUnlock(&This->lock);
2128 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2131 if (This->event){
2132 OSSpinLockUnlock(&This->lock);
2133 FIXME("called twice\n");
2134 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2137 This->event = event;
2139 OSSpinLockUnlock(&This->lock);
2141 return S_OK;
2144 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
2145 void **ppv)
2147 ACImpl *This = impl_from_IAudioClient3(iface);
2149 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2151 if(!ppv)
2152 return E_POINTER;
2153 *ppv = NULL;
2155 OSSpinLockLock(&This->lock);
2157 if(!This->initted){
2158 OSSpinLockUnlock(&This->lock);
2159 return AUDCLNT_E_NOT_INITIALIZED;
2162 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2163 if(This->dataflow != eRender){
2164 OSSpinLockUnlock(&This->lock);
2165 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2167 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2168 *ppv = &This->IAudioRenderClient_iface;
2169 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2170 if(This->dataflow != eCapture){
2171 OSSpinLockUnlock(&This->lock);
2172 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2174 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2175 *ppv = &This->IAudioCaptureClient_iface;
2176 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2177 IAudioClock_AddRef(&This->IAudioClock_iface);
2178 *ppv = &This->IAudioClock_iface;
2179 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2180 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2181 *ppv = &This->IAudioStreamVolume_iface;
2182 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2183 if(!This->session_wrapper){
2184 This->session_wrapper = AudioSessionWrapper_Create(This);
2185 if(!This->session_wrapper){
2186 OSSpinLockUnlock(&This->lock);
2187 return E_OUTOFMEMORY;
2189 }else
2190 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2192 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2193 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2194 if(!This->session_wrapper){
2195 This->session_wrapper = AudioSessionWrapper_Create(This);
2196 if(!This->session_wrapper){
2197 OSSpinLockUnlock(&This->lock);
2198 return E_OUTOFMEMORY;
2200 }else
2201 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2203 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2204 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2205 if(!This->session_wrapper){
2206 This->session_wrapper = AudioSessionWrapper_Create(This);
2207 if(!This->session_wrapper){
2208 OSSpinLockUnlock(&This->lock);
2209 return E_OUTOFMEMORY;
2211 }else
2212 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2214 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2217 if(*ppv){
2218 OSSpinLockUnlock(&This->lock);
2219 return S_OK;
2222 OSSpinLockUnlock(&This->lock);
2224 FIXME("stub %s\n", debugstr_guid(riid));
2225 return E_NOINTERFACE;
2228 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
2229 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
2231 ACImpl *This = impl_from_IAudioClient3(iface);
2233 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
2235 if(!offload_capable)
2236 return E_INVALIDARG;
2238 *offload_capable = FALSE;
2240 return S_OK;
2243 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
2244 const AudioClientProperties *prop)
2246 ACImpl *This = impl_from_IAudioClient3(iface);
2247 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
2249 TRACE("(%p)->(%p)\n", This, prop);
2251 if(!legacy_prop)
2252 return E_POINTER;
2254 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
2255 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2256 legacy_prop->bIsOffload,
2257 legacy_prop->eCategory,
2258 prop->Options);
2259 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
2260 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
2261 legacy_prop->bIsOffload,
2262 legacy_prop->eCategory);
2263 }else{
2264 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
2265 return E_INVALIDARG;
2269 if(legacy_prop->bIsOffload)
2270 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
2272 return S_OK;
2275 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
2276 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
2277 REFERENCE_TIME *max_duration)
2279 ACImpl *This = impl_from_IAudioClient3(iface);
2281 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
2283 return E_NOTIMPL;
2286 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
2287 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
2288 UINT32 *min_period_frames, UINT32 *max_period_frames)
2290 ACImpl *This = impl_from_IAudioClient3(iface);
2292 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
2293 min_period_frames, max_period_frames);
2295 return E_NOTIMPL;
2298 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
2299 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
2301 ACImpl *This = impl_from_IAudioClient3(iface);
2303 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
2305 return E_NOTIMPL;
2308 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
2309 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
2310 const GUID *session_guid)
2312 ACImpl *This = impl_from_IAudioClient3(iface);
2314 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
2316 return E_NOTIMPL;
2319 static const IAudioClient3Vtbl AudioClient3_Vtbl =
2321 AudioClient_QueryInterface,
2322 AudioClient_AddRef,
2323 AudioClient_Release,
2324 AudioClient_Initialize,
2325 AudioClient_GetBufferSize,
2326 AudioClient_GetStreamLatency,
2327 AudioClient_GetCurrentPadding,
2328 AudioClient_IsFormatSupported,
2329 AudioClient_GetMixFormat,
2330 AudioClient_GetDevicePeriod,
2331 AudioClient_Start,
2332 AudioClient_Stop,
2333 AudioClient_Reset,
2334 AudioClient_SetEventHandle,
2335 AudioClient_GetService,
2336 AudioClient_IsOffloadCapable,
2337 AudioClient_SetClientProperties,
2338 AudioClient_GetBufferSizeLimits,
2339 AudioClient_GetSharedModeEnginePeriod,
2340 AudioClient_GetCurrentSharedModeEnginePeriod,
2341 AudioClient_InitializeSharedAudioStream,
2344 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2345 IAudioRenderClient *iface, REFIID riid, void **ppv)
2347 ACImpl *This = impl_from_IAudioRenderClient(iface);
2348 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2350 if(!ppv)
2351 return E_POINTER;
2352 *ppv = NULL;
2354 if(IsEqualIID(riid, &IID_IUnknown) ||
2355 IsEqualIID(riid, &IID_IAudioRenderClient))
2356 *ppv = iface;
2357 else if(IsEqualIID(riid, &IID_IMarshal))
2358 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2360 if(*ppv){
2361 IUnknown_AddRef((IUnknown*)*ppv);
2362 return S_OK;
2365 WARN("Unknown interface %s\n", debugstr_guid(riid));
2366 return E_NOINTERFACE;
2369 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2371 ACImpl *This = impl_from_IAudioRenderClient(iface);
2372 return AudioClient_AddRef(&This->IAudioClient3_iface);
2375 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2377 ACImpl *This = impl_from_IAudioRenderClient(iface);
2378 return AudioClient_Release(&This->IAudioClient3_iface);
2381 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2382 UINT32 frames, BYTE **data)
2384 ACImpl *This = impl_from_IAudioRenderClient(iface);
2385 UINT32 pad;
2386 HRESULT hr;
2388 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2390 if(!data)
2391 return E_POINTER;
2392 *data = NULL;
2394 OSSpinLockLock(&This->lock);
2396 if(This->getbuf_last){
2397 OSSpinLockUnlock(&This->lock);
2398 return AUDCLNT_E_OUT_OF_ORDER;
2401 if(!frames){
2402 OSSpinLockUnlock(&This->lock);
2403 return S_OK;
2406 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
2407 if(FAILED(hr)){
2408 OSSpinLockUnlock(&This->lock);
2409 return hr;
2412 if(pad + frames > This->bufsize_frames){
2413 OSSpinLockUnlock(&This->lock);
2414 return AUDCLNT_E_BUFFER_TOO_LARGE;
2417 if(This->wri_offs_frames + frames > This->bufsize_frames){
2418 if(This->tmp_buffer_frames < frames){
2419 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2420 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, frames * This->fmt->nBlockAlign);
2421 if(!This->tmp_buffer){
2422 OSSpinLockUnlock(&This->lock);
2423 return E_OUTOFMEMORY;
2425 This->tmp_buffer_frames = frames;
2427 *data = This->tmp_buffer;
2428 This->getbuf_last = -frames;
2429 }else{
2430 *data = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2431 This->getbuf_last = frames;
2434 silence_buffer(This, *data, frames);
2436 OSSpinLockUnlock(&This->lock);
2438 return S_OK;
2441 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2442 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
2444 ACImpl *This = impl_from_IAudioRenderClient(iface);
2445 BYTE *buffer;
2447 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
2449 OSSpinLockLock(&This->lock);
2451 if(!frames){
2452 This->getbuf_last = 0;
2453 OSSpinLockUnlock(&This->lock);
2454 return S_OK;
2457 if(!This->getbuf_last){
2458 OSSpinLockUnlock(&This->lock);
2459 return AUDCLNT_E_OUT_OF_ORDER;
2462 if(frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2463 OSSpinLockUnlock(&This->lock);
2464 return AUDCLNT_E_INVALID_SIZE;
2467 if(This->getbuf_last >= 0)
2468 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2469 else
2470 buffer = This->tmp_buffer;
2472 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2473 silence_buffer(This, buffer, frames);
2475 if(This->getbuf_last < 0)
2476 ca_wrap_buffer(This->local_buffer,
2477 This->wri_offs_frames * This->fmt->nBlockAlign,
2478 This->bufsize_frames * This->fmt->nBlockAlign,
2479 buffer, frames * This->fmt->nBlockAlign);
2482 This->wri_offs_frames += frames;
2483 This->wri_offs_frames %= This->bufsize_frames;
2484 This->held_frames += frames;
2485 This->written_frames += frames;
2486 This->getbuf_last = 0;
2488 OSSpinLockUnlock(&This->lock);
2490 return S_OK;
2493 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2494 AudioRenderClient_QueryInterface,
2495 AudioRenderClient_AddRef,
2496 AudioRenderClient_Release,
2497 AudioRenderClient_GetBuffer,
2498 AudioRenderClient_ReleaseBuffer
2501 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2502 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2504 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2505 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2507 if(!ppv)
2508 return E_POINTER;
2509 *ppv = NULL;
2511 if(IsEqualIID(riid, &IID_IUnknown) ||
2512 IsEqualIID(riid, &IID_IAudioCaptureClient))
2513 *ppv = iface;
2514 else if(IsEqualIID(riid, &IID_IMarshal))
2515 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2517 if(*ppv){
2518 IUnknown_AddRef((IUnknown*)*ppv);
2519 return S_OK;
2522 WARN("Unknown interface %s\n", debugstr_guid(riid));
2523 return E_NOINTERFACE;
2526 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2528 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2529 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2532 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2534 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2535 return IAudioClient3_Release(&This->IAudioClient3_iface);
2538 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2539 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2540 UINT64 *qpcpos)
2542 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2543 UINT32 chunk_bytes, chunk_frames;
2545 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2546 devpos, qpcpos);
2548 if(!data)
2549 return E_POINTER;
2551 *data = NULL;
2553 if(!frames || !flags)
2554 return E_POINTER;
2556 OSSpinLockLock(&This->lock);
2558 if(This->getbuf_last){
2559 OSSpinLockUnlock(&This->lock);
2560 return AUDCLNT_E_OUT_OF_ORDER;
2563 capture_resample(This);
2565 if(This->held_frames < This->period_frames){
2566 *frames = 0;
2567 OSSpinLockUnlock(&This->lock);
2568 return AUDCLNT_S_BUFFER_EMPTY;
2571 *flags = 0;
2573 chunk_frames = This->bufsize_frames - This->lcl_offs_frames;
2574 if(chunk_frames < This->period_frames){
2575 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2576 if(!This->tmp_buffer)
2577 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->period_frames * This->fmt->nBlockAlign);
2578 *data = This->tmp_buffer;
2579 memcpy(*data, This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign, chunk_bytes);
2580 memcpy((*data) + chunk_bytes, This->local_buffer, This->period_frames * This->fmt->nBlockAlign - chunk_bytes);
2581 }else
2582 *data = This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
2584 This->getbuf_last = *frames = This->period_frames;
2586 if(devpos)
2587 *devpos = This->written_frames;
2588 if(qpcpos){ /* fixme: qpc of recording time */
2589 LARGE_INTEGER stamp, freq;
2590 QueryPerformanceCounter(&stamp);
2591 QueryPerformanceFrequency(&freq);
2592 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2595 OSSpinLockUnlock(&This->lock);
2597 return S_OK;
2600 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2601 IAudioCaptureClient *iface, UINT32 done)
2603 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2605 TRACE("(%p)->(%u)\n", This, done);
2607 OSSpinLockLock(&This->lock);
2609 if(!done){
2610 This->getbuf_last = 0;
2611 OSSpinLockUnlock(&This->lock);
2612 return S_OK;
2615 if(!This->getbuf_last){
2616 OSSpinLockUnlock(&This->lock);
2617 return AUDCLNT_E_OUT_OF_ORDER;
2620 if(This->getbuf_last != done){
2621 OSSpinLockUnlock(&This->lock);
2622 return AUDCLNT_E_INVALID_SIZE;
2625 This->written_frames += done;
2626 This->held_frames -= done;
2627 This->lcl_offs_frames += done;
2628 This->lcl_offs_frames %= This->bufsize_frames;
2629 This->getbuf_last = 0;
2631 OSSpinLockUnlock(&This->lock);
2633 return S_OK;
2636 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2637 IAudioCaptureClient *iface, UINT32 *frames)
2639 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2641 TRACE("(%p)->(%p)\n", This, frames);
2643 if(!frames)
2644 return E_POINTER;
2646 OSSpinLockLock(&This->lock);
2648 capture_resample(This);
2650 if(This->held_frames >= This->period_frames)
2651 *frames = This->period_frames;
2652 else
2653 *frames = 0;
2655 OSSpinLockUnlock(&This->lock);
2657 return S_OK;
2660 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2662 AudioCaptureClient_QueryInterface,
2663 AudioCaptureClient_AddRef,
2664 AudioCaptureClient_Release,
2665 AudioCaptureClient_GetBuffer,
2666 AudioCaptureClient_ReleaseBuffer,
2667 AudioCaptureClient_GetNextPacketSize
2670 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2671 REFIID riid, void **ppv)
2673 ACImpl *This = impl_from_IAudioClock(iface);
2675 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2677 if(!ppv)
2678 return E_POINTER;
2679 *ppv = NULL;
2681 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2682 *ppv = iface;
2683 else if(IsEqualIID(riid, &IID_IAudioClock2))
2684 *ppv = &This->IAudioClock2_iface;
2685 if(*ppv){
2686 IUnknown_AddRef((IUnknown*)*ppv);
2687 return S_OK;
2690 WARN("Unknown interface %s\n", debugstr_guid(riid));
2691 return E_NOINTERFACE;
2694 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2696 ACImpl *This = impl_from_IAudioClock(iface);
2697 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2700 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2702 ACImpl *This = impl_from_IAudioClock(iface);
2703 return IAudioClient3_Release(&This->IAudioClient3_iface);
2706 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2708 ACImpl *This = impl_from_IAudioClock(iface);
2710 TRACE("(%p)->(%p)\n", This, freq);
2712 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2713 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2714 else
2715 *freq = This->fmt->nSamplesPerSec;
2717 return S_OK;
2720 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2721 UINT64 *pos, UINT64 *qpctime)
2723 *pos = This->written_frames - This->held_frames;
2725 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2726 *pos *= This->fmt->nBlockAlign;
2728 if(qpctime){
2729 LARGE_INTEGER stamp, freq;
2730 QueryPerformanceCounter(&stamp);
2731 QueryPerformanceFrequency(&freq);
2732 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2735 return S_OK;
2738 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2739 UINT64 *qpctime)
2741 ACImpl *This = impl_from_IAudioClock(iface);
2742 HRESULT hr;
2744 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2746 if(!pos)
2747 return E_POINTER;
2749 OSSpinLockLock(&This->lock);
2751 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2753 OSSpinLockUnlock(&This->lock);
2755 return hr;
2758 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2759 DWORD *chars)
2761 ACImpl *This = impl_from_IAudioClock(iface);
2763 TRACE("(%p)->(%p)\n", This, chars);
2765 if(!chars)
2766 return E_POINTER;
2768 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2770 return S_OK;
2773 static const IAudioClockVtbl AudioClock_Vtbl =
2775 AudioClock_QueryInterface,
2776 AudioClock_AddRef,
2777 AudioClock_Release,
2778 AudioClock_GetFrequency,
2779 AudioClock_GetPosition,
2780 AudioClock_GetCharacteristics
2783 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2784 REFIID riid, void **ppv)
2786 ACImpl *This = impl_from_IAudioClock2(iface);
2787 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2790 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2792 ACImpl *This = impl_from_IAudioClock2(iface);
2793 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2796 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2798 ACImpl *This = impl_from_IAudioClock2(iface);
2799 return IAudioClient3_Release(&This->IAudioClient3_iface);
2802 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2803 UINT64 *pos, UINT64 *qpctime)
2805 ACImpl *This = impl_from_IAudioClock2(iface);
2807 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2809 return E_NOTIMPL;
2812 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2814 AudioClock2_QueryInterface,
2815 AudioClock2_AddRef,
2816 AudioClock2_Release,
2817 AudioClock2_GetDevicePosition
2820 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2822 AudioSessionWrapper *ret;
2824 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2825 sizeof(AudioSessionWrapper));
2826 if(!ret)
2827 return NULL;
2829 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2830 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2831 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2833 ret->ref = 1;
2835 ret->client = client;
2836 if(client){
2837 ret->session = client->session;
2838 IAudioClient3_AddRef(&client->IAudioClient3_iface);
2841 return ret;
2844 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2845 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2847 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2849 if(!ppv)
2850 return E_POINTER;
2851 *ppv = NULL;
2853 if(IsEqualIID(riid, &IID_IUnknown) ||
2854 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2855 IsEqualIID(riid, &IID_IAudioSessionControl2))
2856 *ppv = iface;
2857 if(*ppv){
2858 IUnknown_AddRef((IUnknown*)*ppv);
2859 return S_OK;
2862 WARN("Unknown interface %s\n", debugstr_guid(riid));
2863 return E_NOINTERFACE;
2866 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2868 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2869 ULONG ref;
2870 ref = InterlockedIncrement(&This->ref);
2871 TRACE("(%p) Refcount now %u\n", This, ref);
2872 return ref;
2875 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2877 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2878 ULONG ref;
2879 ref = InterlockedDecrement(&This->ref);
2880 TRACE("(%p) Refcount now %u\n", This, ref);
2881 if(!ref){
2882 if(This->client){
2883 OSSpinLockLock(&This->client->lock);
2884 This->client->session_wrapper = NULL;
2885 OSSpinLockUnlock(&This->client->lock);
2886 AudioClient_Release(&This->client->IAudioClient3_iface);
2888 HeapFree(GetProcessHeap(), 0, This);
2890 return ref;
2893 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2894 AudioSessionState *state)
2896 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2897 ACImpl *client;
2899 TRACE("(%p)->(%p)\n", This, state);
2901 if(!state)
2902 return NULL_PTR_ERR;
2904 EnterCriticalSection(&g_sessions_lock);
2906 if(list_empty(&This->session->clients)){
2907 *state = AudioSessionStateExpired;
2908 LeaveCriticalSection(&g_sessions_lock);
2909 return S_OK;
2912 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2913 OSSpinLockLock(&client->lock);
2914 if(client->playing){
2915 *state = AudioSessionStateActive;
2916 OSSpinLockUnlock(&client->lock);
2917 LeaveCriticalSection(&g_sessions_lock);
2918 return S_OK;
2920 OSSpinLockUnlock(&client->lock);
2923 LeaveCriticalSection(&g_sessions_lock);
2925 *state = AudioSessionStateInactive;
2927 return S_OK;
2930 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2931 IAudioSessionControl2 *iface, WCHAR **name)
2933 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2935 FIXME("(%p)->(%p) - stub\n", This, name);
2937 return E_NOTIMPL;
2940 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2941 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2943 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2945 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2947 return E_NOTIMPL;
2950 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2951 IAudioSessionControl2 *iface, WCHAR **path)
2953 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2955 FIXME("(%p)->(%p) - stub\n", This, path);
2957 return E_NOTIMPL;
2960 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2961 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2963 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2965 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2967 return E_NOTIMPL;
2970 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2971 IAudioSessionControl2 *iface, GUID *group)
2973 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2975 FIXME("(%p)->(%p) - stub\n", This, group);
2977 return E_NOTIMPL;
2980 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2981 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2983 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2985 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2986 debugstr_guid(session));
2988 return E_NOTIMPL;
2991 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2992 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2994 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2996 FIXME("(%p)->(%p) - stub\n", This, events);
2998 return S_OK;
3001 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3002 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3004 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3006 FIXME("(%p)->(%p) - stub\n", This, events);
3008 return S_OK;
3011 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3012 IAudioSessionControl2 *iface, WCHAR **id)
3014 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3016 FIXME("(%p)->(%p) - stub\n", This, id);
3018 return E_NOTIMPL;
3021 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3022 IAudioSessionControl2 *iface, WCHAR **id)
3024 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3026 FIXME("(%p)->(%p) - stub\n", This, id);
3028 return E_NOTIMPL;
3031 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3032 IAudioSessionControl2 *iface, DWORD *pid)
3034 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3036 TRACE("(%p)->(%p)\n", This, pid);
3038 if(!pid)
3039 return E_POINTER;
3041 *pid = GetCurrentProcessId();
3043 return S_OK;
3046 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3047 IAudioSessionControl2 *iface)
3049 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3051 TRACE("(%p)\n", This);
3053 return S_FALSE;
3056 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3057 IAudioSessionControl2 *iface, BOOL optout)
3059 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3061 TRACE("(%p)->(%d)\n", This, optout);
3063 return S_OK;
3066 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3068 AudioSessionControl_QueryInterface,
3069 AudioSessionControl_AddRef,
3070 AudioSessionControl_Release,
3071 AudioSessionControl_GetState,
3072 AudioSessionControl_GetDisplayName,
3073 AudioSessionControl_SetDisplayName,
3074 AudioSessionControl_GetIconPath,
3075 AudioSessionControl_SetIconPath,
3076 AudioSessionControl_GetGroupingParam,
3077 AudioSessionControl_SetGroupingParam,
3078 AudioSessionControl_RegisterAudioSessionNotification,
3079 AudioSessionControl_UnregisterAudioSessionNotification,
3080 AudioSessionControl_GetSessionIdentifier,
3081 AudioSessionControl_GetSessionInstanceIdentifier,
3082 AudioSessionControl_GetProcessId,
3083 AudioSessionControl_IsSystemSoundsSession,
3084 AudioSessionControl_SetDuckingPreference
3087 /* index == -1 means set all channels, otherwise sets only the given channel */
3088 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
3090 Float32 level;
3091 OSStatus sc;
3093 if(This->session->mute)
3094 level = 0.;
3095 else{
3096 if(index == (UINT32)-1){
3097 UINT32 i;
3098 level = 1.;
3099 for(i = 0; i < This->fmt->nChannels; ++i){
3100 Float32 tmp;
3101 tmp = This->session->master_vol *
3102 This->session->channel_vols[i] * This->vols[i];
3103 level = tmp < level ? tmp : level;
3105 }else
3106 level = This->session->master_vol *
3107 This->session->channel_vols[index] * This->vols[index];
3110 sc = AudioUnitSetParameter(This->unit, kHALOutputParam_Volume,
3111 kAudioUnitScope_Global, 0, level, 0);
3112 if(sc != noErr)
3113 WARN("Couldn't set volume: %x\n", (int)sc);
3115 return S_OK;
3118 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
3120 HRESULT ret = S_OK;
3121 ACImpl *client;
3123 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
3124 HRESULT hr;
3125 hr = ca_setvol(client, index);
3126 if(FAILED(hr))
3127 ret = hr;
3130 return ret;
3133 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3134 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3136 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3138 if(!ppv)
3139 return E_POINTER;
3140 *ppv = NULL;
3142 if(IsEqualIID(riid, &IID_IUnknown) ||
3143 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3144 *ppv = iface;
3145 if(*ppv){
3146 IUnknown_AddRef((IUnknown*)*ppv);
3147 return S_OK;
3150 WARN("Unknown interface %s\n", debugstr_guid(riid));
3151 return E_NOINTERFACE;
3154 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3156 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3157 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3160 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3162 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3163 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3166 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3167 ISimpleAudioVolume *iface, float level, const GUID *context)
3169 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3170 AudioSession *session = This->session;
3171 HRESULT ret;
3173 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3175 if(level < 0.f || level > 1.f)
3176 return E_INVALIDARG;
3178 if(context)
3179 FIXME("Notifications not supported yet\n");
3181 EnterCriticalSection(&session->lock);
3183 session->master_vol = level;
3185 ret = ca_session_setvol(session, -1);
3187 LeaveCriticalSection(&session->lock);
3189 return ret;
3192 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3193 ISimpleAudioVolume *iface, float *level)
3195 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3196 AudioSession *session = This->session;
3198 TRACE("(%p)->(%p)\n", session, level);
3200 if(!level)
3201 return NULL_PTR_ERR;
3203 *level = session->master_vol;
3205 return S_OK;
3208 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3209 BOOL mute, const GUID *context)
3211 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3212 AudioSession *session = This->session;
3214 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3216 if(context)
3217 FIXME("Notifications not supported yet\n");
3219 EnterCriticalSection(&session->lock);
3221 session->mute = mute;
3223 ca_session_setvol(session, -1);
3225 LeaveCriticalSection(&session->lock);
3227 return S_OK;
3230 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3231 BOOL *mute)
3233 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3234 AudioSession *session = This->session;
3236 TRACE("(%p)->(%p)\n", session, mute);
3238 if(!mute)
3239 return NULL_PTR_ERR;
3241 *mute = session->mute;
3243 return S_OK;
3246 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3248 SimpleAudioVolume_QueryInterface,
3249 SimpleAudioVolume_AddRef,
3250 SimpleAudioVolume_Release,
3251 SimpleAudioVolume_SetMasterVolume,
3252 SimpleAudioVolume_GetMasterVolume,
3253 SimpleAudioVolume_SetMute,
3254 SimpleAudioVolume_GetMute
3257 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3258 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3260 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3262 if(!ppv)
3263 return E_POINTER;
3264 *ppv = NULL;
3266 if(IsEqualIID(riid, &IID_IUnknown) ||
3267 IsEqualIID(riid, &IID_IAudioStreamVolume))
3268 *ppv = iface;
3269 if(*ppv){
3270 IUnknown_AddRef((IUnknown*)*ppv);
3271 return S_OK;
3274 WARN("Unknown interface %s\n", debugstr_guid(riid));
3275 return E_NOINTERFACE;
3278 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3280 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3281 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3284 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3286 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3287 return IAudioClient3_Release(&This->IAudioClient3_iface);
3290 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3291 IAudioStreamVolume *iface, UINT32 *out)
3293 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3295 TRACE("(%p)->(%p)\n", This, out);
3297 if(!out)
3298 return E_POINTER;
3300 *out = This->fmt->nChannels;
3302 return S_OK;
3305 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3306 IAudioStreamVolume *iface, UINT32 index, float level)
3308 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3309 HRESULT ret;
3311 TRACE("(%p)->(%d, %f)\n", This, index, level);
3313 if(level < 0.f || level > 1.f)
3314 return E_INVALIDARG;
3316 if(index >= This->fmt->nChannels)
3317 return E_INVALIDARG;
3319 OSSpinLockLock(&This->lock);
3321 This->vols[index] = level;
3323 WARN("CoreAudio doesn't support per-channel volume control\n");
3324 ret = ca_setvol(This, index);
3326 OSSpinLockUnlock(&This->lock);
3328 return ret;
3331 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3332 IAudioStreamVolume *iface, UINT32 index, float *level)
3334 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3336 TRACE("(%p)->(%d, %p)\n", This, index, level);
3338 if(!level)
3339 return E_POINTER;
3341 if(index >= This->fmt->nChannels)
3342 return E_INVALIDARG;
3344 *level = This->vols[index];
3346 return S_OK;
3349 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3350 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3352 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3353 int i;
3354 HRESULT ret;
3356 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3358 if(!levels)
3359 return E_POINTER;
3361 if(count != This->fmt->nChannels)
3362 return E_INVALIDARG;
3364 OSSpinLockLock(&This->lock);
3366 for(i = 0; i < count; ++i)
3367 This->vols[i] = levels[i];
3369 ret = ca_setvol(This, -1);
3371 OSSpinLockUnlock(&This->lock);
3373 return ret;
3376 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3377 IAudioStreamVolume *iface, UINT32 count, float *levels)
3379 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3380 int i;
3382 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3384 if(!levels)
3385 return E_POINTER;
3387 if(count != This->fmt->nChannels)
3388 return E_INVALIDARG;
3390 OSSpinLockLock(&This->lock);
3392 for(i = 0; i < count; ++i)
3393 levels[i] = This->vols[i];
3395 OSSpinLockUnlock(&This->lock);
3397 return S_OK;
3400 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3402 AudioStreamVolume_QueryInterface,
3403 AudioStreamVolume_AddRef,
3404 AudioStreamVolume_Release,
3405 AudioStreamVolume_GetChannelCount,
3406 AudioStreamVolume_SetChannelVolume,
3407 AudioStreamVolume_GetChannelVolume,
3408 AudioStreamVolume_SetAllVolumes,
3409 AudioStreamVolume_GetAllVolumes
3412 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3413 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3415 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3417 if(!ppv)
3418 return E_POINTER;
3419 *ppv = NULL;
3421 if(IsEqualIID(riid, &IID_IUnknown) ||
3422 IsEqualIID(riid, &IID_IChannelAudioVolume))
3423 *ppv = iface;
3424 if(*ppv){
3425 IUnknown_AddRef((IUnknown*)*ppv);
3426 return S_OK;
3429 WARN("Unknown interface %s\n", debugstr_guid(riid));
3430 return E_NOINTERFACE;
3433 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3435 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3436 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3439 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3441 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3442 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3445 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3446 IChannelAudioVolume *iface, UINT32 *out)
3448 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3449 AudioSession *session = This->session;
3451 TRACE("(%p)->(%p)\n", session, out);
3453 if(!out)
3454 return NULL_PTR_ERR;
3456 *out = session->channel_count;
3458 return S_OK;
3461 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3462 IChannelAudioVolume *iface, UINT32 index, float level,
3463 const GUID *context)
3465 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3466 AudioSession *session = This->session;
3467 HRESULT ret;
3469 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3470 wine_dbgstr_guid(context));
3472 if(level < 0.f || level > 1.f)
3473 return E_INVALIDARG;
3475 if(index >= session->channel_count)
3476 return E_INVALIDARG;
3478 if(context)
3479 FIXME("Notifications not supported yet\n");
3481 EnterCriticalSection(&session->lock);
3483 session->channel_vols[index] = level;
3485 WARN("CoreAudio doesn't support per-channel volume control\n");
3486 ret = ca_session_setvol(session, index);
3488 LeaveCriticalSection(&session->lock);
3490 return ret;
3493 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3494 IChannelAudioVolume *iface, UINT32 index, float *level)
3496 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3497 AudioSession *session = This->session;
3499 TRACE("(%p)->(%d, %p)\n", session, index, level);
3501 if(!level)
3502 return NULL_PTR_ERR;
3504 if(index >= session->channel_count)
3505 return E_INVALIDARG;
3507 *level = session->channel_vols[index];
3509 return S_OK;
3512 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3513 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3514 const GUID *context)
3516 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3517 AudioSession *session = This->session;
3518 int i;
3519 HRESULT ret;
3521 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3522 wine_dbgstr_guid(context));
3524 if(!levels)
3525 return NULL_PTR_ERR;
3527 if(count != session->channel_count)
3528 return E_INVALIDARG;
3530 if(context)
3531 FIXME("Notifications not supported yet\n");
3533 EnterCriticalSection(&session->lock);
3535 for(i = 0; i < count; ++i)
3536 session->channel_vols[i] = levels[i];
3538 ret = ca_session_setvol(session, -1);
3540 LeaveCriticalSection(&session->lock);
3542 return ret;
3545 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3546 IChannelAudioVolume *iface, UINT32 count, float *levels)
3548 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3549 AudioSession *session = This->session;
3550 int i;
3552 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3554 if(!levels)
3555 return NULL_PTR_ERR;
3557 if(count != session->channel_count)
3558 return E_INVALIDARG;
3560 for(i = 0; i < count; ++i)
3561 levels[i] = session->channel_vols[i];
3563 return S_OK;
3566 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3568 ChannelAudioVolume_QueryInterface,
3569 ChannelAudioVolume_AddRef,
3570 ChannelAudioVolume_Release,
3571 ChannelAudioVolume_GetChannelCount,
3572 ChannelAudioVolume_SetChannelVolume,
3573 ChannelAudioVolume_GetChannelVolume,
3574 ChannelAudioVolume_SetAllVolumes,
3575 ChannelAudioVolume_GetAllVolumes
3578 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3579 REFIID riid, void **ppv)
3581 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3583 if(!ppv)
3584 return E_POINTER;
3585 *ppv = NULL;
3587 if(IsEqualIID(riid, &IID_IUnknown) ||
3588 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3589 IsEqualIID(riid, &IID_IAudioSessionManager2))
3590 *ppv = iface;
3591 if(*ppv){
3592 IUnknown_AddRef((IUnknown*)*ppv);
3593 return S_OK;
3596 WARN("Unknown interface %s\n", debugstr_guid(riid));
3597 return E_NOINTERFACE;
3600 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3602 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3603 ULONG ref;
3604 ref = InterlockedIncrement(&This->ref);
3605 TRACE("(%p) Refcount now %u\n", This, ref);
3606 return ref;
3609 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3611 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3612 ULONG ref;
3613 ref = InterlockedDecrement(&This->ref);
3614 TRACE("(%p) Refcount now %u\n", This, ref);
3615 if(!ref)
3616 HeapFree(GetProcessHeap(), 0, This);
3617 return ref;
3620 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3621 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3622 IAudioSessionControl **out)
3624 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3625 AudioSession *session;
3626 AudioSessionWrapper *wrapper;
3627 HRESULT hr;
3629 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3630 flags, out);
3632 hr = get_audio_session(session_guid, This->device, 0, &session);
3633 if(FAILED(hr))
3634 return hr;
3636 wrapper = AudioSessionWrapper_Create(NULL);
3637 if(!wrapper)
3638 return E_OUTOFMEMORY;
3640 wrapper->session = session;
3642 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3644 return S_OK;
3647 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3648 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3649 ISimpleAudioVolume **out)
3651 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3652 AudioSession *session;
3653 AudioSessionWrapper *wrapper;
3654 HRESULT hr;
3656 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3657 flags, out);
3659 hr = get_audio_session(session_guid, This->device, 0, &session);
3660 if(FAILED(hr))
3661 return hr;
3663 wrapper = AudioSessionWrapper_Create(NULL);
3664 if(!wrapper)
3665 return E_OUTOFMEMORY;
3667 wrapper->session = session;
3669 *out = &wrapper->ISimpleAudioVolume_iface;
3671 return S_OK;
3674 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3675 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3677 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3678 FIXME("(%p)->(%p) - stub\n", This, out);
3679 return E_NOTIMPL;
3682 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3683 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3685 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3686 FIXME("(%p)->(%p) - stub\n", This, notification);
3687 return E_NOTIMPL;
3690 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3691 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3693 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3694 FIXME("(%p)->(%p) - stub\n", This, notification);
3695 return E_NOTIMPL;
3698 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3699 IAudioSessionManager2 *iface, const WCHAR *session_id,
3700 IAudioVolumeDuckNotification *notification)
3702 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3703 FIXME("(%p)->(%p) - stub\n", This, notification);
3704 return E_NOTIMPL;
3707 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3708 IAudioSessionManager2 *iface,
3709 IAudioVolumeDuckNotification *notification)
3711 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3712 FIXME("(%p)->(%p) - stub\n", This, notification);
3713 return E_NOTIMPL;
3716 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3718 AudioSessionManager_QueryInterface,
3719 AudioSessionManager_AddRef,
3720 AudioSessionManager_Release,
3721 AudioSessionManager_GetAudioSessionControl,
3722 AudioSessionManager_GetSimpleAudioVolume,
3723 AudioSessionManager_GetSessionEnumerator,
3724 AudioSessionManager_RegisterSessionNotification,
3725 AudioSessionManager_UnregisterSessionNotification,
3726 AudioSessionManager_RegisterDuckNotification,
3727 AudioSessionManager_UnregisterDuckNotification
3730 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3731 IAudioSessionManager2 **out)
3733 SessionMgr *This;
3735 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3736 if(!This)
3737 return E_OUTOFMEMORY;
3739 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3740 This->device = device;
3741 This->ref = 1;
3743 *out = &This->IAudioSessionManager2_iface;
3745 return S_OK;