urlmon: Added Seek implementations for streams using cache file.
[wine.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobc362a5142ff1680021561aa9cff1c5cfa1e14c38
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
53 #undef DPRINTF
55 #include "windef.h"
56 #include "winbase.h"
57 #include "winnls.h"
58 #include "winreg.h"
59 #include "wine/debug.h"
60 #include "wine/unicode.h"
61 #include "wine/list.h"
63 #include "ole2.h"
64 #include "mmdeviceapi.h"
65 #include "devpkey.h"
66 #include "dshow.h"
67 #include "dsound.h"
69 #include "initguid.h"
70 #include "endpointvolume.h"
71 #include "audioclient.h"
72 #include "audiopolicy.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
76 #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H
77 /* Define new AudioComponent Manager functions for OSX 10.5 */
78 typedef Component AudioComponent;
79 typedef ComponentDescription AudioComponentDescription;
80 typedef ComponentInstance AudioComponentInstance;
82 static inline AudioComponent AudioComponentFindNext(AudioComponent ac, AudioComponentDescription *desc)
84 return FindNextComponent(ac, desc);
87 static inline OSStatus AudioComponentInstanceNew(AudioComponent ac, AudioComponentInstance *aci)
89 return OpenAComponent(ac, aci);
92 static inline OSStatus AudioComponentInstanceDispose(AudioComponentInstance aci)
94 return CloseComponent(aci);
96 #endif
98 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
100 static const REFERENCE_TIME DefaultPeriod = 100000;
101 static const REFERENCE_TIME MinimumPeriod = 50000;
103 struct ACImpl;
104 typedef struct ACImpl ACImpl;
106 typedef struct _AudioSession {
107 GUID guid;
108 struct list clients;
110 IMMDevice *device;
112 float master_vol;
113 UINT32 channel_count;
114 float *channel_vols;
115 BOOL mute;
117 CRITICAL_SECTION lock;
119 struct list entry;
120 } AudioSession;
122 typedef struct _AudioSessionWrapper {
123 IAudioSessionControl2 IAudioSessionControl2_iface;
124 IChannelAudioVolume IChannelAudioVolume_iface;
125 ISimpleAudioVolume ISimpleAudioVolume_iface;
127 LONG ref;
129 ACImpl *client;
130 AudioSession *session;
131 } AudioSessionWrapper;
133 struct ACImpl {
134 IAudioClient IAudioClient_iface;
135 IAudioRenderClient IAudioRenderClient_iface;
136 IAudioCaptureClient IAudioCaptureClient_iface;
137 IAudioClock IAudioClock_iface;
138 IAudioClock2 IAudioClock2_iface;
139 IAudioStreamVolume IAudioStreamVolume_iface;
141 LONG ref;
143 IMMDevice *parent;
144 IUnknown *pUnkFTMarshal;
146 WAVEFORMATEX *fmt;
148 EDataFlow dataflow;
149 DWORD flags;
150 AUDCLNT_SHAREMODE share;
151 HANDLE event;
152 float *vols;
154 BOOL initted;
155 AudioDeviceID adevid;
156 AudioObjectPropertyScope scope;
157 AudioConverterRef converter;
158 AudioComponentInstance unit;
159 AudioStreamBasicDescription dev_desc; /* audio unit format, not necessarily the same as fmt */
160 HANDLE timer;
161 UINT32 period_ms, bufsize_frames, period_frames;
162 UINT64 written_frames;
163 UINT32 lcl_offs_frames, wri_offs_frames, held_frames, tmp_buffer_frames;
164 UINT32 cap_bufsize_frames, cap_offs_frames, cap_held_frames, wrap_bufsize_frames, resamp_bufsize_frames;
165 INT32 getbuf_last;
166 BOOL playing;
167 BYTE *cap_buffer, *wrap_buffer, *resamp_buffer, *local_buffer, *tmp_buffer;
169 AudioSession *session;
170 AudioSessionWrapper *session_wrapper;
172 struct list entry;
174 OSSpinLock lock;
177 static const IAudioClientVtbl AudioClient_Vtbl;
178 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
179 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
180 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
181 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
182 static const IAudioClockVtbl AudioClock_Vtbl;
183 static const IAudioClock2Vtbl AudioClock2_Vtbl;
184 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
185 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
186 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
188 typedef struct _SessionMgr {
189 IAudioSessionManager2 IAudioSessionManager2_iface;
191 LONG ref;
193 IMMDevice *device;
194 } SessionMgr;
196 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
197 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
198 'w','i','n','e','c','o','r','e','a','u','d','i','o','.','d','r','v','\\','d','e','v','i','c','e','s',0};
199 static const WCHAR guidW[] = {'g','u','i','d',0};
201 static HANDLE g_timer_q;
203 static CRITICAL_SECTION g_sessions_lock;
204 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
206 0, 0, &g_sessions_lock,
207 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
208 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
210 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
211 static struct list g_sessions = LIST_INIT(g_sessions);
213 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
214 static HRESULT ca_setvol(ACImpl *This, UINT32 index);
216 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
218 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
221 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
223 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
226 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
228 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
231 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
233 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
236 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
238 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
241 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
243 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
246 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
248 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
251 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
253 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
256 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
258 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
261 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
263 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
266 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
268 switch (reason)
270 case DLL_PROCESS_ATTACH:
271 g_timer_q = CreateTimerQueue();
272 if(!g_timer_q)
273 return FALSE;
274 break;
276 case DLL_PROCESS_DETACH:
277 if (reserved) break;
278 DeleteCriticalSection(&g_sessions_lock);
279 break;
281 return TRUE;
284 /* From <dlls/mmdevapi/mmdevapi.h> */
285 enum DriverPriority {
286 Priority_Unavailable = 0,
287 Priority_Low,
288 Priority_Neutral,
289 Priority_Preferred
292 int WINAPI AUDDRV_GetPriority(void)
294 return Priority_Neutral;
297 static HRESULT osstatus_to_hresult(OSStatus sc)
299 switch(sc){
300 case kAudioFormatUnsupportedDataFormatError:
301 case kAudioFormatUnknownFormatError:
302 case kAudioDeviceUnsupportedFormatError:
303 return AUDCLNT_E_UNSUPPORTED_FORMAT;
304 case kAudioHardwareBadDeviceError:
305 return AUDCLNT_E_DEVICE_INVALIDATED;
307 return E_FAIL;
310 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
311 GUID *guid)
313 HKEY key;
314 BOOL opened = FALSE;
315 LONG lr;
317 if(!drv_key){
318 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
319 NULL, &drv_key, NULL);
320 if(lr != ERROR_SUCCESS){
321 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
322 return;
324 opened = TRUE;
327 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
328 NULL, &key, NULL);
329 if(lr != ERROR_SUCCESS){
330 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
331 goto exit;
334 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
335 sizeof(GUID));
336 if(lr != ERROR_SUCCESS)
337 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
339 RegCloseKey(key);
340 exit:
341 if(opened)
342 RegCloseKey(drv_key);
345 static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid)
347 HKEY key = NULL, dev_key;
348 DWORD type, size = sizeof(*guid);
349 WCHAR key_name[256];
351 static const WCHAR key_fmt[] = {'%','u',0};
353 if(flow == eCapture)
354 key_name[0] = '1';
355 else
356 key_name[0] = '0';
357 key_name[1] = ',';
359 sprintfW(key_name + 2, key_fmt, device);
361 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
362 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
363 if(RegQueryValueExW(dev_key, guidW, 0, &type,
364 (BYTE*)guid, &size) == ERROR_SUCCESS){
365 if(type == REG_BINARY){
366 RegCloseKey(dev_key);
367 RegCloseKey(key);
368 return;
370 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
371 wine_dbgstr_w(key_name), type);
373 RegCloseKey(dev_key);
377 CoCreateGuid(guid);
379 set_device_guid(flow, key, key_name, guid);
381 if(key)
382 RegCloseKey(key);
385 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
386 GUID **guids, UINT *num, UINT *def_index)
388 UInt32 devsize, size;
389 AudioDeviceID *devices;
390 AudioDeviceID default_id;
391 AudioObjectPropertyAddress addr;
392 OSStatus sc;
393 int i, ndevices;
395 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
397 addr.mScope = kAudioObjectPropertyScopeGlobal;
398 addr.mElement = kAudioObjectPropertyElementMaster;
399 if(flow == eRender)
400 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
401 else if(flow == eCapture)
402 addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
403 else
404 return E_INVALIDARG;
406 size = sizeof(default_id);
407 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
408 NULL, &size, &default_id);
409 if(sc != noErr){
410 WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc);
411 default_id = -1;
414 addr.mSelector = kAudioHardwarePropertyDevices;
415 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0,
416 NULL, &devsize);
417 if(sc != noErr){
418 WARN("Getting _Devices property size failed: %x\n", (int)sc);
419 return osstatus_to_hresult(sc);
422 devices = HeapAlloc(GetProcessHeap(), 0, devsize);
423 if(!devices)
424 return E_OUTOFMEMORY;
426 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
427 &devsize, devices);
428 if(sc != noErr){
429 WARN("Getting _Devices property failed: %x\n", (int)sc);
430 HeapFree(GetProcessHeap(), 0, devices);
431 return osstatus_to_hresult(sc);
434 ndevices = devsize / sizeof(AudioDeviceID);
436 *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
437 if(!*ids){
438 HeapFree(GetProcessHeap(), 0, devices);
439 return E_OUTOFMEMORY;
442 *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
443 if(!*guids){
444 HeapFree(GetProcessHeap(), 0, *ids);
445 HeapFree(GetProcessHeap(), 0, devices);
446 return E_OUTOFMEMORY;
449 *num = 0;
450 *def_index = (UINT)-1;
451 for(i = 0; i < ndevices; ++i){
452 AudioBufferList *buffers;
453 CFStringRef name;
454 SIZE_T len;
455 int j;
457 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
458 if(flow == eRender)
459 addr.mScope = kAudioDevicePropertyScopeOutput;
460 else
461 addr.mScope = kAudioDevicePropertyScopeInput;
462 addr.mElement = 0;
463 sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size);
464 if(sc != noErr){
465 WARN("Unable to get _StreamConfiguration property size for "
466 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
467 continue;
470 buffers = HeapAlloc(GetProcessHeap(), 0, size);
471 if(!buffers){
472 HeapFree(GetProcessHeap(), 0, devices);
473 for(j = 0; j < *num; ++j)
474 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
475 HeapFree(GetProcessHeap(), 0, *guids);
476 HeapFree(GetProcessHeap(), 0, *ids);
477 return E_OUTOFMEMORY;
480 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
481 &size, buffers);
482 if(sc != noErr){
483 WARN("Unable to get _StreamConfiguration property for "
484 "device %u: %x\n", (unsigned int)devices[i], (int)sc);
485 HeapFree(GetProcessHeap(), 0, buffers);
486 continue;
489 /* check that there's at least one channel in this device before
490 * we claim it as usable */
491 for(j = 0; j < buffers->mNumberBuffers; ++j)
492 if(buffers->mBuffers[j].mNumberChannels > 0)
493 break;
494 if(j >= buffers->mNumberBuffers){
495 HeapFree(GetProcessHeap(), 0, buffers);
496 continue;
499 HeapFree(GetProcessHeap(), 0, buffers);
501 size = sizeof(name);
502 addr.mSelector = kAudioObjectPropertyName;
503 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL,
504 &size, &name);
505 if(sc != noErr){
506 WARN("Unable to get _Name property for device %u: %x\n",
507 (unsigned int)devices[i], (int)sc);
508 continue;
511 len = CFStringGetLength(name) + 1;
512 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
513 if(!(*ids)[*num]){
514 CFRelease(name);
515 HeapFree(GetProcessHeap(), 0, devices);
516 for(j = 0; j < *num; ++j)
517 HeapFree(GetProcessHeap(), 0, (*ids)[j]);
518 HeapFree(GetProcessHeap(), 0, *ids);
519 HeapFree(GetProcessHeap(), 0, *guids);
520 return E_OUTOFMEMORY;
522 CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]);
523 ((*ids)[*num])[len - 1] = 0;
524 CFRelease(name);
526 get_device_guid(flow, devices[i], &(*guids)[*num]);
528 if(*def_index == (UINT)-1 && devices[i] == default_id)
529 *def_index = *num;
531 TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]),
532 (unsigned int)devices[i], (*def_index == *num) ? " (default)" : "");
534 (*num)++;
537 if(*def_index == (UINT)-1)
538 *def_index = 0;
540 HeapFree(GetProcessHeap(), 0, devices);
542 return S_OK;
545 static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow)
547 HKEY devices_key;
548 UINT i = 0;
549 WCHAR key_name[256];
550 DWORD key_name_size;
552 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
553 ERR("No devices in registry?\n");
554 return FALSE;
557 while(1){
558 HKEY key;
559 DWORD size, type;
560 GUID reg_guid;
562 key_name_size = sizeof(key_name);
563 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
564 NULL, NULL, NULL) != ERROR_SUCCESS)
565 break;
567 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
568 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
569 continue;
572 size = sizeof(reg_guid);
573 if(RegQueryValueExW(key, guidW, 0, &type,
574 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
575 if(IsEqualGUID(&reg_guid, guid)){
576 RegCloseKey(key);
577 RegCloseKey(devices_key);
579 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
581 if(key_name[0] == '0')
582 *flow = eRender;
583 else if(key_name[0] == '1')
584 *flow = eCapture;
585 else{
586 ERR("Unknown device type: %c\n", key_name[0]);
587 return FALSE;
590 *id = strtoulW(key_name + 2, NULL, 10);
592 return TRUE;
596 RegCloseKey(key);
599 RegCloseKey(devices_key);
601 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
603 return FALSE;
606 static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
608 AudioComponentInstance unit;
609 AudioComponent comp;
610 AudioComponentDescription desc;
611 OSStatus sc;
613 memset(&desc, 0, sizeof(desc));
614 desc.componentType = kAudioUnitType_Output;
615 desc.componentSubType = kAudioUnitSubType_HALOutput;
616 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
618 if(!(comp = AudioComponentFindNext(NULL, &desc))){
619 WARN("AudioComponentFindNext failed\n");
620 return NULL;
623 sc = AudioComponentInstanceNew(comp, &unit);
624 if(sc != noErr){
625 WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
626 return NULL;
629 if(dataflow == eCapture){
630 UInt32 enableio;
632 enableio = 1;
633 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
634 kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
635 if(sc != noErr){
636 WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
637 AudioComponentInstanceDispose(unit);
638 return NULL;
641 enableio = 0;
642 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
643 kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
644 if(sc != noErr){
645 WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
646 AudioComponentInstanceDispose(unit);
647 return NULL;
651 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
652 kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
653 if(sc != noErr){
654 WARN("Couldn't set audio unit device\n");
655 AudioComponentInstanceDispose(unit);
656 return NULL;
659 return unit;
662 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
664 ACImpl *This;
665 AudioDeviceID adevid;
666 EDataFlow dataflow;
667 HRESULT hr;
669 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
671 if(!get_deviceid_by_guid(guid, &adevid, &dataflow))
672 return AUDCLNT_E_DEVICE_INVALIDATED;
674 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
675 if(!This)
676 return E_OUTOFMEMORY;
678 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
679 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
680 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
681 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
682 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
683 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
685 This->dataflow = dataflow;
687 if(dataflow == eRender)
688 This->scope = kAudioDevicePropertyScopeOutput;
689 else if(dataflow == eCapture)
690 This->scope = kAudioDevicePropertyScopeInput;
691 else{
692 HeapFree(GetProcessHeap(), 0, This);
693 return E_INVALIDARG;
696 This->lock = 0;
698 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
699 (IUnknown **)&This->pUnkFTMarshal);
700 if (FAILED(hr)) {
701 HeapFree(GetProcessHeap(), 0, This);
702 return hr;
705 This->parent = dev;
706 IMMDevice_AddRef(This->parent);
708 This->adevid = adevid;
710 if(!(This->unit = get_audiounit(This->dataflow, This->adevid))){
711 HeapFree(GetProcessHeap(), 0, This);
712 return AUDCLNT_E_DEVICE_INVALIDATED;
715 *out = &This->IAudioClient_iface;
716 IAudioClient_AddRef(&This->IAudioClient_iface);
718 return S_OK;
721 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
722 REFIID riid, void **ppv)
724 ACImpl *This = impl_from_IAudioClient(iface);
725 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
727 if(!ppv)
728 return E_POINTER;
729 *ppv = NULL;
730 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
731 *ppv = iface;
732 else if(IsEqualIID(riid, &IID_IMarshal))
733 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
735 if(*ppv){
736 IUnknown_AddRef((IUnknown*)*ppv);
737 return S_OK;
739 WARN("Unknown interface %s\n", debugstr_guid(riid));
740 return E_NOINTERFACE;
743 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
745 ACImpl *This = impl_from_IAudioClient(iface);
746 ULONG ref;
747 ref = InterlockedIncrement(&This->ref);
748 TRACE("(%p) Refcount now %u\n", This, ref);
749 return ref;
752 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
754 ACImpl *This = impl_from_IAudioClient(iface);
755 ULONG ref;
756 ref = InterlockedDecrement(&This->ref);
757 TRACE("(%p) Refcount now %u\n", This, ref);
758 if(!ref){
759 if(This->timer){
760 HANDLE event;
761 BOOL wait;
762 event = CreateEventW(NULL, TRUE, FALSE, NULL);
763 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
764 wait = wait && GetLastError() == ERROR_IO_PENDING;
765 if(event && wait)
766 WaitForSingleObject(event, INFINITE);
767 CloseHandle(event);
769 AudioOutputUnitStop(This->unit);
770 AudioComponentInstanceDispose(This->unit);
771 if(This->converter)
772 AudioConverterDispose(This->converter);
773 if(This->session){
774 EnterCriticalSection(&g_sessions_lock);
775 list_remove(&This->entry);
776 LeaveCriticalSection(&g_sessions_lock);
778 HeapFree(GetProcessHeap(), 0, This->vols);
779 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
780 HeapFree(GetProcessHeap(), 0, This->cap_buffer);
781 HeapFree(GetProcessHeap(), 0, This->local_buffer);
782 free(This->wrap_buffer);
783 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
784 CoTaskMemFree(This->fmt);
785 IMMDevice_Release(This->parent);
786 IUnknown_Release(This->pUnkFTMarshal);
787 HeapFree(GetProcessHeap(), 0, This);
789 return ref;
792 static void dump_fmt(const WAVEFORMATEX *fmt)
794 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
795 switch(fmt->wFormatTag){
796 case WAVE_FORMAT_PCM:
797 TRACE("WAVE_FORMAT_PCM");
798 break;
799 case WAVE_FORMAT_IEEE_FLOAT:
800 TRACE("WAVE_FORMAT_IEEE_FLOAT");
801 break;
802 case WAVE_FORMAT_EXTENSIBLE:
803 TRACE("WAVE_FORMAT_EXTENSIBLE");
804 break;
805 default:
806 TRACE("Unknown");
807 break;
809 TRACE(")\n");
811 TRACE("nChannels: %u\n", fmt->nChannels);
812 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
813 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
814 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
815 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
816 TRACE("cbSize: %u\n", fmt->cbSize);
818 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
819 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
820 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
821 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
822 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
826 static DWORD get_channel_mask(unsigned int channels)
828 switch(channels){
829 case 0:
830 return 0;
831 case 1:
832 return KSAUDIO_SPEAKER_MONO;
833 case 2:
834 return KSAUDIO_SPEAKER_STEREO;
835 case 3:
836 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
837 case 4:
838 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
839 case 5:
840 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
841 case 6:
842 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
843 case 7:
844 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
845 case 8:
846 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
848 FIXME("Unknown speaker configuration: %u\n", channels);
849 return 0;
852 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
854 WAVEFORMATEX *ret;
855 size_t size;
857 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
858 size = sizeof(WAVEFORMATEXTENSIBLE);
859 else
860 size = sizeof(WAVEFORMATEX);
862 ret = CoTaskMemAlloc(size);
863 if(!ret)
864 return NULL;
866 memcpy(ret, fmt, size);
868 ret->cbSize = size - sizeof(WAVEFORMATEX);
870 return ret;
873 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
874 const WAVEFORMATEX *fmt)
876 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
878 desc->mFormatFlags = 0;
880 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
881 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
882 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
883 desc->mFormatID = kAudioFormatLinearPCM;
884 if(fmt->wBitsPerSample > 8)
885 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
886 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
887 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
888 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
889 desc->mFormatID = kAudioFormatLinearPCM;
890 desc->mFormatFlags = kAudioFormatFlagIsFloat;
891 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
892 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
893 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
894 desc->mFormatID = kAudioFormatULaw;
895 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
896 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
897 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
898 desc->mFormatID = kAudioFormatALaw;
899 }else
900 return AUDCLNT_E_UNSUPPORTED_FORMAT;
902 desc->mSampleRate = fmt->nSamplesPerSec;
903 desc->mBytesPerPacket = fmt->nBlockAlign;
904 desc->mFramesPerPacket = 1;
905 desc->mBytesPerFrame = fmt->nBlockAlign;
906 desc->mChannelsPerFrame = fmt->nChannels;
907 desc->mBitsPerChannel = fmt->wBitsPerSample;
908 desc->mReserved = 0;
910 return S_OK;
913 static void session_init_vols(AudioSession *session, UINT channels)
915 if(session->channel_count < channels){
916 UINT i;
918 if(session->channel_vols)
919 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
920 session->channel_vols, sizeof(float) * channels);
921 else
922 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
923 sizeof(float) * channels);
924 if(!session->channel_vols)
925 return;
927 for(i = session->channel_count; i < channels; ++i)
928 session->channel_vols[i] = 1.f;
930 session->channel_count = channels;
934 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
935 UINT num_channels)
937 AudioSession *ret;
939 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
940 if(!ret)
941 return NULL;
943 memcpy(&ret->guid, guid, sizeof(GUID));
945 ret->device = device;
947 list_init(&ret->clients);
949 list_add_head(&g_sessions, &ret->entry);
951 InitializeCriticalSection(&ret->lock);
952 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
954 session_init_vols(ret, num_channels);
956 ret->master_vol = 1.f;
958 return ret;
961 /* if channels == 0, then this will return or create a session with
962 * matching dataflow and GUID. otherwise, channels must also match */
963 static HRESULT get_audio_session(const GUID *sessionguid,
964 IMMDevice *device, UINT channels, AudioSession **out)
966 AudioSession *session;
968 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
969 *out = create_session(&GUID_NULL, device, channels);
970 if(!*out)
971 return E_OUTOFMEMORY;
973 return S_OK;
976 *out = NULL;
977 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
978 if(session->device == device &&
979 IsEqualGUID(sessionguid, &session->guid)){
980 session_init_vols(session, channels);
981 *out = session;
982 break;
986 if(!*out){
987 *out = create_session(sessionguid, device, channels);
988 if(!*out)
989 return E_OUTOFMEMORY;
992 return S_OK;
995 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
996 BYTE *src, UINT32 src_bytes)
998 UINT32 chunk_bytes = dst_bytes - dst_offs;
1000 if(chunk_bytes < src_bytes){
1001 memcpy(dst + dst_offs, src, chunk_bytes);
1002 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
1003 }else
1004 memcpy(dst + dst_offs, src, src_bytes);
1007 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1009 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1010 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1011 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1012 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1013 This->fmt->wBitsPerSample == 8)
1014 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1015 else
1016 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1019 /* CA is pulling data from us */
1020 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
1021 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1022 AudioBufferList *data)
1024 ACImpl *This = user;
1025 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
1027 OSSpinLockLock(&This->lock);
1029 if(This->playing){
1030 lcl_offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1031 to_copy_frames = min(nframes, This->held_frames);
1032 to_copy_bytes = to_copy_frames * This->fmt->nBlockAlign;
1034 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) * This->fmt->nBlockAlign;
1036 if(to_copy_bytes > chunk_bytes){
1037 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, chunk_bytes);
1038 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, This->local_buffer, to_copy_bytes - chunk_bytes);
1039 }else
1040 memcpy(data->mBuffers[0].mData, This->local_buffer + lcl_offs_bytes, to_copy_bytes);
1042 This->lcl_offs_frames += to_copy_frames;
1043 This->lcl_offs_frames %= This->bufsize_frames;
1044 This->held_frames -= to_copy_frames;
1045 }else
1046 to_copy_bytes = to_copy_frames = 0;
1048 if(nframes > to_copy_frames)
1049 silence_buffer(This, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
1051 OSSpinLockUnlock(&This->lock);
1053 return noErr;
1056 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1058 if(left <= right)
1059 return right - left;
1060 return bufsize - (left - right);
1063 /* place data from cap_buffer into provided AudioBufferList */
1064 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1065 AudioStreamPacketDescription **packets, void *user)
1067 ACImpl *This = user;
1069 *nframes = min(*nframes, This->cap_held_frames);
1070 if(!*nframes){
1071 data->mBuffers[0].mData = NULL;
1072 data->mBuffers[0].mDataByteSize = 0;
1073 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1074 return noErr;
1077 data->mBuffers[0].mDataByteSize = *nframes * This->fmt->nBlockAlign;
1078 data->mBuffers[0].mNumberChannels = This->fmt->nChannels;
1080 if(This->cap_offs_frames + *nframes > This->cap_bufsize_frames){
1081 UINT32 chunk_frames = This->cap_bufsize_frames - This->cap_offs_frames;
1083 if(This->wrap_bufsize_frames < *nframes){
1084 free(This->wrap_buffer);
1085 This->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1086 This->wrap_bufsize_frames = *nframes;
1089 memcpy(This->wrap_buffer, This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign,
1090 chunk_frames * This->fmt->nBlockAlign);
1091 memcpy(This->wrap_buffer + chunk_frames * This->fmt->nBlockAlign, This->cap_buffer,
1092 (*nframes - chunk_frames) * This->fmt->nBlockAlign);
1094 data->mBuffers[0].mData = This->wrap_buffer;
1095 }else
1096 data->mBuffers[0].mData = This->cap_buffer + This->cap_offs_frames * This->fmt->nBlockAlign;
1098 This->cap_offs_frames += *nframes;
1099 This->cap_offs_frames %= This->cap_bufsize_frames;
1100 This->cap_held_frames -= *nframes;
1102 if(packets)
1103 *packets = NULL;
1105 return noErr;
1108 static void capture_resample(ACImpl *This)
1110 UINT32 resamp_period_frames = MulDiv(This->period_frames, This->dev_desc.mSampleRate, This->fmt->nSamplesPerSec);
1111 OSStatus sc;
1113 /* the resampling process often needs more source frames than we'd
1114 * guess from a straight conversion using the sample rate ratio. so
1115 * only convert if we have extra source data. */
1116 while(This->cap_held_frames > resamp_period_frames * 2){
1117 AudioBufferList converted_list;
1118 UInt32 wanted_frames = This->period_frames;
1120 converted_list.mNumberBuffers = 1;
1121 converted_list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1122 converted_list.mBuffers[0].mDataByteSize = wanted_frames * This->fmt->nBlockAlign;
1124 if(This->resamp_bufsize_frames < wanted_frames){
1125 HeapFree(GetProcessHeap(), 0, This->resamp_buffer);
1126 This->resamp_buffer = HeapAlloc(GetProcessHeap(), 0, converted_list.mBuffers[0].mDataByteSize);
1127 This->resamp_bufsize_frames = wanted_frames;
1130 converted_list.mBuffers[0].mData = This->resamp_buffer;
1132 sc = AudioConverterFillComplexBuffer(This->converter, feed_cb,
1133 This, &wanted_frames, &converted_list, NULL);
1134 if(sc != noErr){
1135 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1136 break;
1139 ca_wrap_buffer(This->local_buffer,
1140 This->wri_offs_frames * This->fmt->nBlockAlign,
1141 This->bufsize_frames * This->fmt->nBlockAlign,
1142 This->resamp_buffer, wanted_frames * This->fmt->nBlockAlign);
1144 This->wri_offs_frames += wanted_frames;
1145 This->wri_offs_frames %= This->bufsize_frames;
1146 if(This->held_frames + wanted_frames > This->bufsize_frames){
1147 This->lcl_offs_frames += buf_ptr_diff(This->lcl_offs_frames,
1148 This->wri_offs_frames, This->bufsize_frames);
1149 This->held_frames = This->bufsize_frames;
1150 }else
1151 This->held_frames += wanted_frames;
1155 /* we need to trigger CA to pull data from the device and give it to us
1157 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
1159 * raw data is resampled from cap_buffer into resamp_buffer in period-size
1160 * chunks and copied to local_buffer
1162 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
1163 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
1164 AudioBufferList *data)
1166 ACImpl *This = user;
1167 AudioBufferList list;
1168 OSStatus sc;
1169 UINT32 cap_wri_offs_frames;
1171 OSSpinLockLock(&This->lock);
1173 cap_wri_offs_frames = (This->cap_offs_frames + This->cap_held_frames) % This->cap_bufsize_frames;
1175 list.mNumberBuffers = 1;
1176 list.mBuffers[0].mNumberChannels = This->fmt->nChannels;
1177 list.mBuffers[0].mDataByteSize = nframes * This->fmt->nBlockAlign;
1179 if(!This->playing || cap_wri_offs_frames + nframes > This->cap_bufsize_frames){
1180 if(This->wrap_bufsize_frames < nframes){
1181 free(This->wrap_buffer);
1182 This->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
1183 This->wrap_bufsize_frames = nframes;
1186 list.mBuffers[0].mData = This->wrap_buffer;
1187 }else
1188 list.mBuffers[0].mData = This->cap_buffer + cap_wri_offs_frames * This->fmt->nBlockAlign;
1190 sc = AudioUnitRender(This->unit, flags, ts, bus, nframes, &list);
1191 if(sc != noErr){
1192 OSSpinLockUnlock(&This->lock);
1193 return sc;
1196 if(This->playing){
1197 if(list.mBuffers[0].mData == This->wrap_buffer){
1198 ca_wrap_buffer(This->cap_buffer,
1199 cap_wri_offs_frames * This->fmt->nBlockAlign,
1200 This->cap_bufsize_frames * This->fmt->nBlockAlign,
1201 This->wrap_buffer, list.mBuffers[0].mDataByteSize);
1204 This->cap_held_frames += list.mBuffers[0].mDataByteSize / This->fmt->nBlockAlign;
1205 if(This->cap_held_frames > This->cap_bufsize_frames){
1206 This->cap_offs_frames += This->cap_held_frames % This->cap_bufsize_frames;
1207 This->cap_offs_frames %= This->cap_bufsize_frames;
1208 This->cap_held_frames = This->cap_bufsize_frames;
1212 OSSpinLockUnlock(&This->lock);
1213 return noErr;
1216 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
1218 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
1219 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
1220 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
1221 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
1222 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
1223 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
1226 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
1227 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
1228 AudioConverterRef *converter)
1230 OSStatus sc;
1231 HRESULT hr;
1233 if(dataflow == eCapture){
1234 AudioStreamBasicDescription desc;
1235 UInt32 size;
1236 Float64 rate;
1237 fenv_t fenv;
1238 BOOL fenv_stored = TRUE;
1240 hr = ca_get_audiodesc(&desc, fmt);
1241 if(FAILED(hr))
1242 return hr;
1243 dump_adesc("requested", &desc);
1245 /* input-only units can't perform sample rate conversion, so we have to
1246 * set up our own AudioConverter to support arbitrary sample rates. */
1247 size = sizeof(*dev_desc);
1248 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
1249 kAudioUnitScope_Input, 1, dev_desc, &size);
1250 if(sc != noErr){
1251 WARN("Couldn't get unit format: %x\n", (int)sc);
1252 return osstatus_to_hresult(sc);
1254 dump_adesc("hardware", dev_desc);
1256 rate = dev_desc->mSampleRate;
1257 *dev_desc = desc;
1258 dev_desc->mSampleRate = rate;
1260 dump_adesc("final", dev_desc);
1261 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1262 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
1263 if(sc != noErr){
1264 WARN("Couldn't set unit format: %x\n", (int)sc);
1265 return osstatus_to_hresult(sc);
1268 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
1269 if(feholdexcept(&fenv)){
1270 WARN("Failed to store fenv state\n");
1271 fenv_stored = FALSE;
1274 sc = AudioConverterNew(dev_desc, &desc, converter);
1276 if(fenv_stored && fesetenv(&fenv))
1277 WARN("Failed to restore fenv state\n");
1279 if(sc != noErr){
1280 WARN("Couldn't create audio converter: %x\n", (int)sc);
1281 return osstatus_to_hresult(sc);
1283 }else{
1284 hr = ca_get_audiodesc(dev_desc, fmt);
1285 if(FAILED(hr))
1286 return hr;
1288 dump_adesc("final", dev_desc);
1289 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
1290 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
1291 if(sc != noErr){
1292 WARN("Couldn't set format: %x\n", (int)sc);
1293 return osstatus_to_hresult(sc);
1297 return S_OK;
1300 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1301 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1302 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1303 const GUID *sessionguid)
1305 ACImpl *This = impl_from_IAudioClient(iface);
1306 HRESULT hr;
1307 OSStatus sc;
1308 int i;
1310 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1311 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1313 if(!fmt)
1314 return E_POINTER;
1316 dump_fmt(fmt);
1318 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1319 return AUDCLNT_E_NOT_INITIALIZED;
1321 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1322 AUDCLNT_STREAMFLAGS_LOOPBACK |
1323 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1324 AUDCLNT_STREAMFLAGS_NOPERSIST |
1325 AUDCLNT_STREAMFLAGS_RATEADJUST |
1326 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1327 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1328 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1329 TRACE("Unknown flags: %08x\n", flags);
1330 return E_INVALIDARG;
1333 if(mode == AUDCLNT_SHAREMODE_SHARED){
1334 period = DefaultPeriod;
1335 if( duration < 3 * period)
1336 duration = 3 * period;
1337 }else{
1338 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1339 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1340 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1341 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1344 if(!period)
1345 period = DefaultPeriod; /* not minimum */
1346 if(period < MinimumPeriod || period > 5000000)
1347 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1348 if(duration > 20000000) /* the smaller the period, the lower this limit */
1349 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1350 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1351 if(duration != period)
1352 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1353 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1354 return AUDCLNT_E_DEVICE_IN_USE;
1355 }else{
1356 if( duration < 8 * period)
1357 duration = 8 * period; /* may grow above 2s */
1361 OSSpinLockLock(&This->lock);
1363 if(This->initted){
1364 OSSpinLockUnlock(&This->lock);
1365 return AUDCLNT_E_ALREADY_INITIALIZED;
1368 This->fmt = clone_format(fmt);
1369 if(!This->fmt){
1370 OSSpinLockUnlock(&This->lock);
1371 return E_OUTOFMEMORY;
1374 This->period_ms = period / 10000;
1375 This->period_frames = MulDiv(period, This->fmt->nSamplesPerSec, 10000000);
1377 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1378 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1379 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1381 hr = ca_setup_audiounit(This->dataflow, This->unit, This->fmt, &This->dev_desc, &This->converter);
1382 if(FAILED(hr)){
1383 CoTaskMemFree(This->fmt);
1384 This->fmt = NULL;
1385 OSSpinLockUnlock(&This->lock);
1386 return hr;
1389 if(This->dataflow == eCapture){
1390 AURenderCallbackStruct input;
1392 memset(&input, 0, sizeof(input));
1393 input.inputProc = &ca_capture_cb;
1394 input.inputProcRefCon = This;
1396 sc = AudioUnitSetProperty(This->unit, kAudioOutputUnitProperty_SetInputCallback,
1397 kAudioUnitScope_Output, 1, &input, sizeof(input));
1398 if(sc != noErr){
1399 WARN("Couldn't set callback: %x\n", (int)sc);
1400 AudioConverterDispose(This->converter);
1401 This->converter = NULL;
1402 CoTaskMemFree(This->fmt);
1403 This->fmt = NULL;
1404 OSSpinLockUnlock(&This->lock);
1405 return osstatus_to_hresult(sc);
1407 }else{
1408 AURenderCallbackStruct input;
1410 memset(&input, 0, sizeof(input));
1411 input.inputProc = &ca_render_cb;
1412 input.inputProcRefCon = This;
1414 sc = AudioUnitSetProperty(This->unit, kAudioUnitProperty_SetRenderCallback,
1415 kAudioUnitScope_Input, 0, &input, sizeof(input));
1416 if(sc != noErr){
1417 WARN("Couldn't set callback: %x\n", (int)sc);
1418 CoTaskMemFree(This->fmt);
1419 This->fmt = NULL;
1420 OSSpinLockUnlock(&This->lock);
1421 return osstatus_to_hresult(sc);
1425 sc = AudioUnitInitialize(This->unit);
1426 if(sc != noErr){
1427 WARN("Couldn't initialize: %x\n", (int)sc);
1428 if(This->converter){
1429 AudioConverterDispose(This->converter);
1430 This->converter = NULL;
1432 CoTaskMemFree(This->fmt);
1433 This->fmt = NULL;
1434 OSSpinLockUnlock(&This->lock);
1435 return osstatus_to_hresult(sc);
1438 /* we play audio continuously because AudioOutputUnitStart sometimes takes
1439 * a while to return */
1440 sc = AudioOutputUnitStart(This->unit);
1441 if(sc != noErr){
1442 WARN("Unit failed to start: %x\n", (int)sc);
1443 if(This->converter){
1444 AudioConverterDispose(This->converter);
1445 This->converter = NULL;
1447 CoTaskMemFree(This->fmt);
1448 This->fmt = NULL;
1449 OSSpinLockUnlock(&This->lock);
1450 return osstatus_to_hresult(sc);
1453 This->local_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_frames * fmt->nBlockAlign);
1454 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1456 if(This->dataflow == eCapture){
1457 This->cap_bufsize_frames = MulDiv(duration, This->dev_desc.mSampleRate, 10000000);
1458 This->cap_buffer = HeapAlloc(GetProcessHeap(), 0, This->cap_bufsize_frames * This->fmt->nBlockAlign);
1461 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1462 if(!This->vols){
1463 CoTaskMemFree(This->fmt);
1464 This->fmt = NULL;
1465 OSSpinLockUnlock(&This->lock);
1466 return E_OUTOFMEMORY;
1469 for(i = 0; i < fmt->nChannels; ++i)
1470 This->vols[i] = 1.f;
1472 This->share = mode;
1473 This->flags = flags;
1475 EnterCriticalSection(&g_sessions_lock);
1477 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1478 &This->session);
1479 if(FAILED(hr)){
1480 LeaveCriticalSection(&g_sessions_lock);
1481 CoTaskMemFree(This->fmt);
1482 This->fmt = NULL;
1483 HeapFree(GetProcessHeap(), 0, This->vols);
1484 This->vols = NULL;
1485 OSSpinLockUnlock(&This->lock);
1486 return E_INVALIDARG;
1489 list_add_tail(&This->session->clients, &This->entry);
1491 LeaveCriticalSection(&g_sessions_lock);
1493 ca_setvol(This, -1);
1495 This->initted = TRUE;
1497 OSSpinLockUnlock(&This->lock);
1499 return S_OK;
1502 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1503 UINT32 *frames)
1505 ACImpl *This = impl_from_IAudioClient(iface);
1507 TRACE("(%p)->(%p)\n", This, frames);
1509 if(!frames)
1510 return E_POINTER;
1512 OSSpinLockLock(&This->lock);
1514 if(!This->initted){
1515 OSSpinLockUnlock(&This->lock);
1516 return AUDCLNT_E_NOT_INITIALIZED;
1519 *frames = This->bufsize_frames;
1521 OSSpinLockUnlock(&This->lock);
1523 return S_OK;
1526 static HRESULT ca_get_max_stream_latency(ACImpl *This, UInt32 *max)
1528 AudioObjectPropertyAddress addr;
1529 AudioStreamID *ids;
1530 UInt32 size;
1531 OSStatus sc;
1532 int nstreams, i;
1534 addr.mScope = This->scope;
1535 addr.mElement = 0;
1536 addr.mSelector = kAudioDevicePropertyStreams;
1538 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL,
1539 &size);
1540 if(sc != noErr){
1541 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1542 return osstatus_to_hresult(sc);
1545 ids = HeapAlloc(GetProcessHeap(), 0, size);
1546 if(!ids)
1547 return E_OUTOFMEMORY;
1549 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, ids);
1550 if(sc != noErr){
1551 WARN("Unable to get _Streams property: %x\n", (int)sc);
1552 HeapFree(GetProcessHeap(), 0, ids);
1553 return osstatus_to_hresult(sc);
1556 nstreams = size / sizeof(AudioStreamID);
1557 *max = 0;
1559 addr.mSelector = kAudioStreamPropertyLatency;
1560 for(i = 0; i < nstreams; ++i){
1561 UInt32 latency;
1563 size = sizeof(latency);
1564 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL,
1565 &size, &latency);
1566 if(sc != noErr){
1567 WARN("Unable to get _Latency property: %x\n", (int)sc);
1568 continue;
1571 if(latency > *max)
1572 *max = latency;
1575 HeapFree(GetProcessHeap(), 0, ids);
1577 return S_OK;
1580 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1581 REFERENCE_TIME *out)
1583 ACImpl *This = impl_from_IAudioClient(iface);
1584 UInt32 latency, stream_latency, size;
1585 AudioObjectPropertyAddress addr;
1586 OSStatus sc;
1587 HRESULT hr;
1589 TRACE("(%p)->(%p)\n", This, out);
1591 if(!out)
1592 return E_POINTER;
1594 OSSpinLockLock(&This->lock);
1596 if(!This->initted){
1597 OSSpinLockUnlock(&This->lock);
1598 return AUDCLNT_E_NOT_INITIALIZED;
1601 addr.mScope = This->scope;
1602 addr.mSelector = kAudioDevicePropertyLatency;
1603 addr.mElement = 0;
1605 size = sizeof(latency);
1606 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1607 &size, &latency);
1608 if(sc != noErr){
1609 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1610 OSSpinLockUnlock(&This->lock);
1611 return osstatus_to_hresult(sc);
1614 hr = ca_get_max_stream_latency(This, &stream_latency);
1615 if(FAILED(hr)){
1616 OSSpinLockUnlock(&This->lock);
1617 return hr;
1620 latency += stream_latency;
1621 /* pretend we process audio in Period chunks, so max latency includes
1622 * the period time */
1623 *out = MulDiv(latency, 10000000, This->fmt->nSamplesPerSec)
1624 + This->period_ms * 10000;
1626 OSSpinLockUnlock(&This->lock);
1628 return S_OK;
1631 static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
1632 UINT32 *numpad)
1634 if(!This->initted)
1635 return AUDCLNT_E_NOT_INITIALIZED;
1637 if(This->dataflow == eCapture)
1638 capture_resample(This);
1640 *numpad = This->held_frames;
1642 return S_OK;
1645 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1646 UINT32 *numpad)
1648 ACImpl *This = impl_from_IAudioClient(iface);
1649 HRESULT hr;
1651 TRACE("(%p)->(%p)\n", This, numpad);
1653 if(!numpad)
1654 return E_POINTER;
1656 OSSpinLockLock(&This->lock);
1658 hr = AudioClient_GetCurrentPadding_nolock(This, numpad);
1660 OSSpinLockUnlock(&This->lock);
1662 return hr;
1665 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1666 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1667 WAVEFORMATEX **outpwfx)
1669 ACImpl *This = impl_from_IAudioClient(iface);
1670 AudioStreamBasicDescription dev_desc;
1671 AudioConverterRef converter;
1672 AudioComponentInstance unit;
1673 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)pwfx;
1674 HRESULT hr;
1676 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1678 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1679 return E_POINTER;
1681 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1682 return E_INVALIDARG;
1684 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1685 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1686 return E_INVALIDARG;
1688 dump_fmt(pwfx);
1690 if(outpwfx){
1691 *outpwfx = NULL;
1692 if(mode != AUDCLNT_SHAREMODE_SHARED)
1693 outpwfx = NULL;
1696 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1697 if(pwfx->nAvgBytesPerSec == 0 ||
1698 pwfx->nBlockAlign == 0 ||
1699 fmtex->Samples.wValidBitsPerSample > pwfx->wBitsPerSample)
1700 return E_INVALIDARG;
1701 if(fmtex->Samples.wValidBitsPerSample < pwfx->wBitsPerSample)
1702 goto unsupported;
1703 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE){
1704 if(fmtex->dwChannelMask == 0 ||
1705 fmtex->dwChannelMask & SPEAKER_RESERVED)
1706 goto unsupported;
1710 if(pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample / 8 ||
1711 pwfx->nAvgBytesPerSec != pwfx->nBlockAlign * pwfx->nSamplesPerSec)
1712 goto unsupported;
1714 if(pwfx->nChannels == 0)
1715 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1717 unit = get_audiounit(This->dataflow, This->adevid);
1719 converter = NULL;
1720 hr = ca_setup_audiounit(This->dataflow, unit, pwfx, &dev_desc, &converter);
1721 AudioComponentInstanceDispose(unit);
1722 if(FAILED(hr))
1723 goto unsupported;
1725 if(converter)
1726 AudioConverterDispose(converter);
1728 return S_OK;
1730 unsupported:
1731 if(outpwfx){
1732 hr = IAudioClient_GetMixFormat(&This->IAudioClient_iface, outpwfx);
1733 if(FAILED(hr))
1734 return hr;
1735 return S_FALSE;
1738 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1741 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1742 WAVEFORMATEX **pwfx)
1744 ACImpl *This = impl_from_IAudioClient(iface);
1745 WAVEFORMATEXTENSIBLE *fmt;
1746 OSStatus sc;
1747 UInt32 size;
1748 Float64 rate;
1749 AudioBufferList *buffers;
1750 AudioObjectPropertyAddress addr;
1751 int i;
1753 TRACE("(%p)->(%p)\n", This, pwfx);
1755 if(!pwfx)
1756 return E_POINTER;
1757 *pwfx = NULL;
1759 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1760 if(!fmt)
1761 return E_OUTOFMEMORY;
1763 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1765 addr.mScope = This->scope;
1766 addr.mElement = 0;
1767 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
1769 sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
1770 if(sc != noErr){
1771 CoTaskMemFree(fmt);
1772 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
1773 return osstatus_to_hresult(sc);
1776 buffers = HeapAlloc(GetProcessHeap(), 0, size);
1777 if(!buffers){
1778 CoTaskMemFree(fmt);
1779 return E_OUTOFMEMORY;
1782 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL,
1783 &size, buffers);
1784 if(sc != noErr){
1785 CoTaskMemFree(fmt);
1786 HeapFree(GetProcessHeap(), 0, buffers);
1787 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
1788 return osstatus_to_hresult(sc);
1791 fmt->Format.nChannels = 0;
1792 for(i = 0; i < buffers->mNumberBuffers; ++i)
1793 fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
1795 HeapFree(GetProcessHeap(), 0, buffers);
1797 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1799 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
1800 size = sizeof(Float64);
1801 sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, &rate);
1802 if(sc != noErr){
1803 CoTaskMemFree(fmt);
1804 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
1805 return osstatus_to_hresult(sc);
1807 fmt->Format.nSamplesPerSec = rate;
1809 fmt->Format.wBitsPerSample = 32;
1810 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1812 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1813 fmt->Format.nChannels) / 8;
1814 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1815 fmt->Format.nBlockAlign;
1817 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1818 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1820 *pwfx = (WAVEFORMATEX*)fmt;
1821 dump_fmt(*pwfx);
1823 return S_OK;
1826 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1827 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1829 ACImpl *This = impl_from_IAudioClient(iface);
1831 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1833 if(!defperiod && !minperiod)
1834 return E_POINTER;
1836 if(defperiod)
1837 *defperiod = DefaultPeriod;
1838 if(minperiod)
1839 *minperiod = MinimumPeriod;
1841 return S_OK;
1844 void CALLBACK ca_period_cb(void *user, BOOLEAN timer)
1846 ACImpl *This = user;
1848 if(This->event)
1849 SetEvent(This->event);
1852 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1854 ACImpl *This = impl_from_IAudioClient(iface);
1856 TRACE("(%p)\n", This);
1858 OSSpinLockLock(&This->lock);
1860 if(!This->initted){
1861 OSSpinLockUnlock(&This->lock);
1862 return AUDCLNT_E_NOT_INITIALIZED;
1865 if(This->playing){
1866 OSSpinLockUnlock(&This->lock);
1867 return AUDCLNT_E_NOT_STOPPED;
1870 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1871 OSSpinLockUnlock(&This->lock);
1872 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1875 if(This->event && !This->timer)
1876 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, ca_period_cb,
1877 This, 0, This->period_ms, WT_EXECUTEINTIMERTHREAD)){
1878 This->timer = NULL;
1879 OSSpinLockUnlock(&This->lock);
1880 WARN("Unable to create timer: %u\n", GetLastError());
1881 return E_OUTOFMEMORY;
1884 This->playing = TRUE;
1886 OSSpinLockUnlock(&This->lock);
1888 return S_OK;
1891 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1893 ACImpl *This = impl_from_IAudioClient(iface);
1895 TRACE("(%p)\n", This);
1897 OSSpinLockLock(&This->lock);
1899 if(!This->initted){
1900 OSSpinLockUnlock(&This->lock);
1901 return AUDCLNT_E_NOT_INITIALIZED;
1904 if(!This->playing){
1905 OSSpinLockUnlock(&This->lock);
1906 return S_FALSE;
1909 This->playing = FALSE;
1911 OSSpinLockUnlock(&This->lock);
1913 return S_OK;
1916 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1918 ACImpl *This = impl_from_IAudioClient(iface);
1920 TRACE("(%p)\n", This);
1922 OSSpinLockLock(&This->lock);
1924 if(!This->initted){
1925 OSSpinLockUnlock(&This->lock);
1926 return AUDCLNT_E_NOT_INITIALIZED;
1929 if(This->playing){
1930 OSSpinLockUnlock(&This->lock);
1931 return AUDCLNT_E_NOT_STOPPED;
1934 if(This->getbuf_last){
1935 OSSpinLockUnlock(&This->lock);
1936 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1939 if(This->dataflow == eRender){
1940 This->written_frames = 0;
1941 }else{
1942 This->written_frames += This->held_frames;
1945 This->held_frames = 0;
1946 This->lcl_offs_frames = 0;
1947 This->wri_offs_frames = 0;
1948 This->cap_offs_frames = 0;
1949 This->cap_held_frames = 0;
1951 OSSpinLockUnlock(&This->lock);
1953 return S_OK;
1956 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1957 HANDLE event)
1959 ACImpl *This = impl_from_IAudioClient(iface);
1961 TRACE("(%p)->(%p)\n", This, event);
1963 if(!event)
1964 return E_INVALIDARG;
1966 OSSpinLockLock(&This->lock);
1968 if(!This->initted){
1969 OSSpinLockUnlock(&This->lock);
1970 return AUDCLNT_E_NOT_INITIALIZED;
1973 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1974 OSSpinLockUnlock(&This->lock);
1975 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1978 if (This->event){
1979 OSSpinLockUnlock(&This->lock);
1980 FIXME("called twice\n");
1981 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1984 This->event = event;
1986 OSSpinLockUnlock(&This->lock);
1988 return S_OK;
1991 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1992 void **ppv)
1994 ACImpl *This = impl_from_IAudioClient(iface);
1996 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1998 if(!ppv)
1999 return E_POINTER;
2000 *ppv = NULL;
2002 OSSpinLockLock(&This->lock);
2004 if(!This->initted){
2005 OSSpinLockUnlock(&This->lock);
2006 return AUDCLNT_E_NOT_INITIALIZED;
2009 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2010 if(This->dataflow != eRender){
2011 OSSpinLockUnlock(&This->lock);
2012 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2014 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2015 *ppv = &This->IAudioRenderClient_iface;
2016 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2017 if(This->dataflow != eCapture){
2018 OSSpinLockUnlock(&This->lock);
2019 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2021 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2022 *ppv = &This->IAudioCaptureClient_iface;
2023 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2024 IAudioClock_AddRef(&This->IAudioClock_iface);
2025 *ppv = &This->IAudioClock_iface;
2026 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2027 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2028 *ppv = &This->IAudioStreamVolume_iface;
2029 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2030 if(!This->session_wrapper){
2031 This->session_wrapper = AudioSessionWrapper_Create(This);
2032 if(!This->session_wrapper){
2033 OSSpinLockUnlock(&This->lock);
2034 return E_OUTOFMEMORY;
2036 }else
2037 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2039 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2040 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2041 if(!This->session_wrapper){
2042 This->session_wrapper = AudioSessionWrapper_Create(This);
2043 if(!This->session_wrapper){
2044 OSSpinLockUnlock(&This->lock);
2045 return E_OUTOFMEMORY;
2047 }else
2048 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2050 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2051 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2052 if(!This->session_wrapper){
2053 This->session_wrapper = AudioSessionWrapper_Create(This);
2054 if(!This->session_wrapper){
2055 OSSpinLockUnlock(&This->lock);
2056 return E_OUTOFMEMORY;
2058 }else
2059 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2061 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2064 if(*ppv){
2065 OSSpinLockUnlock(&This->lock);
2066 return S_OK;
2069 OSSpinLockUnlock(&This->lock);
2071 FIXME("stub %s\n", debugstr_guid(riid));
2072 return E_NOINTERFACE;
2075 static const IAudioClientVtbl AudioClient_Vtbl =
2077 AudioClient_QueryInterface,
2078 AudioClient_AddRef,
2079 AudioClient_Release,
2080 AudioClient_Initialize,
2081 AudioClient_GetBufferSize,
2082 AudioClient_GetStreamLatency,
2083 AudioClient_GetCurrentPadding,
2084 AudioClient_IsFormatSupported,
2085 AudioClient_GetMixFormat,
2086 AudioClient_GetDevicePeriod,
2087 AudioClient_Start,
2088 AudioClient_Stop,
2089 AudioClient_Reset,
2090 AudioClient_SetEventHandle,
2091 AudioClient_GetService
2094 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2095 IAudioRenderClient *iface, REFIID riid, void **ppv)
2097 ACImpl *This = impl_from_IAudioRenderClient(iface);
2098 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2100 if(!ppv)
2101 return E_POINTER;
2102 *ppv = NULL;
2104 if(IsEqualIID(riid, &IID_IUnknown) ||
2105 IsEqualIID(riid, &IID_IAudioRenderClient))
2106 *ppv = iface;
2107 else if(IsEqualIID(riid, &IID_IMarshal))
2108 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2110 if(*ppv){
2111 IUnknown_AddRef((IUnknown*)*ppv);
2112 return S_OK;
2115 WARN("Unknown interface %s\n", debugstr_guid(riid));
2116 return E_NOINTERFACE;
2119 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2121 ACImpl *This = impl_from_IAudioRenderClient(iface);
2122 return AudioClient_AddRef(&This->IAudioClient_iface);
2125 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2127 ACImpl *This = impl_from_IAudioRenderClient(iface);
2128 return AudioClient_Release(&This->IAudioClient_iface);
2131 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2132 UINT32 frames, BYTE **data)
2134 ACImpl *This = impl_from_IAudioRenderClient(iface);
2135 UINT32 pad;
2136 HRESULT hr;
2138 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2140 if(!data)
2141 return E_POINTER;
2142 *data = NULL;
2144 OSSpinLockLock(&This->lock);
2146 if(This->getbuf_last){
2147 OSSpinLockUnlock(&This->lock);
2148 return AUDCLNT_E_OUT_OF_ORDER;
2151 if(!frames){
2152 OSSpinLockUnlock(&This->lock);
2153 return S_OK;
2156 hr = AudioClient_GetCurrentPadding_nolock(This, &pad);
2157 if(FAILED(hr)){
2158 OSSpinLockUnlock(&This->lock);
2159 return hr;
2162 if(pad + frames > This->bufsize_frames){
2163 OSSpinLockUnlock(&This->lock);
2164 return AUDCLNT_E_BUFFER_TOO_LARGE;
2167 if(This->wri_offs_frames + frames > This->bufsize_frames){
2168 if(This->tmp_buffer_frames < frames){
2169 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2170 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, frames * This->fmt->nBlockAlign);
2171 if(!This->tmp_buffer){
2172 OSSpinLockUnlock(&This->lock);
2173 return E_OUTOFMEMORY;
2175 This->tmp_buffer_frames = frames;
2177 *data = This->tmp_buffer;
2178 This->getbuf_last = -frames;
2179 }else{
2180 *data = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2181 This->getbuf_last = frames;
2184 silence_buffer(This, *data, frames);
2186 OSSpinLockUnlock(&This->lock);
2188 return S_OK;
2191 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2192 IAudioRenderClient *iface, UINT32 frames, DWORD flags)
2194 ACImpl *This = impl_from_IAudioRenderClient(iface);
2195 BYTE *buffer;
2197 TRACE("(%p)->(%u, %x)\n", This, frames, flags);
2199 OSSpinLockLock(&This->lock);
2201 if(!frames){
2202 This->getbuf_last = 0;
2203 OSSpinLockUnlock(&This->lock);
2204 return S_OK;
2207 if(!This->getbuf_last){
2208 OSSpinLockUnlock(&This->lock);
2209 return AUDCLNT_E_OUT_OF_ORDER;
2212 if(frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2213 OSSpinLockUnlock(&This->lock);
2214 return AUDCLNT_E_INVALID_SIZE;
2217 if(This->getbuf_last >= 0)
2218 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2219 else
2220 buffer = This->tmp_buffer;
2222 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2223 silence_buffer(This, buffer, frames);
2225 if(This->getbuf_last < 0)
2226 ca_wrap_buffer(This->local_buffer,
2227 This->wri_offs_frames * This->fmt->nBlockAlign,
2228 This->bufsize_frames * This->fmt->nBlockAlign,
2229 buffer, frames * This->fmt->nBlockAlign);
2232 This->wri_offs_frames += frames;
2233 This->wri_offs_frames %= This->bufsize_frames;
2234 This->held_frames += frames;
2235 This->written_frames += frames;
2236 This->getbuf_last = 0;
2238 OSSpinLockUnlock(&This->lock);
2240 return S_OK;
2243 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2244 AudioRenderClient_QueryInterface,
2245 AudioRenderClient_AddRef,
2246 AudioRenderClient_Release,
2247 AudioRenderClient_GetBuffer,
2248 AudioRenderClient_ReleaseBuffer
2251 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2252 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2254 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2255 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2257 if(!ppv)
2258 return E_POINTER;
2259 *ppv = NULL;
2261 if(IsEqualIID(riid, &IID_IUnknown) ||
2262 IsEqualIID(riid, &IID_IAudioCaptureClient))
2263 *ppv = iface;
2264 else if(IsEqualIID(riid, &IID_IMarshal))
2265 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2267 if(*ppv){
2268 IUnknown_AddRef((IUnknown*)*ppv);
2269 return S_OK;
2272 WARN("Unknown interface %s\n", debugstr_guid(riid));
2273 return E_NOINTERFACE;
2276 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2278 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2279 return IAudioClient_AddRef(&This->IAudioClient_iface);
2282 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2284 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2285 return IAudioClient_Release(&This->IAudioClient_iface);
2288 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2289 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2290 UINT64 *qpcpos)
2292 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2293 UINT32 chunk_bytes, chunk_frames;
2295 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2296 devpos, qpcpos);
2298 if(!data || !frames || !flags)
2299 return E_POINTER;
2301 OSSpinLockLock(&This->lock);
2303 if(This->getbuf_last){
2304 OSSpinLockUnlock(&This->lock);
2305 return AUDCLNT_E_OUT_OF_ORDER;
2308 capture_resample(This);
2310 if(This->held_frames < This->period_frames){
2311 *frames = 0;
2312 OSSpinLockUnlock(&This->lock);
2313 return AUDCLNT_S_BUFFER_EMPTY;
2316 *flags = 0;
2318 chunk_frames = This->bufsize_frames - This->lcl_offs_frames;
2319 if(chunk_frames < This->period_frames){
2320 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2321 if(!This->tmp_buffer)
2322 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->period_frames * This->fmt->nBlockAlign);
2323 *data = This->tmp_buffer;
2324 memcpy(*data, This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign, chunk_bytes);
2325 memcpy((*data) + chunk_bytes, This->local_buffer, This->period_frames * This->fmt->nBlockAlign - chunk_bytes);
2326 }else
2327 *data = This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
2329 This->getbuf_last = *frames = This->period_frames;
2331 if(devpos)
2332 *devpos = This->written_frames;
2333 if(qpcpos){ /* fixme: qpc of recording time */
2334 LARGE_INTEGER stamp, freq;
2335 QueryPerformanceCounter(&stamp);
2336 QueryPerformanceFrequency(&freq);
2337 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2340 OSSpinLockUnlock(&This->lock);
2342 return S_OK;
2345 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2346 IAudioCaptureClient *iface, UINT32 done)
2348 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2350 TRACE("(%p)->(%u)\n", This, done);
2352 OSSpinLockLock(&This->lock);
2354 if(!done){
2355 This->getbuf_last = 0;
2356 OSSpinLockUnlock(&This->lock);
2357 return S_OK;
2360 if(!This->getbuf_last){
2361 OSSpinLockUnlock(&This->lock);
2362 return AUDCLNT_E_OUT_OF_ORDER;
2365 if(This->getbuf_last != done){
2366 OSSpinLockUnlock(&This->lock);
2367 return AUDCLNT_E_INVALID_SIZE;
2370 This->written_frames += done;
2371 This->held_frames -= done;
2372 This->lcl_offs_frames += done;
2373 This->lcl_offs_frames %= This->bufsize_frames;
2374 This->getbuf_last = 0;
2376 OSSpinLockUnlock(&This->lock);
2378 return S_OK;
2381 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2382 IAudioCaptureClient *iface, UINT32 *frames)
2384 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2386 TRACE("(%p)->(%p)\n", This, frames);
2388 if(!frames)
2389 return E_POINTER;
2391 OSSpinLockLock(&This->lock);
2393 capture_resample(This);
2395 if(This->held_frames >= This->period_frames)
2396 *frames = This->period_frames;
2397 else
2398 *frames = 0;
2400 OSSpinLockUnlock(&This->lock);
2402 return S_OK;
2405 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2407 AudioCaptureClient_QueryInterface,
2408 AudioCaptureClient_AddRef,
2409 AudioCaptureClient_Release,
2410 AudioCaptureClient_GetBuffer,
2411 AudioCaptureClient_ReleaseBuffer,
2412 AudioCaptureClient_GetNextPacketSize
2415 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2416 REFIID riid, void **ppv)
2418 ACImpl *This = impl_from_IAudioClock(iface);
2420 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2422 if(!ppv)
2423 return E_POINTER;
2424 *ppv = NULL;
2426 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2427 *ppv = iface;
2428 else if(IsEqualIID(riid, &IID_IAudioClock2))
2429 *ppv = &This->IAudioClock2_iface;
2430 if(*ppv){
2431 IUnknown_AddRef((IUnknown*)*ppv);
2432 return S_OK;
2435 WARN("Unknown interface %s\n", debugstr_guid(riid));
2436 return E_NOINTERFACE;
2439 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2441 ACImpl *This = impl_from_IAudioClock(iface);
2442 return IAudioClient_AddRef(&This->IAudioClient_iface);
2445 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2447 ACImpl *This = impl_from_IAudioClock(iface);
2448 return IAudioClient_Release(&This->IAudioClient_iface);
2451 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2453 ACImpl *This = impl_from_IAudioClock(iface);
2455 TRACE("(%p)->(%p)\n", This, freq);
2457 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2458 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2459 else
2460 *freq = This->fmt->nSamplesPerSec;
2462 return S_OK;
2465 static HRESULT AudioClock_GetPosition_nolock(ACImpl *This,
2466 UINT64 *pos, UINT64 *qpctime)
2468 *pos = This->written_frames - This->held_frames;
2470 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2471 *pos *= This->fmt->nBlockAlign;
2473 if(qpctime){
2474 LARGE_INTEGER stamp, freq;
2475 QueryPerformanceCounter(&stamp);
2476 QueryPerformanceFrequency(&freq);
2477 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2480 return S_OK;
2483 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2484 UINT64 *qpctime)
2486 ACImpl *This = impl_from_IAudioClock(iface);
2487 HRESULT hr;
2489 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2491 if(!pos)
2492 return E_POINTER;
2494 OSSpinLockLock(&This->lock);
2496 hr = AudioClock_GetPosition_nolock(This, pos, qpctime);
2498 OSSpinLockUnlock(&This->lock);
2500 return hr;
2503 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2504 DWORD *chars)
2506 ACImpl *This = impl_from_IAudioClock(iface);
2508 TRACE("(%p)->(%p)\n", This, chars);
2510 if(!chars)
2511 return E_POINTER;
2513 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2515 return S_OK;
2518 static const IAudioClockVtbl AudioClock_Vtbl =
2520 AudioClock_QueryInterface,
2521 AudioClock_AddRef,
2522 AudioClock_Release,
2523 AudioClock_GetFrequency,
2524 AudioClock_GetPosition,
2525 AudioClock_GetCharacteristics
2528 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2529 REFIID riid, void **ppv)
2531 ACImpl *This = impl_from_IAudioClock2(iface);
2532 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2535 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2537 ACImpl *This = impl_from_IAudioClock2(iface);
2538 return IAudioClient_AddRef(&This->IAudioClient_iface);
2541 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2543 ACImpl *This = impl_from_IAudioClock2(iface);
2544 return IAudioClient_Release(&This->IAudioClient_iface);
2547 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2548 UINT64 *pos, UINT64 *qpctime)
2550 ACImpl *This = impl_from_IAudioClock2(iface);
2552 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2554 return E_NOTIMPL;
2557 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2559 AudioClock2_QueryInterface,
2560 AudioClock2_AddRef,
2561 AudioClock2_Release,
2562 AudioClock2_GetDevicePosition
2565 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2567 AudioSessionWrapper *ret;
2569 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2570 sizeof(AudioSessionWrapper));
2571 if(!ret)
2572 return NULL;
2574 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2575 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2576 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2578 ret->ref = 1;
2580 ret->client = client;
2581 if(client){
2582 ret->session = client->session;
2583 AudioClient_AddRef(&client->IAudioClient_iface);
2586 return ret;
2589 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2590 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2592 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2594 if(!ppv)
2595 return E_POINTER;
2596 *ppv = NULL;
2598 if(IsEqualIID(riid, &IID_IUnknown) ||
2599 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2600 IsEqualIID(riid, &IID_IAudioSessionControl2))
2601 *ppv = iface;
2602 if(*ppv){
2603 IUnknown_AddRef((IUnknown*)*ppv);
2604 return S_OK;
2607 WARN("Unknown interface %s\n", debugstr_guid(riid));
2608 return E_NOINTERFACE;
2611 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2613 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2614 ULONG ref;
2615 ref = InterlockedIncrement(&This->ref);
2616 TRACE("(%p) Refcount now %u\n", This, ref);
2617 return ref;
2620 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2622 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2623 ULONG ref;
2624 ref = InterlockedDecrement(&This->ref);
2625 TRACE("(%p) Refcount now %u\n", This, ref);
2626 if(!ref){
2627 if(This->client){
2628 OSSpinLockLock(&This->client->lock);
2629 This->client->session_wrapper = NULL;
2630 OSSpinLockUnlock(&This->client->lock);
2631 AudioClient_Release(&This->client->IAudioClient_iface);
2633 HeapFree(GetProcessHeap(), 0, This);
2635 return ref;
2638 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2639 AudioSessionState *state)
2641 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2642 ACImpl *client;
2644 TRACE("(%p)->(%p)\n", This, state);
2646 if(!state)
2647 return NULL_PTR_ERR;
2649 EnterCriticalSection(&g_sessions_lock);
2651 if(list_empty(&This->session->clients)){
2652 *state = AudioSessionStateExpired;
2653 LeaveCriticalSection(&g_sessions_lock);
2654 return S_OK;
2657 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2658 OSSpinLockLock(&client->lock);
2659 if(client->playing){
2660 *state = AudioSessionStateActive;
2661 OSSpinLockUnlock(&client->lock);
2662 LeaveCriticalSection(&g_sessions_lock);
2663 return S_OK;
2665 OSSpinLockUnlock(&client->lock);
2668 LeaveCriticalSection(&g_sessions_lock);
2670 *state = AudioSessionStateInactive;
2672 return S_OK;
2675 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2676 IAudioSessionControl2 *iface, WCHAR **name)
2678 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2680 FIXME("(%p)->(%p) - stub\n", This, name);
2682 return E_NOTIMPL;
2685 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2686 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2688 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2690 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2692 return E_NOTIMPL;
2695 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2696 IAudioSessionControl2 *iface, WCHAR **path)
2698 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2700 FIXME("(%p)->(%p) - stub\n", This, path);
2702 return E_NOTIMPL;
2705 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2706 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2708 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2710 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2712 return E_NOTIMPL;
2715 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2716 IAudioSessionControl2 *iface, GUID *group)
2718 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2720 FIXME("(%p)->(%p) - stub\n", This, group);
2722 return E_NOTIMPL;
2725 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2726 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2728 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2730 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2731 debugstr_guid(session));
2733 return E_NOTIMPL;
2736 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2737 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2739 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2741 FIXME("(%p)->(%p) - stub\n", This, events);
2743 return S_OK;
2746 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2747 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2749 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2751 FIXME("(%p)->(%p) - stub\n", This, events);
2753 return S_OK;
2756 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2757 IAudioSessionControl2 *iface, WCHAR **id)
2759 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2761 FIXME("(%p)->(%p) - stub\n", This, id);
2763 return E_NOTIMPL;
2766 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2767 IAudioSessionControl2 *iface, WCHAR **id)
2769 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2771 FIXME("(%p)->(%p) - stub\n", This, id);
2773 return E_NOTIMPL;
2776 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2777 IAudioSessionControl2 *iface, DWORD *pid)
2779 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2781 TRACE("(%p)->(%p)\n", This, pid);
2783 if(!pid)
2784 return E_POINTER;
2786 *pid = GetCurrentProcessId();
2788 return S_OK;
2791 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2792 IAudioSessionControl2 *iface)
2794 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2796 TRACE("(%p)\n", This);
2798 return S_FALSE;
2801 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2802 IAudioSessionControl2 *iface, BOOL optout)
2804 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2806 TRACE("(%p)->(%d)\n", This, optout);
2808 return S_OK;
2811 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2813 AudioSessionControl_QueryInterface,
2814 AudioSessionControl_AddRef,
2815 AudioSessionControl_Release,
2816 AudioSessionControl_GetState,
2817 AudioSessionControl_GetDisplayName,
2818 AudioSessionControl_SetDisplayName,
2819 AudioSessionControl_GetIconPath,
2820 AudioSessionControl_SetIconPath,
2821 AudioSessionControl_GetGroupingParam,
2822 AudioSessionControl_SetGroupingParam,
2823 AudioSessionControl_RegisterAudioSessionNotification,
2824 AudioSessionControl_UnregisterAudioSessionNotification,
2825 AudioSessionControl_GetSessionIdentifier,
2826 AudioSessionControl_GetSessionInstanceIdentifier,
2827 AudioSessionControl_GetProcessId,
2828 AudioSessionControl_IsSystemSoundsSession,
2829 AudioSessionControl_SetDuckingPreference
2832 /* index == -1 means set all channels, otherwise sets only the given channel */
2833 static HRESULT ca_setvol(ACImpl *This, UINT32 index)
2835 Float32 level;
2836 OSStatus sc;
2838 if(This->session->mute)
2839 level = 0.;
2840 else{
2841 if(index == (UINT32)-1){
2842 UINT32 i;
2843 level = 1.;
2844 for(i = 0; i < This->fmt->nChannels; ++i){
2845 Float32 tmp;
2846 tmp = This->session->master_vol *
2847 This->session->channel_vols[i] * This->vols[i];
2848 level = tmp < level ? tmp : level;
2850 }else
2851 level = This->session->master_vol *
2852 This->session->channel_vols[index] * This->vols[index];
2855 sc = AudioUnitSetParameter(This->unit, kHALOutputParam_Volume,
2856 kAudioUnitScope_Global, 0, level, 0);
2857 if(sc != noErr)
2858 WARN("Couldn't set volume: %x\n", (int)sc);
2860 return S_OK;
2863 static HRESULT ca_session_setvol(AudioSession *session, UINT32 index)
2865 HRESULT ret = S_OK;
2866 ACImpl *client;
2868 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2869 HRESULT hr;
2870 hr = ca_setvol(client, index);
2871 if(FAILED(hr))
2872 ret = hr;
2875 return ret;
2878 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2879 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2881 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2883 if(!ppv)
2884 return E_POINTER;
2885 *ppv = NULL;
2887 if(IsEqualIID(riid, &IID_IUnknown) ||
2888 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2889 *ppv = iface;
2890 if(*ppv){
2891 IUnknown_AddRef((IUnknown*)*ppv);
2892 return S_OK;
2895 WARN("Unknown interface %s\n", debugstr_guid(riid));
2896 return E_NOINTERFACE;
2899 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2901 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2902 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2905 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2907 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2908 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2911 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2912 ISimpleAudioVolume *iface, float level, const GUID *context)
2914 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2915 AudioSession *session = This->session;
2916 HRESULT ret;
2918 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2920 if(level < 0.f || level > 1.f)
2921 return E_INVALIDARG;
2923 if(context)
2924 FIXME("Notifications not supported yet\n");
2926 EnterCriticalSection(&session->lock);
2928 session->master_vol = level;
2930 ret = ca_session_setvol(session, -1);
2932 LeaveCriticalSection(&session->lock);
2934 return ret;
2937 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2938 ISimpleAudioVolume *iface, float *level)
2940 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2941 AudioSession *session = This->session;
2943 TRACE("(%p)->(%p)\n", session, level);
2945 if(!level)
2946 return NULL_PTR_ERR;
2948 *level = session->master_vol;
2950 return S_OK;
2953 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2954 BOOL mute, const GUID *context)
2956 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2957 AudioSession *session = This->session;
2959 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2961 if(context)
2962 FIXME("Notifications not supported yet\n");
2964 EnterCriticalSection(&session->lock);
2966 session->mute = mute;
2968 ca_session_setvol(session, -1);
2970 LeaveCriticalSection(&session->lock);
2972 return S_OK;
2975 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2976 BOOL *mute)
2978 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2979 AudioSession *session = This->session;
2981 TRACE("(%p)->(%p)\n", session, mute);
2983 if(!mute)
2984 return NULL_PTR_ERR;
2986 *mute = session->mute;
2988 return S_OK;
2991 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2993 SimpleAudioVolume_QueryInterface,
2994 SimpleAudioVolume_AddRef,
2995 SimpleAudioVolume_Release,
2996 SimpleAudioVolume_SetMasterVolume,
2997 SimpleAudioVolume_GetMasterVolume,
2998 SimpleAudioVolume_SetMute,
2999 SimpleAudioVolume_GetMute
3002 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3003 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3005 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3007 if(!ppv)
3008 return E_POINTER;
3009 *ppv = NULL;
3011 if(IsEqualIID(riid, &IID_IUnknown) ||
3012 IsEqualIID(riid, &IID_IAudioStreamVolume))
3013 *ppv = iface;
3014 if(*ppv){
3015 IUnknown_AddRef((IUnknown*)*ppv);
3016 return S_OK;
3019 WARN("Unknown interface %s\n", debugstr_guid(riid));
3020 return E_NOINTERFACE;
3023 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3025 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3026 return IAudioClient_AddRef(&This->IAudioClient_iface);
3029 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3031 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3032 return IAudioClient_Release(&This->IAudioClient_iface);
3035 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3036 IAudioStreamVolume *iface, UINT32 *out)
3038 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3040 TRACE("(%p)->(%p)\n", This, out);
3042 if(!out)
3043 return E_POINTER;
3045 *out = This->fmt->nChannels;
3047 return S_OK;
3050 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3051 IAudioStreamVolume *iface, UINT32 index, float level)
3053 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3054 HRESULT ret;
3056 TRACE("(%p)->(%d, %f)\n", This, index, level);
3058 if(level < 0.f || level > 1.f)
3059 return E_INVALIDARG;
3061 if(index >= This->fmt->nChannels)
3062 return E_INVALIDARG;
3064 OSSpinLockLock(&This->lock);
3066 This->vols[index] = level;
3068 WARN("CoreAudio doesn't support per-channel volume control\n");
3069 ret = ca_setvol(This, index);
3071 OSSpinLockUnlock(&This->lock);
3073 return ret;
3076 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3077 IAudioStreamVolume *iface, UINT32 index, float *level)
3079 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3081 TRACE("(%p)->(%d, %p)\n", This, index, level);
3083 if(!level)
3084 return E_POINTER;
3086 if(index >= This->fmt->nChannels)
3087 return E_INVALIDARG;
3089 *level = This->vols[index];
3091 return S_OK;
3094 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3095 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3097 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3098 int i;
3099 HRESULT ret;
3101 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3103 if(!levels)
3104 return E_POINTER;
3106 if(count != This->fmt->nChannels)
3107 return E_INVALIDARG;
3109 OSSpinLockLock(&This->lock);
3111 for(i = 0; i < count; ++i)
3112 This->vols[i] = levels[i];
3114 ret = ca_setvol(This, -1);
3116 OSSpinLockUnlock(&This->lock);
3118 return ret;
3121 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3122 IAudioStreamVolume *iface, UINT32 count, float *levels)
3124 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3125 int i;
3127 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3129 if(!levels)
3130 return E_POINTER;
3132 if(count != This->fmt->nChannels)
3133 return E_INVALIDARG;
3135 OSSpinLockLock(&This->lock);
3137 for(i = 0; i < count; ++i)
3138 levels[i] = This->vols[i];
3140 OSSpinLockUnlock(&This->lock);
3142 return S_OK;
3145 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3147 AudioStreamVolume_QueryInterface,
3148 AudioStreamVolume_AddRef,
3149 AudioStreamVolume_Release,
3150 AudioStreamVolume_GetChannelCount,
3151 AudioStreamVolume_SetChannelVolume,
3152 AudioStreamVolume_GetChannelVolume,
3153 AudioStreamVolume_SetAllVolumes,
3154 AudioStreamVolume_GetAllVolumes
3157 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3158 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3160 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3162 if(!ppv)
3163 return E_POINTER;
3164 *ppv = NULL;
3166 if(IsEqualIID(riid, &IID_IUnknown) ||
3167 IsEqualIID(riid, &IID_IChannelAudioVolume))
3168 *ppv = iface;
3169 if(*ppv){
3170 IUnknown_AddRef((IUnknown*)*ppv);
3171 return S_OK;
3174 WARN("Unknown interface %s\n", debugstr_guid(riid));
3175 return E_NOINTERFACE;
3178 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3180 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3181 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3184 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3186 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3187 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3190 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3191 IChannelAudioVolume *iface, UINT32 *out)
3193 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3194 AudioSession *session = This->session;
3196 TRACE("(%p)->(%p)\n", session, out);
3198 if(!out)
3199 return NULL_PTR_ERR;
3201 *out = session->channel_count;
3203 return S_OK;
3206 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3207 IChannelAudioVolume *iface, UINT32 index, float level,
3208 const GUID *context)
3210 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3211 AudioSession *session = This->session;
3212 HRESULT ret;
3214 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3215 wine_dbgstr_guid(context));
3217 if(level < 0.f || level > 1.f)
3218 return E_INVALIDARG;
3220 if(index >= session->channel_count)
3221 return E_INVALIDARG;
3223 if(context)
3224 FIXME("Notifications not supported yet\n");
3226 EnterCriticalSection(&session->lock);
3228 session->channel_vols[index] = level;
3230 WARN("CoreAudio doesn't support per-channel volume control\n");
3231 ret = ca_session_setvol(session, index);
3233 LeaveCriticalSection(&session->lock);
3235 return ret;
3238 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3239 IChannelAudioVolume *iface, UINT32 index, float *level)
3241 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3242 AudioSession *session = This->session;
3244 TRACE("(%p)->(%d, %p)\n", session, index, level);
3246 if(!level)
3247 return NULL_PTR_ERR;
3249 if(index >= session->channel_count)
3250 return E_INVALIDARG;
3252 *level = session->channel_vols[index];
3254 return S_OK;
3257 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3258 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3259 const GUID *context)
3261 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3262 AudioSession *session = This->session;
3263 int i;
3264 HRESULT ret;
3266 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3267 wine_dbgstr_guid(context));
3269 if(!levels)
3270 return NULL_PTR_ERR;
3272 if(count != session->channel_count)
3273 return E_INVALIDARG;
3275 if(context)
3276 FIXME("Notifications not supported yet\n");
3278 EnterCriticalSection(&session->lock);
3280 for(i = 0; i < count; ++i)
3281 session->channel_vols[i] = levels[i];
3283 ret = ca_session_setvol(session, -1);
3285 LeaveCriticalSection(&session->lock);
3287 return ret;
3290 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3291 IChannelAudioVolume *iface, UINT32 count, float *levels)
3293 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3294 AudioSession *session = This->session;
3295 int i;
3297 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3299 if(!levels)
3300 return NULL_PTR_ERR;
3302 if(count != session->channel_count)
3303 return E_INVALIDARG;
3305 for(i = 0; i < count; ++i)
3306 levels[i] = session->channel_vols[i];
3308 return S_OK;
3311 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3313 ChannelAudioVolume_QueryInterface,
3314 ChannelAudioVolume_AddRef,
3315 ChannelAudioVolume_Release,
3316 ChannelAudioVolume_GetChannelCount,
3317 ChannelAudioVolume_SetChannelVolume,
3318 ChannelAudioVolume_GetChannelVolume,
3319 ChannelAudioVolume_SetAllVolumes,
3320 ChannelAudioVolume_GetAllVolumes
3323 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3324 REFIID riid, void **ppv)
3326 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3328 if(!ppv)
3329 return E_POINTER;
3330 *ppv = NULL;
3332 if(IsEqualIID(riid, &IID_IUnknown) ||
3333 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3334 IsEqualIID(riid, &IID_IAudioSessionManager2))
3335 *ppv = iface;
3336 if(*ppv){
3337 IUnknown_AddRef((IUnknown*)*ppv);
3338 return S_OK;
3341 WARN("Unknown interface %s\n", debugstr_guid(riid));
3342 return E_NOINTERFACE;
3345 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3347 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3348 ULONG ref;
3349 ref = InterlockedIncrement(&This->ref);
3350 TRACE("(%p) Refcount now %u\n", This, ref);
3351 return ref;
3354 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3356 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3357 ULONG ref;
3358 ref = InterlockedDecrement(&This->ref);
3359 TRACE("(%p) Refcount now %u\n", This, ref);
3360 if(!ref)
3361 HeapFree(GetProcessHeap(), 0, This);
3362 return ref;
3365 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3366 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3367 IAudioSessionControl **out)
3369 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3370 AudioSession *session;
3371 AudioSessionWrapper *wrapper;
3372 HRESULT hr;
3374 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3375 flags, out);
3377 hr = get_audio_session(session_guid, This->device, 0, &session);
3378 if(FAILED(hr))
3379 return hr;
3381 wrapper = AudioSessionWrapper_Create(NULL);
3382 if(!wrapper)
3383 return E_OUTOFMEMORY;
3385 wrapper->session = session;
3387 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3389 return S_OK;
3392 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3393 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3394 ISimpleAudioVolume **out)
3396 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3397 AudioSession *session;
3398 AudioSessionWrapper *wrapper;
3399 HRESULT hr;
3401 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3402 flags, out);
3404 hr = get_audio_session(session_guid, This->device, 0, &session);
3405 if(FAILED(hr))
3406 return hr;
3408 wrapper = AudioSessionWrapper_Create(NULL);
3409 if(!wrapper)
3410 return E_OUTOFMEMORY;
3412 wrapper->session = session;
3414 *out = &wrapper->ISimpleAudioVolume_iface;
3416 return S_OK;
3419 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3420 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3422 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3423 FIXME("(%p)->(%p) - stub\n", This, out);
3424 return E_NOTIMPL;
3427 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3428 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3430 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3431 FIXME("(%p)->(%p) - stub\n", This, notification);
3432 return E_NOTIMPL;
3435 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3436 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3438 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3439 FIXME("(%p)->(%p) - stub\n", This, notification);
3440 return E_NOTIMPL;
3443 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3444 IAudioSessionManager2 *iface, const WCHAR *session_id,
3445 IAudioVolumeDuckNotification *notification)
3447 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3448 FIXME("(%p)->(%p) - stub\n", This, notification);
3449 return E_NOTIMPL;
3452 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3453 IAudioSessionManager2 *iface,
3454 IAudioVolumeDuckNotification *notification)
3456 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3457 FIXME("(%p)->(%p) - stub\n", This, notification);
3458 return E_NOTIMPL;
3461 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3463 AudioSessionManager_QueryInterface,
3464 AudioSessionManager_AddRef,
3465 AudioSessionManager_Release,
3466 AudioSessionManager_GetAudioSessionControl,
3467 AudioSessionManager_GetSimpleAudioVolume,
3468 AudioSessionManager_GetSessionEnumerator,
3469 AudioSessionManager_RegisterSessionNotification,
3470 AudioSessionManager_UnregisterSessionNotification,
3471 AudioSessionManager_RegisterDuckNotification,
3472 AudioSessionManager_UnregisterDuckNotification
3475 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3476 IAudioSessionManager2 **out)
3478 SessionMgr *This;
3480 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3481 if(!This)
3482 return E_OUTOFMEMORY;
3484 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3485 This->device = device;
3486 This->ref = 1;
3488 *out = &This->IAudioSessionManager2_iface;
3490 return S_OK;