winepulse: Add IAudioRenderClient and IAudioCaptureClient
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob62ae7ca4eb3de2eac0ac5d3de07850ddf505740d
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <sys/soundcard.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
45 #include "ole2.h"
46 #include "mmdeviceapi.h"
47 #include "devpkey.h"
48 #include "dshow.h"
49 #include "dsound.h"
51 #include "initguid.h"
52 #include "endpointvolume.h"
53 #include "audiopolicy.h"
54 #include "audioclient.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(oss);
58 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
60 static const REFERENCE_TIME DefaultPeriod = 200000;
61 static const REFERENCE_TIME MinimumPeriod = 100000;
63 struct ACImpl;
64 typedef struct ACImpl ACImpl;
66 typedef struct _AudioSession {
67 GUID guid;
68 struct list clients;
70 IMMDevice *device;
72 float master_vol;
73 UINT32 channel_count;
74 float *channel_vols;
75 BOOL mute;
77 CRITICAL_SECTION lock;
79 struct list entry;
80 } AudioSession;
82 typedef struct _AudioSessionWrapper {
83 IAudioSessionControl2 IAudioSessionControl2_iface;
84 IChannelAudioVolume IChannelAudioVolume_iface;
85 ISimpleAudioVolume ISimpleAudioVolume_iface;
87 LONG ref;
89 ACImpl *client;
90 AudioSession *session;
91 } AudioSessionWrapper;
93 struct ACImpl {
94 IAudioClient IAudioClient_iface;
95 IAudioRenderClient IAudioRenderClient_iface;
96 IAudioCaptureClient IAudioCaptureClient_iface;
97 IAudioClock IAudioClock_iface;
98 IAudioClock2 IAudioClock2_iface;
99 IAudioStreamVolume IAudioStreamVolume_iface;
101 LONG ref;
103 IMMDevice *parent;
105 WAVEFORMATEX *fmt;
107 EDataFlow dataflow;
108 DWORD flags;
109 AUDCLNT_SHAREMODE share;
110 HANDLE event;
111 float *vols;
113 int fd;
114 oss_audioinfo ai;
115 char devnode[OSS_DEVNODE_SIZE];
117 BOOL initted, playing;
118 UINT64 written_frames, last_pos_frames;
119 UINT32 period_us, period_frames, bufsize_frames, held_frames, tmp_buffer_frames;
120 UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
122 BYTE *local_buffer, *tmp_buffer;
123 LONG32 getbuf_last; /* <0 when using tmp_buffer */
124 HANDLE timer;
126 CRITICAL_SECTION lock;
128 AudioSession *session;
129 AudioSessionWrapper *session_wrapper;
131 struct list entry;
134 typedef struct _SessionMgr {
135 IAudioSessionManager2 IAudioSessionManager2_iface;
137 LONG ref;
139 IMMDevice *device;
140 } SessionMgr;
142 typedef struct _OSSDevice {
143 EDataFlow flow;
144 char devnode[OSS_DEVNODE_SIZE];
145 GUID guid;
147 struct list entry;
148 } OSSDevice;
150 static struct list g_devices = LIST_INIT(g_devices);
152 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
153 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
154 'w','i','n','e','o','s','s','.','d','r','v',0};
155 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
156 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
157 'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
158 static const WCHAR guidW[] = {'g','u','i','d',0};
160 static HANDLE g_timer_q;
162 static CRITICAL_SECTION g_sessions_lock;
163 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
165 0, 0, &g_sessions_lock,
166 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
167 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
169 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
170 static struct list g_sessions = LIST_INIT(g_sessions);
172 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
174 static const IAudioClientVtbl AudioClient_Vtbl;
175 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
176 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
177 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
178 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
179 static const IAudioClockVtbl AudioClock_Vtbl;
180 static const IAudioClock2Vtbl AudioClock2_Vtbl;
181 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
182 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
183 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
185 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
187 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
190 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
192 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
195 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
197 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
200 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
202 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
205 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
207 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
210 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
212 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
215 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
220 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
222 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
225 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
227 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
230 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
232 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
235 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
237 switch (reason)
239 case DLL_PROCESS_ATTACH:
240 g_timer_q = CreateTimerQueue();
241 if(!g_timer_q)
242 return FALSE;
243 break;
245 case DLL_PROCESS_DETACH:
247 OSSDevice *iter, *iter2;
249 DeleteCriticalSection(&g_sessions_lock);
251 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
252 HeapFree(GetProcessHeap(), 0, iter);
255 break;
258 return TRUE;
261 /* From <dlls/mmdevapi/mmdevapi.h> */
262 enum DriverPriority {
263 Priority_Unavailable = 0,
264 Priority_Low,
265 Priority_Neutral,
266 Priority_Preferred
269 int WINAPI AUDDRV_GetPriority(void)
271 int mixer_fd;
272 oss_sysinfo sysinfo;
274 /* Attempt to determine if we are running on OSS or ALSA's OSS
275 * compatibility layer. There is no official way to do that, so just check
276 * for validity as best as possible, without rejecting valid OSS
277 * implementations. */
279 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
280 if(mixer_fd < 0){
281 TRACE("Priority_Unavailable: open failed\n");
282 return Priority_Unavailable;
285 sysinfo.version[0] = 0xFF;
286 sysinfo.versionnum = ~0;
287 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
288 TRACE("Priority_Unavailable: ioctl failed\n");
289 close(mixer_fd);
290 return Priority_Unavailable;
293 close(mixer_fd);
295 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
296 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
297 return Priority_Low;
299 if(sysinfo.versionnum & 0x80000000){
300 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
301 return Priority_Low;
304 TRACE("Priority_Preferred: Seems like valid OSS!\n");
306 return Priority_Preferred;
309 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
310 GUID *guid)
312 HKEY key;
313 BOOL opened = FALSE;
314 LONG lr;
316 if(!drv_key){
317 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
318 NULL, &drv_key, NULL);
319 if(lr != ERROR_SUCCESS){
320 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
321 return;
323 opened = TRUE;
326 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
327 NULL, &key, NULL);
328 if(lr != ERROR_SUCCESS){
329 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
330 goto exit;
333 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
334 sizeof(GUID));
335 if(lr != ERROR_SUCCESS)
336 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
338 RegCloseKey(key);
339 exit:
340 if(opened)
341 RegCloseKey(drv_key);
344 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
346 HKEY key = NULL, dev_key;
347 DWORD type, size = sizeof(*guid);
348 WCHAR key_name[256];
350 if(flow == eCapture)
351 key_name[0] = '1';
352 else
353 key_name[0] = '0';
354 key_name[1] = ',';
355 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
356 (sizeof(key_name) / sizeof(*key_name)) - 2);
358 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
359 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
360 if(RegQueryValueExW(dev_key, guidW, 0, &type,
361 (BYTE*)guid, &size) == ERROR_SUCCESS){
362 if(type == REG_BINARY){
363 RegCloseKey(dev_key);
364 RegCloseKey(key);
365 return;
367 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
368 wine_dbgstr_w(key_name), type);
370 RegCloseKey(dev_key);
374 CoCreateGuid(guid);
376 set_device_guid(flow, key, key_name, guid);
378 if(key)
379 RegCloseKey(key);
382 static const char *oss_clean_devnode(const char *devnode)
384 static char ret[OSS_DEVNODE_SIZE];
386 const char *dot, *slash;
387 size_t len;
389 dot = strrchr(devnode, '.');
390 if(!dot)
391 return devnode;
393 slash = strrchr(devnode, '/');
394 if(slash && dot < slash)
395 return devnode;
397 len = dot - devnode;
399 memcpy(ret, devnode, len);
400 ret[len] = '\0';
402 return ret;
405 static UINT get_default_index(EDataFlow flow)
407 int fd = -1, err;
408 UINT i;
409 oss_audioinfo ai;
410 const char *devnode;
411 OSSDevice *dev_item;
413 if(flow == eRender)
414 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
415 else
416 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
418 if(fd < 0){
419 WARN("Couldn't open default device!\n");
420 return 0;
423 ai.dev = -1;
424 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
425 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
426 close(fd);
427 return 0;
430 close(fd);
432 TRACE("Default devnode: %s\n", ai.devnode);
433 devnode = oss_clean_devnode(ai.devnode);
434 i = 0;
435 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
436 if(dev_item->flow == flow){
437 if(!strcmp(devnode, dev_item->devnode))
438 return i;
439 ++i;
443 WARN("Couldn't find default device! Choosing first.\n");
444 return 0;
447 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
448 UINT *num, UINT *def_index)
450 int i, mixer_fd;
451 oss_sysinfo sysinfo;
452 static int print_once = 0;
454 static const WCHAR outW[] = {'O','u','t',':',' ',0};
455 static const WCHAR inW[] = {'I','n',':',' ',0};
457 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
459 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
460 if(mixer_fd < 0){
461 ERR("OSS /dev/mixer doesn't seem to exist\n");
462 return AUDCLNT_E_SERVICE_NOT_RUNNING;
465 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
466 close(mixer_fd);
468 if(errno == EINVAL){
469 ERR("OSS version too old, need at least OSSv4\n");
470 return AUDCLNT_E_SERVICE_NOT_RUNNING;
473 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
474 return E_FAIL;
477 if(!print_once){
478 TRACE("OSS sysinfo:\n");
479 TRACE("product: %s\n", sysinfo.product);
480 TRACE("version: %s\n", sysinfo.version);
481 TRACE("versionnum: %x\n", sysinfo.versionnum);
482 TRACE("numaudios: %d\n", sysinfo.numaudios);
483 TRACE("nummixers: %d\n", sysinfo.nummixers);
484 TRACE("numcards: %d\n", sysinfo.numcards);
485 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
486 print_once = 1;
489 if(sysinfo.numaudios <= 0){
490 WARN("No audio devices!\n");
491 close(mixer_fd);
492 return AUDCLNT_E_SERVICE_NOT_RUNNING;
495 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
496 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
498 *num = 0;
499 for(i = 0; i < sysinfo.numaudios; ++i){
500 oss_audioinfo ai = {0};
501 const char *devnode;
502 OSSDevice *dev_item;
503 int fd;
505 ai.dev = i;
506 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
507 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
508 strerror(errno));
509 continue;
512 devnode = oss_clean_devnode(ai.devnode);
514 /* check for duplicates */
515 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
516 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
517 break;
519 if(&dev_item->entry != &g_devices)
520 continue;
522 if(flow == eRender)
523 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
524 else
525 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
526 if(fd < 0){
527 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
528 devnode, errno, strerror(errno));
529 continue;
531 close(fd);
533 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
534 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
535 size_t len, prefix_len;
536 const WCHAR *prefix;
538 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
540 dev_item->flow = flow;
541 get_device_guid(flow, devnode, &dev_item->guid);
542 strcpy(dev_item->devnode, devnode);
544 (*guids)[*num] = dev_item->guid;
546 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
547 if(flow == eRender){
548 prefix = outW;
549 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
550 len += prefix_len;
551 }else{
552 prefix = inW;
553 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
554 len += prefix_len;
556 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
557 len * sizeof(WCHAR));
558 if(!(*ids)[*num]){
559 for(i = 0; i < *num; ++i)
560 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
561 HeapFree(GetProcessHeap(), 0, *ids);
562 HeapFree(GetProcessHeap(), 0, *guids);
563 HeapFree(GetProcessHeap(), 0, dev_item);
564 close(mixer_fd);
565 return E_OUTOFMEMORY;
567 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
568 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
569 (*ids)[*num] + prefix_len, len - prefix_len);
571 list_add_tail(&g_devices, &dev_item->entry);
573 (*num)++;
577 close(mixer_fd);
579 *def_index = get_default_index(flow);
581 return S_OK;
584 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
586 OSSDevice *dev_item;
587 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
588 if(IsEqualGUID(guid, &dev_item->guid))
589 return dev_item;
590 return NULL;
593 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
594 IAudioClient **out)
596 ACImpl *This;
597 const OSSDevice *oss_dev;
599 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
601 oss_dev = get_ossdevice_from_guid(guid);
602 if(!oss_dev){
603 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
604 return AUDCLNT_E_DEVICE_INVALIDATED;
607 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
608 if(!This)
609 return E_OUTOFMEMORY;
611 if(oss_dev->flow == eRender)
612 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
613 else if(oss_dev->flow == eCapture)
614 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
615 else{
616 HeapFree(GetProcessHeap(), 0, This);
617 return E_INVALIDARG;
619 if(This->fd < 0){
620 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
621 strerror(errno));
622 HeapFree(GetProcessHeap(), 0, This);
623 return AUDCLNT_E_DEVICE_INVALIDATED;
626 This->dataflow = oss_dev->flow;
628 This->ai.dev = -1;
629 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
630 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
631 errno, strerror(errno));
632 close(This->fd);
633 HeapFree(GetProcessHeap(), 0, This);
634 return E_FAIL;
637 strcpy(This->devnode, oss_dev->devnode);
639 TRACE("OSS audioinfo:\n");
640 TRACE("devnode: %s\n", This->ai.devnode);
641 TRACE("name: %s\n", This->ai.name);
642 TRACE("busy: %x\n", This->ai.busy);
643 TRACE("caps: %x\n", This->ai.caps);
644 TRACE("iformats: %x\n", This->ai.iformats);
645 TRACE("oformats: %x\n", This->ai.oformats);
646 TRACE("enabled: %d\n", This->ai.enabled);
647 TRACE("min_rate: %d\n", This->ai.min_rate);
648 TRACE("max_rate: %d\n", This->ai.max_rate);
649 TRACE("min_channels: %d\n", This->ai.min_channels);
650 TRACE("max_channels: %d\n", This->ai.max_channels);
652 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
653 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
654 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
655 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
656 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
657 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
659 InitializeCriticalSection(&This->lock);
660 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
662 This->parent = dev;
663 IMMDevice_AddRef(This->parent);
665 IAudioClient_AddRef(&This->IAudioClient_iface);
667 *out = &This->IAudioClient_iface;
669 return S_OK;
672 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
673 REFIID riid, void **ppv)
675 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
677 if(!ppv)
678 return E_POINTER;
679 *ppv = NULL;
680 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
681 *ppv = iface;
682 if(*ppv){
683 IUnknown_AddRef((IUnknown*)*ppv);
684 return S_OK;
686 WARN("Unknown interface %s\n", debugstr_guid(riid));
687 return E_NOINTERFACE;
690 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
692 ACImpl *This = impl_from_IAudioClient(iface);
693 ULONG ref;
694 ref = InterlockedIncrement(&This->ref);
695 TRACE("(%p) Refcount now %u\n", This, ref);
696 return ref;
699 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
701 ACImpl *This = impl_from_IAudioClient(iface);
702 ULONG ref;
703 ref = InterlockedDecrement(&This->ref);
704 TRACE("(%p) Refcount now %u\n", This, ref);
705 if(!ref){
706 IAudioClient_Stop(iface);
707 IMMDevice_Release(This->parent);
708 This->lock.DebugInfo->Spare[0] = 0;
709 DeleteCriticalSection(&This->lock);
710 close(This->fd);
711 if(This->initted){
712 EnterCriticalSection(&g_sessions_lock);
713 list_remove(&This->entry);
714 LeaveCriticalSection(&g_sessions_lock);
716 HeapFree(GetProcessHeap(), 0, This->vols);
717 HeapFree(GetProcessHeap(), 0, This->local_buffer);
718 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
719 CoTaskMemFree(This->fmt);
720 HeapFree(GetProcessHeap(), 0, This);
722 return ref;
725 static void dump_fmt(const WAVEFORMATEX *fmt)
727 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
728 switch(fmt->wFormatTag){
729 case WAVE_FORMAT_PCM:
730 TRACE("WAVE_FORMAT_PCM");
731 break;
732 case WAVE_FORMAT_IEEE_FLOAT:
733 TRACE("WAVE_FORMAT_IEEE_FLOAT");
734 break;
735 case WAVE_FORMAT_EXTENSIBLE:
736 TRACE("WAVE_FORMAT_EXTENSIBLE");
737 break;
738 default:
739 TRACE("Unknown");
740 break;
742 TRACE(")\n");
744 TRACE("nChannels: %u\n", fmt->nChannels);
745 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
746 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
747 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
748 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
749 TRACE("cbSize: %u\n", fmt->cbSize);
751 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
752 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
753 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
754 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
755 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
759 static DWORD get_channel_mask(unsigned int channels)
761 switch(channels){
762 case 0:
763 return 0;
764 case 1:
765 return KSAUDIO_SPEAKER_MONO;
766 case 2:
767 return KSAUDIO_SPEAKER_STEREO;
768 case 3:
769 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
770 case 4:
771 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
772 case 5:
773 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
774 case 6:
775 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
776 case 7:
777 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
778 case 8:
779 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
781 FIXME("Unknown speaker configuration: %u\n", channels);
782 return 0;
785 static int get_oss_format(const WAVEFORMATEX *fmt)
787 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
789 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
790 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
791 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
792 switch(fmt->wBitsPerSample){
793 case 8:
794 return AFMT_U8;
795 case 16:
796 return AFMT_S16_LE;
797 case 24:
798 return AFMT_S24_LE;
799 case 32:
800 return AFMT_S32_LE;
802 return -1;
805 #ifdef AFMT_FLOAT
806 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
807 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
808 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
809 if(fmt->wBitsPerSample != 32)
810 return -1;
812 return AFMT_FLOAT;
814 #endif
816 return -1;
819 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
821 WAVEFORMATEX *ret;
822 size_t size;
824 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
825 size = sizeof(WAVEFORMATEXTENSIBLE);
826 else
827 size = sizeof(WAVEFORMATEX);
829 ret = CoTaskMemAlloc(size);
830 if(!ret)
831 return NULL;
833 memcpy(ret, fmt, size);
835 ret->cbSize = size - sizeof(WAVEFORMATEX);
837 return ret;
840 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
841 WAVEFORMATEX **out, BOOL query)
843 int tmp, oss_format;
844 double tenth;
845 HRESULT ret = S_OK;
846 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
847 WAVEFORMATEX *closest = NULL;
849 tmp = oss_format = get_oss_format(fmt);
850 if(oss_format < 0)
851 return AUDCLNT_E_UNSUPPORTED_FORMAT;
852 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
853 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
854 return E_FAIL;
856 if(tmp != oss_format){
857 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
858 return AUDCLNT_E_UNSUPPORTED_FORMAT;
861 closest = clone_format(fmt);
862 if(!closest)
863 return E_OUTOFMEMORY;
865 tmp = fmt->nSamplesPerSec;
866 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
867 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
868 CoTaskMemFree(closest);
869 return E_FAIL;
871 tenth = fmt->nSamplesPerSec * 0.1;
872 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
873 ret = S_FALSE;
874 closest->nSamplesPerSec = tmp;
877 tmp = fmt->nChannels;
878 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
879 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
880 CoTaskMemFree(closest);
881 return E_FAIL;
883 if(tmp != fmt->nChannels){
884 ret = S_FALSE;
885 closest->nChannels = tmp;
888 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
889 DWORD mask = get_channel_mask(closest->nChannels);
891 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
893 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
894 fmtex->dwChannelMask != 0 &&
895 fmtex->dwChannelMask != mask)
896 ret = S_FALSE;
899 if(ret == S_FALSE && !out)
900 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
902 if(ret == S_FALSE && out){
903 closest->nBlockAlign =
904 closest->nChannels * closest->wBitsPerSample / 8;
905 closest->nAvgBytesPerSec =
906 closest->nBlockAlign * closest->nSamplesPerSec;
907 *out = closest;
908 } else
909 CoTaskMemFree(closest);
911 TRACE("returning: %08x\n", ret);
912 return ret;
915 static void session_init_vols(AudioSession *session, UINT channels)
917 if(session->channel_count < channels){
918 UINT i;
920 if(session->channel_vols)
921 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
922 session->channel_vols, sizeof(float) * channels);
923 else
924 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
925 sizeof(float) * channels);
926 if(!session->channel_vols)
927 return;
929 for(i = session->channel_count; i < channels; ++i)
930 session->channel_vols[i] = 1.f;
932 session->channel_count = channels;
936 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
937 UINT num_channels)
939 AudioSession *ret;
941 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
942 if(!ret)
943 return NULL;
945 memcpy(&ret->guid, guid, sizeof(GUID));
947 ret->device = device;
949 list_init(&ret->clients);
951 list_add_head(&g_sessions, &ret->entry);
953 InitializeCriticalSection(&ret->lock);
954 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
956 session_init_vols(ret, num_channels);
958 ret->master_vol = 1.f;
960 return ret;
963 /* if channels == 0, then this will return or create a session with
964 * matching dataflow and GUID. otherwise, channels must also match */
965 static HRESULT get_audio_session(const GUID *sessionguid,
966 IMMDevice *device, UINT channels, AudioSession **out)
968 AudioSession *session;
970 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
971 *out = create_session(&GUID_NULL, device, channels);
972 if(!*out)
973 return E_OUTOFMEMORY;
975 return S_OK;
978 *out = NULL;
979 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
980 if(session->device == device &&
981 IsEqualGUID(sessionguid, &session->guid)){
982 session_init_vols(session, channels);
983 *out = session;
984 break;
988 if(!*out){
989 *out = create_session(sessionguid, device, channels);
990 if(!*out)
991 return E_OUTOFMEMORY;
994 return S_OK;
997 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
998 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
999 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1000 const GUID *sessionguid)
1002 ACImpl *This = impl_from_IAudioClient(iface);
1003 int i;
1004 HRESULT hr;
1006 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1007 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1009 if(!fmt)
1010 return E_POINTER;
1012 dump_fmt(fmt);
1014 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1015 return AUDCLNT_E_NOT_INITIALIZED;
1017 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1018 AUDCLNT_STREAMFLAGS_LOOPBACK |
1019 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1020 AUDCLNT_STREAMFLAGS_NOPERSIST |
1021 AUDCLNT_STREAMFLAGS_RATEADJUST |
1022 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1023 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1024 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1025 TRACE("Unknown flags: %08x\n", flags);
1026 return E_INVALIDARG;
1029 if(mode == AUDCLNT_SHAREMODE_SHARED){
1030 period = DefaultPeriod;
1031 if( duration < 3 * period)
1032 duration = 3 * period;
1033 }else{
1034 if(!period)
1035 period = DefaultPeriod; /* not minimum */
1036 if(period < MinimumPeriod || period > 5000000)
1037 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1038 if(duration > 20000000) /* the smaller the period, the lower this limit */
1039 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1040 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1041 if(duration != period)
1042 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1043 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1044 return AUDCLNT_E_DEVICE_IN_USE;
1045 }else{
1046 if( duration < 8 * period)
1047 duration = 8 * period; /* may grow above 2s */
1051 EnterCriticalSection(&This->lock);
1053 if(This->initted){
1054 LeaveCriticalSection(&This->lock);
1055 return AUDCLNT_E_ALREADY_INITIALIZED;
1058 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
1059 if(FAILED(hr)){
1060 LeaveCriticalSection(&This->lock);
1061 return hr;
1064 This->fmt = clone_format(fmt);
1065 if(!This->fmt){
1066 LeaveCriticalSection(&This->lock);
1067 return E_OUTOFMEMORY;
1070 This->period_us = period / 10;
1071 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1073 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1074 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1075 This->bufsize_frames * fmt->nBlockAlign);
1076 if(!This->local_buffer){
1077 CoTaskMemFree(This->fmt);
1078 This->fmt = NULL;
1079 LeaveCriticalSection(&This->lock);
1080 return E_OUTOFMEMORY;
1083 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1084 if(!This->vols){
1085 CoTaskMemFree(This->fmt);
1086 This->fmt = NULL;
1087 LeaveCriticalSection(&This->lock);
1088 return E_OUTOFMEMORY;
1091 for(i = 0; i < fmt->nChannels; ++i)
1092 This->vols[i] = 1.f;
1094 This->share = mode;
1095 This->flags = flags;
1096 This->oss_bufsize_bytes = 0;
1098 EnterCriticalSection(&g_sessions_lock);
1100 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1101 &This->session);
1102 if(FAILED(hr)){
1103 LeaveCriticalSection(&g_sessions_lock);
1104 HeapFree(GetProcessHeap(), 0, This->vols);
1105 This->vols = NULL;
1106 CoTaskMemFree(This->fmt);
1107 This->fmt = NULL;
1108 LeaveCriticalSection(&This->lock);
1109 return hr;
1112 list_add_tail(&This->session->clients, &This->entry);
1114 LeaveCriticalSection(&g_sessions_lock);
1116 This->initted = TRUE;
1118 LeaveCriticalSection(&This->lock);
1120 return S_OK;
1123 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1124 UINT32 *frames)
1126 ACImpl *This = impl_from_IAudioClient(iface);
1128 TRACE("(%p)->(%p)\n", This, frames);
1130 if(!frames)
1131 return E_POINTER;
1133 EnterCriticalSection(&This->lock);
1135 if(!This->initted){
1136 LeaveCriticalSection(&This->lock);
1137 return AUDCLNT_E_NOT_INITIALIZED;
1140 *frames = This->bufsize_frames;
1142 TRACE("buffer size: %u\n", *frames);
1144 LeaveCriticalSection(&This->lock);
1146 return S_OK;
1149 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1150 REFERENCE_TIME *latency)
1152 ACImpl *This = impl_from_IAudioClient(iface);
1154 TRACE("(%p)->(%p)\n", This, latency);
1156 if(!latency)
1157 return E_POINTER;
1159 EnterCriticalSection(&This->lock);
1161 if(!This->initted){
1162 LeaveCriticalSection(&This->lock);
1163 return AUDCLNT_E_NOT_INITIALIZED;
1166 /* pretend we process audio in Period chunks, so max latency includes
1167 * the period time. Some native machines add .6666ms in shared mode. */
1168 *latency = This->period_us * 10 + 6666;
1170 LeaveCriticalSection(&This->lock);
1172 return S_OK;
1175 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1176 UINT32 *numpad)
1178 ACImpl *This = impl_from_IAudioClient(iface);
1180 TRACE("(%p)->(%p)\n", This, numpad);
1182 if(!numpad)
1183 return E_POINTER;
1185 EnterCriticalSection(&This->lock);
1187 if(!This->initted){
1188 LeaveCriticalSection(&This->lock);
1189 return AUDCLNT_E_NOT_INITIALIZED;
1192 *numpad = This->held_frames;
1194 TRACE("padding: %u\n", *numpad);
1196 LeaveCriticalSection(&This->lock);
1198 return S_OK;
1201 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1202 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1203 WAVEFORMATEX **outpwfx)
1205 ACImpl *This = impl_from_IAudioClient(iface);
1206 int fd = -1;
1207 HRESULT ret;
1209 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1211 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1212 return E_POINTER;
1214 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1215 return E_INVALIDARG;
1217 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1218 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1219 return E_INVALIDARG;
1221 dump_fmt(pwfx);
1223 if(outpwfx){
1224 *outpwfx = NULL;
1225 if(mode != AUDCLNT_SHAREMODE_SHARED)
1226 outpwfx = NULL;
1229 if(This->dataflow == eRender)
1230 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1231 else if(This->dataflow == eCapture)
1232 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1234 if(fd < 0){
1235 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1236 strerror(errno));
1237 return AUDCLNT_E_DEVICE_INVALIDATED;
1240 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1242 close(fd);
1244 return ret;
1247 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1248 WAVEFORMATEX **pwfx)
1250 ACImpl *This = impl_from_IAudioClient(iface);
1251 WAVEFORMATEXTENSIBLE *fmt;
1252 int formats;
1254 TRACE("(%p)->(%p)\n", This, pwfx);
1256 if(!pwfx)
1257 return E_POINTER;
1258 *pwfx = NULL;
1260 if(This->dataflow == eRender)
1261 formats = This->ai.oformats;
1262 else if(This->dataflow == eCapture)
1263 formats = This->ai.iformats;
1264 else
1265 return E_UNEXPECTED;
1267 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1268 if(!fmt)
1269 return E_OUTOFMEMORY;
1271 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1272 if(formats & AFMT_S16_LE){
1273 fmt->Format.wBitsPerSample = 16;
1274 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1275 #ifdef AFMT_FLOAT
1276 }else if(formats & AFMT_FLOAT){
1277 fmt->Format.wBitsPerSample = 32;
1278 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1279 #endif
1280 }else if(formats & AFMT_U8){
1281 fmt->Format.wBitsPerSample = 8;
1282 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1283 }else if(formats & AFMT_S32_LE){
1284 fmt->Format.wBitsPerSample = 32;
1285 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1286 }else if(formats & AFMT_S24_LE){
1287 fmt->Format.wBitsPerSample = 24;
1288 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1289 }else{
1290 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1291 CoTaskMemFree(fmt);
1292 return E_FAIL;
1295 fmt->Format.nChannels = This->ai.max_channels;
1296 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1297 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1299 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1300 fmt->Format.nChannels) / 8;
1301 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1302 fmt->Format.nBlockAlign;
1304 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1305 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1307 *pwfx = (WAVEFORMATEX*)fmt;
1308 dump_fmt(*pwfx);
1310 return S_OK;
1313 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1314 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1316 ACImpl *This = impl_from_IAudioClient(iface);
1318 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1320 if(!defperiod && !minperiod)
1321 return E_POINTER;
1323 if(defperiod)
1324 *defperiod = DefaultPeriod;
1325 if(minperiod)
1326 *minperiod = MinimumPeriod;
1328 return S_OK;
1331 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1333 if(This->fmt->wBitsPerSample == 8)
1334 memset(buf, 128, frames * This->fmt->nBlockAlign);
1335 else
1336 memset(buf, 0, frames * This->fmt->nBlockAlign);
1339 static void oss_write_data(ACImpl *This)
1341 ssize_t written_bytes;
1342 UINT32 written_frames, in_oss_frames, write_limit, max_period;
1343 size_t to_write_frames, to_write_bytes;
1344 audio_buf_info bi;
1345 BYTE *buf =
1346 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1348 if(This->held_frames == 0)
1349 return;
1351 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1352 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1353 else
1354 to_write_frames = This->held_frames;
1356 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1357 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1358 return;
1361 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1363 if(bi.bytes > This->oss_bufsize_bytes){
1364 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1365 bi.bytes, This->oss_bufsize_bytes);
1366 This->oss_bufsize_bytes = bi.bytes;
1367 in_oss_frames = 0;
1368 }else if(This->oss_bufsize_bytes - bi.bytes <= bi.fragsize)
1369 in_oss_frames = 0;
1370 else
1371 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1373 write_limit = 0;
1374 while(write_limit + in_oss_frames < max_period * 3)
1375 write_limit += max_period;
1376 if(write_limit == 0)
1377 return;
1379 to_write_frames = min(to_write_frames, write_limit);
1380 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1382 if(This->session->mute)
1383 oss_silence_buffer(This, buf, to_write_frames);
1385 written_bytes = write(This->fd, buf, to_write_bytes);
1386 if(written_bytes < 0){
1387 /* EAGAIN is OSS buffer full, log that too */
1388 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1389 return;
1391 written_frames = written_bytes / This->fmt->nBlockAlign;
1393 This->lcl_offs_frames += written_frames;
1394 This->lcl_offs_frames %= This->bufsize_frames;
1395 This->held_frames -= written_frames;
1397 if(written_frames < to_write_frames){
1398 /* OSS buffer probably full */
1399 return;
1402 if(This->held_frames && written_frames < write_limit){
1403 /* wrapped and have some data back at the start to write */
1405 to_write_frames = min(write_limit - written_frames, This->held_frames);
1406 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1408 if(This->session->mute)
1409 oss_silence_buffer(This, This->local_buffer, to_write_frames);
1411 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1412 if(written_bytes < 0){
1413 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1414 return;
1416 written_frames = written_bytes / This->fmt->nBlockAlign;
1418 This->lcl_offs_frames += written_frames;
1419 This->lcl_offs_frames %= This->bufsize_frames;
1420 This->held_frames -= written_frames;
1424 static void oss_read_data(ACImpl *This)
1426 UINT64 pos, readable;
1427 audio_buf_info bi;
1428 ssize_t nread;
1430 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1431 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1432 return;
1435 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1436 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1438 if(bi.bytes < readable)
1439 readable = bi.bytes;
1441 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1442 readable);
1443 if(nread < 0){
1444 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1445 return;
1448 This->held_frames += nread / This->fmt->nBlockAlign;
1450 if(This->held_frames > This->bufsize_frames){
1451 WARN("Overflow of unread data\n");
1452 This->lcl_offs_frames += This->held_frames;
1453 This->lcl_offs_frames %= This->bufsize_frames;
1454 This->held_frames = This->bufsize_frames;
1458 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1460 ACImpl *This = user;
1462 EnterCriticalSection(&This->lock);
1464 if(This->playing){
1465 if(This->dataflow == eRender && This->held_frames)
1466 oss_write_data(This);
1467 else if(This->dataflow == eCapture)
1468 oss_read_data(This);
1470 if(This->event)
1471 SetEvent(This->event);
1474 LeaveCriticalSection(&This->lock);
1477 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1479 ACImpl *This = impl_from_IAudioClient(iface);
1481 TRACE("(%p)\n", This);
1483 EnterCriticalSection(&This->lock);
1485 if(!This->initted){
1486 LeaveCriticalSection(&This->lock);
1487 return AUDCLNT_E_NOT_INITIALIZED;
1490 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1491 LeaveCriticalSection(&This->lock);
1492 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1495 if(This->playing){
1496 LeaveCriticalSection(&This->lock);
1497 return AUDCLNT_E_NOT_STOPPED;
1500 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1501 oss_period_callback, This, 0, This->period_us / 1000,
1502 WT_EXECUTEINTIMERTHREAD))
1503 ERR("Unable to create period timer: %u\n", GetLastError());
1505 This->playing = TRUE;
1507 LeaveCriticalSection(&This->lock);
1509 return S_OK;
1512 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1514 ACImpl *This = impl_from_IAudioClient(iface);
1515 HANDLE event;
1516 DWORD wait;
1518 TRACE("(%p)\n", This);
1520 EnterCriticalSection(&This->lock);
1522 if(!This->initted){
1523 LeaveCriticalSection(&This->lock);
1524 return AUDCLNT_E_NOT_INITIALIZED;
1527 if(!This->playing){
1528 LeaveCriticalSection(&This->lock);
1529 return S_FALSE;
1532 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1533 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1534 if(wait)
1535 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1536 wait = wait && GetLastError() == ERROR_IO_PENDING;
1538 This->playing = FALSE;
1540 LeaveCriticalSection(&This->lock);
1542 if(event && wait)
1543 WaitForSingleObject(event, INFINITE);
1544 CloseHandle(event);
1546 return S_OK;
1549 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1551 ACImpl *This = impl_from_IAudioClient(iface);
1553 TRACE("(%p)\n", This);
1555 EnterCriticalSection(&This->lock);
1557 if(!This->initted){
1558 LeaveCriticalSection(&This->lock);
1559 return AUDCLNT_E_NOT_INITIALIZED;
1562 if(This->playing){
1563 LeaveCriticalSection(&This->lock);
1564 return AUDCLNT_E_NOT_STOPPED;
1567 if(This->getbuf_last){
1568 LeaveCriticalSection(&This->lock);
1569 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1572 if(This->dataflow == eRender){
1573 This->written_frames = 0;
1574 This->last_pos_frames = 0;
1575 }else{
1576 This->written_frames += This->held_frames;
1578 This->held_frames = 0;
1579 This->lcl_offs_frames = 0;
1581 LeaveCriticalSection(&This->lock);
1583 return S_OK;
1586 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1587 HANDLE event)
1589 ACImpl *This = impl_from_IAudioClient(iface);
1591 TRACE("(%p)->(%p)\n", This, event);
1593 if(!event)
1594 return E_INVALIDARG;
1596 EnterCriticalSection(&This->lock);
1598 if(!This->initted){
1599 LeaveCriticalSection(&This->lock);
1600 return AUDCLNT_E_NOT_INITIALIZED;
1603 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1604 LeaveCriticalSection(&This->lock);
1605 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1608 This->event = event;
1610 LeaveCriticalSection(&This->lock);
1612 return S_OK;
1615 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1616 void **ppv)
1618 ACImpl *This = impl_from_IAudioClient(iface);
1620 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1622 if(!ppv)
1623 return E_POINTER;
1624 *ppv = NULL;
1626 EnterCriticalSection(&This->lock);
1628 if(!This->initted){
1629 LeaveCriticalSection(&This->lock);
1630 return AUDCLNT_E_NOT_INITIALIZED;
1633 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1634 if(This->dataflow != eRender){
1635 LeaveCriticalSection(&This->lock);
1636 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1638 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1639 *ppv = &This->IAudioRenderClient_iface;
1640 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1641 if(This->dataflow != eCapture){
1642 LeaveCriticalSection(&This->lock);
1643 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1645 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1646 *ppv = &This->IAudioCaptureClient_iface;
1647 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1648 IAudioClock_AddRef(&This->IAudioClock_iface);
1649 *ppv = &This->IAudioClock_iface;
1650 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1651 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1652 *ppv = &This->IAudioStreamVolume_iface;
1653 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1654 if(!This->session_wrapper){
1655 This->session_wrapper = AudioSessionWrapper_Create(This);
1656 if(!This->session_wrapper){
1657 LeaveCriticalSection(&This->lock);
1658 return E_OUTOFMEMORY;
1660 }else
1661 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1663 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1664 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1665 if(!This->session_wrapper){
1666 This->session_wrapper = AudioSessionWrapper_Create(This);
1667 if(!This->session_wrapper){
1668 LeaveCriticalSection(&This->lock);
1669 return E_OUTOFMEMORY;
1671 }else
1672 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1674 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1675 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1676 if(!This->session_wrapper){
1677 This->session_wrapper = AudioSessionWrapper_Create(This);
1678 if(!This->session_wrapper){
1679 LeaveCriticalSection(&This->lock);
1680 return E_OUTOFMEMORY;
1682 }else
1683 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1685 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1688 if(*ppv){
1689 LeaveCriticalSection(&This->lock);
1690 return S_OK;
1693 LeaveCriticalSection(&This->lock);
1695 FIXME("stub %s\n", debugstr_guid(riid));
1696 return E_NOINTERFACE;
1699 static const IAudioClientVtbl AudioClient_Vtbl =
1701 AudioClient_QueryInterface,
1702 AudioClient_AddRef,
1703 AudioClient_Release,
1704 AudioClient_Initialize,
1705 AudioClient_GetBufferSize,
1706 AudioClient_GetStreamLatency,
1707 AudioClient_GetCurrentPadding,
1708 AudioClient_IsFormatSupported,
1709 AudioClient_GetMixFormat,
1710 AudioClient_GetDevicePeriod,
1711 AudioClient_Start,
1712 AudioClient_Stop,
1713 AudioClient_Reset,
1714 AudioClient_SetEventHandle,
1715 AudioClient_GetService
1718 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1719 IAudioRenderClient *iface, REFIID riid, void **ppv)
1721 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1723 if(!ppv)
1724 return E_POINTER;
1725 *ppv = NULL;
1727 if(IsEqualIID(riid, &IID_IUnknown) ||
1728 IsEqualIID(riid, &IID_IAudioRenderClient))
1729 *ppv = iface;
1730 if(*ppv){
1731 IUnknown_AddRef((IUnknown*)*ppv);
1732 return S_OK;
1735 WARN("Unknown interface %s\n", debugstr_guid(riid));
1736 return E_NOINTERFACE;
1739 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1741 ACImpl *This = impl_from_IAudioRenderClient(iface);
1742 return AudioClient_AddRef(&This->IAudioClient_iface);
1745 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1747 ACImpl *This = impl_from_IAudioRenderClient(iface);
1748 return AudioClient_Release(&This->IAudioClient_iface);
1751 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1752 UINT32 frames, BYTE **data)
1754 ACImpl *This = impl_from_IAudioRenderClient(iface);
1755 UINT32 write_pos;
1757 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1759 if(!data)
1760 return E_POINTER;
1762 *data = NULL;
1764 EnterCriticalSection(&This->lock);
1766 if(This->getbuf_last){
1767 LeaveCriticalSection(&This->lock);
1768 return AUDCLNT_E_OUT_OF_ORDER;
1771 if(!frames){
1772 LeaveCriticalSection(&This->lock);
1773 return S_OK;
1776 if(This->held_frames + frames > This->bufsize_frames){
1777 LeaveCriticalSection(&This->lock);
1778 return AUDCLNT_E_BUFFER_TOO_LARGE;
1781 write_pos =
1782 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1783 if(write_pos + frames > This->bufsize_frames){
1784 if(This->tmp_buffer_frames < frames){
1785 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1786 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1787 frames * This->fmt->nBlockAlign);
1788 if(!This->tmp_buffer){
1789 LeaveCriticalSection(&This->lock);
1790 return E_OUTOFMEMORY;
1792 This->tmp_buffer_frames = frames;
1794 *data = This->tmp_buffer;
1795 This->getbuf_last = -frames;
1796 }else{
1797 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1798 This->getbuf_last = frames;
1801 LeaveCriticalSection(&This->lock);
1803 return S_OK;
1806 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1808 UINT32 write_offs_frames =
1809 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1810 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1811 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1812 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1813 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1815 if(written_bytes <= chunk_bytes){
1816 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1817 }else{
1818 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1819 memcpy(This->local_buffer, buffer + chunk_bytes,
1820 written_bytes - chunk_bytes);
1824 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1825 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1827 ACImpl *This = impl_from_IAudioRenderClient(iface);
1828 BYTE *buffer;
1830 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1832 EnterCriticalSection(&This->lock);
1834 if(!written_frames){
1835 This->getbuf_last = 0;
1836 LeaveCriticalSection(&This->lock);
1837 return S_OK;
1840 if(!This->getbuf_last){
1841 LeaveCriticalSection(&This->lock);
1842 return AUDCLNT_E_OUT_OF_ORDER;
1845 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1846 LeaveCriticalSection(&This->lock);
1847 return AUDCLNT_E_INVALID_SIZE;
1850 if(This->getbuf_last >= 0)
1851 buffer = This->local_buffer + This->fmt->nBlockAlign *
1852 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1853 else
1854 buffer = This->tmp_buffer;
1856 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1857 oss_silence_buffer(This, buffer, written_frames);
1859 if(This->getbuf_last < 0)
1860 oss_wrap_buffer(This, buffer, written_frames);
1862 This->held_frames += written_frames;
1863 This->written_frames += written_frames;
1864 This->getbuf_last = 0;
1866 LeaveCriticalSection(&This->lock);
1868 return S_OK;
1871 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1872 AudioRenderClient_QueryInterface,
1873 AudioRenderClient_AddRef,
1874 AudioRenderClient_Release,
1875 AudioRenderClient_GetBuffer,
1876 AudioRenderClient_ReleaseBuffer
1879 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1880 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1882 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1884 if(!ppv)
1885 return E_POINTER;
1886 *ppv = NULL;
1888 if(IsEqualIID(riid, &IID_IUnknown) ||
1889 IsEqualIID(riid, &IID_IAudioCaptureClient))
1890 *ppv = iface;
1891 if(*ppv){
1892 IUnknown_AddRef((IUnknown*)*ppv);
1893 return S_OK;
1896 WARN("Unknown interface %s\n", debugstr_guid(riid));
1897 return E_NOINTERFACE;
1900 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1902 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1903 return IAudioClient_AddRef(&This->IAudioClient_iface);
1906 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1908 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1909 return IAudioClient_Release(&This->IAudioClient_iface);
1912 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1913 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1914 UINT64 *qpcpos)
1916 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1918 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1919 devpos, qpcpos);
1921 if(!data || !frames || !flags)
1922 return E_POINTER;
1924 EnterCriticalSection(&This->lock);
1926 if(This->getbuf_last){
1927 LeaveCriticalSection(&This->lock);
1928 return AUDCLNT_E_OUT_OF_ORDER;
1931 if(This->held_frames < This->period_frames){
1932 *frames = 0;
1933 LeaveCriticalSection(&This->lock);
1934 return AUDCLNT_S_BUFFER_EMPTY;
1937 *flags = 0;
1939 *frames = This->period_frames;
1941 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1942 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1943 if(This->tmp_buffer_frames < *frames){
1944 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1945 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1946 *frames * This->fmt->nBlockAlign);
1947 if(!This->tmp_buffer){
1948 LeaveCriticalSection(&This->lock);
1949 return E_OUTOFMEMORY;
1951 This->tmp_buffer_frames = *frames;
1954 *data = This->tmp_buffer;
1955 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1956 This->fmt->nBlockAlign;
1957 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1958 frames_bytes = *frames * This->fmt->nBlockAlign;
1959 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1960 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
1961 frames_bytes - chunk_bytes);
1962 }else
1963 *data = This->local_buffer +
1964 This->lcl_offs_frames * This->fmt->nBlockAlign;
1966 This->getbuf_last = *frames;
1968 if(devpos)
1969 *devpos = This->written_frames;
1970 if(qpcpos){
1971 LARGE_INTEGER stamp, freq;
1972 QueryPerformanceCounter(&stamp);
1973 QueryPerformanceFrequency(&freq);
1974 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1977 LeaveCriticalSection(&This->lock);
1979 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1982 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1983 IAudioCaptureClient *iface, UINT32 done)
1985 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1987 TRACE("(%p)->(%u)\n", This, done);
1989 EnterCriticalSection(&This->lock);
1991 if(!done){
1992 This->getbuf_last = 0;
1993 LeaveCriticalSection(&This->lock);
1994 return S_OK;
1997 if(!This->getbuf_last){
1998 LeaveCriticalSection(&This->lock);
1999 return AUDCLNT_E_OUT_OF_ORDER;
2002 if(This->getbuf_last != done){
2003 LeaveCriticalSection(&This->lock);
2004 return AUDCLNT_E_INVALID_SIZE;
2007 This->written_frames += done;
2008 This->held_frames -= done;
2009 This->lcl_offs_frames += done;
2010 This->lcl_offs_frames %= This->bufsize_frames;
2011 This->getbuf_last = 0;
2013 LeaveCriticalSection(&This->lock);
2015 return S_OK;
2018 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2019 IAudioCaptureClient *iface, UINT32 *frames)
2021 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2023 TRACE("(%p)->(%p)\n", This, frames);
2025 if(!frames)
2026 return E_POINTER;
2028 EnterCriticalSection(&This->lock);
2030 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2032 LeaveCriticalSection(&This->lock);
2034 return S_OK;
2037 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2039 AudioCaptureClient_QueryInterface,
2040 AudioCaptureClient_AddRef,
2041 AudioCaptureClient_Release,
2042 AudioCaptureClient_GetBuffer,
2043 AudioCaptureClient_ReleaseBuffer,
2044 AudioCaptureClient_GetNextPacketSize
2047 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2048 REFIID riid, void **ppv)
2050 ACImpl *This = impl_from_IAudioClock(iface);
2052 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2054 if(!ppv)
2055 return E_POINTER;
2056 *ppv = NULL;
2058 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2059 *ppv = iface;
2060 else if(IsEqualIID(riid, &IID_IAudioClock2))
2061 *ppv = &This->IAudioClock2_iface;
2062 if(*ppv){
2063 IUnknown_AddRef((IUnknown*)*ppv);
2064 return S_OK;
2067 WARN("Unknown interface %s\n", debugstr_guid(riid));
2068 return E_NOINTERFACE;
2071 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2073 ACImpl *This = impl_from_IAudioClock(iface);
2074 return IAudioClient_AddRef(&This->IAudioClient_iface);
2077 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2079 ACImpl *This = impl_from_IAudioClock(iface);
2080 return IAudioClient_Release(&This->IAudioClient_iface);
2083 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2085 ACImpl *This = impl_from_IAudioClock(iface);
2087 TRACE("(%p)->(%p)\n", This, freq);
2089 *freq = This->fmt->nSamplesPerSec;
2091 return S_OK;
2094 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2095 UINT64 *qpctime)
2097 ACImpl *This = impl_from_IAudioClock(iface);
2098 int delay;
2100 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2102 if(!pos)
2103 return E_POINTER;
2105 EnterCriticalSection(&This->lock);
2107 if(This->dataflow == eRender){
2108 if(!This->playing || !This->held_frames ||
2109 ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay) < 0)
2110 delay = 0;
2111 else
2112 delay /= This->fmt->nBlockAlign;
2113 if(This->held_frames + delay >= This->written_frames)
2114 *pos = This->last_pos_frames;
2115 else{
2116 *pos = This->written_frames - This->held_frames - delay;
2117 if(*pos < This->last_pos_frames)
2118 *pos = This->last_pos_frames;
2120 }else if(This->dataflow == eCapture){
2121 audio_buf_info bi;
2122 UINT32 held;
2124 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2125 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2126 held = 0;
2127 }else{
2128 if(bi.bytes <= bi.fragsize)
2129 held = 0;
2130 else
2131 held = bi.bytes / This->fmt->nBlockAlign;
2134 *pos = This->written_frames + held;
2137 This->last_pos_frames = *pos;
2139 LeaveCriticalSection(&This->lock);
2141 if(qpctime){
2142 LARGE_INTEGER stamp, freq;
2143 QueryPerformanceCounter(&stamp);
2144 QueryPerformanceFrequency(&freq);
2145 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2148 return S_OK;
2151 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2152 DWORD *chars)
2154 ACImpl *This = impl_from_IAudioClock(iface);
2156 TRACE("(%p)->(%p)\n", This, chars);
2158 if(!chars)
2159 return E_POINTER;
2161 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2163 return S_OK;
2166 static const IAudioClockVtbl AudioClock_Vtbl =
2168 AudioClock_QueryInterface,
2169 AudioClock_AddRef,
2170 AudioClock_Release,
2171 AudioClock_GetFrequency,
2172 AudioClock_GetPosition,
2173 AudioClock_GetCharacteristics
2176 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2177 REFIID riid, void **ppv)
2179 ACImpl *This = impl_from_IAudioClock2(iface);
2180 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2183 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2185 ACImpl *This = impl_from_IAudioClock2(iface);
2186 return IAudioClient_AddRef(&This->IAudioClient_iface);
2189 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2191 ACImpl *This = impl_from_IAudioClock2(iface);
2192 return IAudioClient_Release(&This->IAudioClient_iface);
2195 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2196 UINT64 *pos, UINT64 *qpctime)
2198 ACImpl *This = impl_from_IAudioClock2(iface);
2200 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2202 return E_NOTIMPL;
2205 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2207 AudioClock2_QueryInterface,
2208 AudioClock2_AddRef,
2209 AudioClock2_Release,
2210 AudioClock2_GetDevicePosition
2213 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2215 AudioSessionWrapper *ret;
2217 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2218 sizeof(AudioSessionWrapper));
2219 if(!ret)
2220 return NULL;
2222 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2223 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2224 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2226 ret->ref = 1;
2228 ret->client = client;
2229 if(client){
2230 ret->session = client->session;
2231 AudioClient_AddRef(&client->IAudioClient_iface);
2234 return ret;
2237 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2238 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2240 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2242 if(!ppv)
2243 return E_POINTER;
2244 *ppv = NULL;
2246 if(IsEqualIID(riid, &IID_IUnknown) ||
2247 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2248 IsEqualIID(riid, &IID_IAudioSessionControl2))
2249 *ppv = iface;
2250 if(*ppv){
2251 IUnknown_AddRef((IUnknown*)*ppv);
2252 return S_OK;
2255 WARN("Unknown interface %s\n", debugstr_guid(riid));
2256 return E_NOINTERFACE;
2259 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2261 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2262 ULONG ref;
2263 ref = InterlockedIncrement(&This->ref);
2264 TRACE("(%p) Refcount now %u\n", This, ref);
2265 return ref;
2268 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2270 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2271 ULONG ref;
2272 ref = InterlockedDecrement(&This->ref);
2273 TRACE("(%p) Refcount now %u\n", This, ref);
2274 if(!ref){
2275 if(This->client){
2276 EnterCriticalSection(&This->client->lock);
2277 This->client->session_wrapper = NULL;
2278 LeaveCriticalSection(&This->client->lock);
2279 AudioClient_Release(&This->client->IAudioClient_iface);
2281 HeapFree(GetProcessHeap(), 0, This);
2283 return ref;
2286 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2287 AudioSessionState *state)
2289 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2290 ACImpl *client;
2292 TRACE("(%p)->(%p)\n", This, state);
2294 if(!state)
2295 return NULL_PTR_ERR;
2297 EnterCriticalSection(&g_sessions_lock);
2299 if(list_empty(&This->session->clients)){
2300 *state = AudioSessionStateExpired;
2301 LeaveCriticalSection(&g_sessions_lock);
2302 return S_OK;
2305 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2306 EnterCriticalSection(&client->lock);
2307 if(client->playing){
2308 *state = AudioSessionStateActive;
2309 LeaveCriticalSection(&client->lock);
2310 LeaveCriticalSection(&g_sessions_lock);
2311 return S_OK;
2313 LeaveCriticalSection(&client->lock);
2316 LeaveCriticalSection(&g_sessions_lock);
2318 *state = AudioSessionStateInactive;
2320 return S_OK;
2323 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2324 IAudioSessionControl2 *iface, WCHAR **name)
2326 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2328 FIXME("(%p)->(%p) - stub\n", This, name);
2330 return E_NOTIMPL;
2333 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2334 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2336 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2338 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2340 return E_NOTIMPL;
2343 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2344 IAudioSessionControl2 *iface, WCHAR **path)
2346 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2348 FIXME("(%p)->(%p) - stub\n", This, path);
2350 return E_NOTIMPL;
2353 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2354 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2356 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2358 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2360 return E_NOTIMPL;
2363 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2364 IAudioSessionControl2 *iface, GUID *group)
2366 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2368 FIXME("(%p)->(%p) - stub\n", This, group);
2370 return E_NOTIMPL;
2373 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2374 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2376 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2378 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2379 debugstr_guid(session));
2381 return E_NOTIMPL;
2384 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2385 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2387 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2389 FIXME("(%p)->(%p) - stub\n", This, events);
2391 return S_OK;
2394 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2395 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2397 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2399 FIXME("(%p)->(%p) - stub\n", This, events);
2401 return S_OK;
2404 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2405 IAudioSessionControl2 *iface, WCHAR **id)
2407 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2409 FIXME("(%p)->(%p) - stub\n", This, id);
2411 return E_NOTIMPL;
2414 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2415 IAudioSessionControl2 *iface, WCHAR **id)
2417 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2419 FIXME("(%p)->(%p) - stub\n", This, id);
2421 return E_NOTIMPL;
2424 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2425 IAudioSessionControl2 *iface, DWORD *pid)
2427 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2429 TRACE("(%p)->(%p)\n", This, pid);
2431 if(!pid)
2432 return E_POINTER;
2434 *pid = GetCurrentProcessId();
2436 return S_OK;
2439 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2440 IAudioSessionControl2 *iface)
2442 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2444 TRACE("(%p)\n", This);
2446 return S_FALSE;
2449 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2450 IAudioSessionControl2 *iface, BOOL optout)
2452 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2454 TRACE("(%p)->(%d)\n", This, optout);
2456 return S_OK;
2459 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2461 AudioSessionControl_QueryInterface,
2462 AudioSessionControl_AddRef,
2463 AudioSessionControl_Release,
2464 AudioSessionControl_GetState,
2465 AudioSessionControl_GetDisplayName,
2466 AudioSessionControl_SetDisplayName,
2467 AudioSessionControl_GetIconPath,
2468 AudioSessionControl_SetIconPath,
2469 AudioSessionControl_GetGroupingParam,
2470 AudioSessionControl_SetGroupingParam,
2471 AudioSessionControl_RegisterAudioSessionNotification,
2472 AudioSessionControl_UnregisterAudioSessionNotification,
2473 AudioSessionControl_GetSessionIdentifier,
2474 AudioSessionControl_GetSessionInstanceIdentifier,
2475 AudioSessionControl_GetProcessId,
2476 AudioSessionControl_IsSystemSoundsSession,
2477 AudioSessionControl_SetDuckingPreference
2480 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2481 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2483 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2485 if(!ppv)
2486 return E_POINTER;
2487 *ppv = NULL;
2489 if(IsEqualIID(riid, &IID_IUnknown) ||
2490 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2491 *ppv = iface;
2492 if(*ppv){
2493 IUnknown_AddRef((IUnknown*)*ppv);
2494 return S_OK;
2497 WARN("Unknown interface %s\n", debugstr_guid(riid));
2498 return E_NOINTERFACE;
2501 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2503 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2504 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2507 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2509 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2510 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2513 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2514 ISimpleAudioVolume *iface, float level, const GUID *context)
2516 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2517 AudioSession *session = This->session;
2519 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2521 if(level < 0.f || level > 1.f)
2522 return E_INVALIDARG;
2524 if(context)
2525 FIXME("Notifications not supported yet\n");
2527 EnterCriticalSection(&session->lock);
2529 session->master_vol = level;
2531 TRACE("OSS doesn't support setting volume\n");
2533 LeaveCriticalSection(&session->lock);
2535 return S_OK;
2538 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2539 ISimpleAudioVolume *iface, float *level)
2541 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2542 AudioSession *session = This->session;
2544 TRACE("(%p)->(%p)\n", session, level);
2546 if(!level)
2547 return NULL_PTR_ERR;
2549 *level = session->master_vol;
2551 return S_OK;
2554 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2555 BOOL mute, const GUID *context)
2557 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2558 AudioSession *session = This->session;
2560 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2562 EnterCriticalSection(&session->lock);
2564 session->mute = mute;
2566 LeaveCriticalSection(&session->lock);
2568 return S_OK;
2571 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2572 BOOL *mute)
2574 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2575 AudioSession *session = This->session;
2577 TRACE("(%p)->(%p)\n", session, mute);
2579 if(!mute)
2580 return NULL_PTR_ERR;
2582 *mute = This->session->mute;
2584 return S_OK;
2587 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2589 SimpleAudioVolume_QueryInterface,
2590 SimpleAudioVolume_AddRef,
2591 SimpleAudioVolume_Release,
2592 SimpleAudioVolume_SetMasterVolume,
2593 SimpleAudioVolume_GetMasterVolume,
2594 SimpleAudioVolume_SetMute,
2595 SimpleAudioVolume_GetMute
2598 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2599 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2601 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2603 if(!ppv)
2604 return E_POINTER;
2605 *ppv = NULL;
2607 if(IsEqualIID(riid, &IID_IUnknown) ||
2608 IsEqualIID(riid, &IID_IAudioStreamVolume))
2609 *ppv = iface;
2610 if(*ppv){
2611 IUnknown_AddRef((IUnknown*)*ppv);
2612 return S_OK;
2615 WARN("Unknown interface %s\n", debugstr_guid(riid));
2616 return E_NOINTERFACE;
2619 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2621 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2622 return IAudioClient_AddRef(&This->IAudioClient_iface);
2625 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2627 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2628 return IAudioClient_Release(&This->IAudioClient_iface);
2631 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2632 IAudioStreamVolume *iface, UINT32 *out)
2634 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2636 TRACE("(%p)->(%p)\n", This, out);
2638 if(!out)
2639 return E_POINTER;
2641 *out = This->fmt->nChannels;
2643 return S_OK;
2646 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2647 IAudioStreamVolume *iface, UINT32 index, float level)
2649 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2651 TRACE("(%p)->(%d, %f)\n", This, index, level);
2653 if(level < 0.f || level > 1.f)
2654 return E_INVALIDARG;
2656 if(index >= This->fmt->nChannels)
2657 return E_INVALIDARG;
2659 EnterCriticalSection(&This->lock);
2661 This->vols[index] = level;
2663 TRACE("OSS doesn't support setting volume\n");
2665 LeaveCriticalSection(&This->lock);
2667 return S_OK;
2670 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2671 IAudioStreamVolume *iface, UINT32 index, float *level)
2673 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2675 TRACE("(%p)->(%d, %p)\n", This, index, level);
2677 if(!level)
2678 return E_POINTER;
2680 if(index >= This->fmt->nChannels)
2681 return E_INVALIDARG;
2683 *level = This->vols[index];
2685 return S_OK;
2688 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2689 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2691 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2692 int i;
2694 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2696 if(!levels)
2697 return E_POINTER;
2699 if(count != This->fmt->nChannels)
2700 return E_INVALIDARG;
2702 EnterCriticalSection(&This->lock);
2704 for(i = 0; i < count; ++i)
2705 This->vols[i] = levels[i];
2707 TRACE("OSS doesn't support setting volume\n");
2709 LeaveCriticalSection(&This->lock);
2711 return S_OK;
2714 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2715 IAudioStreamVolume *iface, UINT32 count, float *levels)
2717 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2718 int i;
2720 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2722 if(!levels)
2723 return E_POINTER;
2725 if(count != This->fmt->nChannels)
2726 return E_INVALIDARG;
2728 EnterCriticalSection(&This->lock);
2730 for(i = 0; i < count; ++i)
2731 levels[i] = This->vols[i];
2733 LeaveCriticalSection(&This->lock);
2735 return S_OK;
2738 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2740 AudioStreamVolume_QueryInterface,
2741 AudioStreamVolume_AddRef,
2742 AudioStreamVolume_Release,
2743 AudioStreamVolume_GetChannelCount,
2744 AudioStreamVolume_SetChannelVolume,
2745 AudioStreamVolume_GetChannelVolume,
2746 AudioStreamVolume_SetAllVolumes,
2747 AudioStreamVolume_GetAllVolumes
2750 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2751 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2753 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2755 if(!ppv)
2756 return E_POINTER;
2757 *ppv = NULL;
2759 if(IsEqualIID(riid, &IID_IUnknown) ||
2760 IsEqualIID(riid, &IID_IChannelAudioVolume))
2761 *ppv = iface;
2762 if(*ppv){
2763 IUnknown_AddRef((IUnknown*)*ppv);
2764 return S_OK;
2767 WARN("Unknown interface %s\n", debugstr_guid(riid));
2768 return E_NOINTERFACE;
2771 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2773 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2774 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2777 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2779 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2780 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2783 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2784 IChannelAudioVolume *iface, UINT32 *out)
2786 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2787 AudioSession *session = This->session;
2789 TRACE("(%p)->(%p)\n", session, out);
2791 if(!out)
2792 return NULL_PTR_ERR;
2794 *out = session->channel_count;
2796 return S_OK;
2799 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2800 IChannelAudioVolume *iface, UINT32 index, float level,
2801 const GUID *context)
2803 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2804 AudioSession *session = This->session;
2806 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2807 wine_dbgstr_guid(context));
2809 if(level < 0.f || level > 1.f)
2810 return E_INVALIDARG;
2812 if(index >= session->channel_count)
2813 return E_INVALIDARG;
2815 if(context)
2816 FIXME("Notifications not supported yet\n");
2818 EnterCriticalSection(&session->lock);
2820 session->channel_vols[index] = level;
2822 TRACE("OSS doesn't support setting volume\n");
2824 LeaveCriticalSection(&session->lock);
2826 return S_OK;
2829 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2830 IChannelAudioVolume *iface, UINT32 index, float *level)
2832 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2833 AudioSession *session = This->session;
2835 TRACE("(%p)->(%d, %p)\n", session, index, level);
2837 if(!level)
2838 return NULL_PTR_ERR;
2840 if(index >= session->channel_count)
2841 return E_INVALIDARG;
2843 *level = session->channel_vols[index];
2845 return S_OK;
2848 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2849 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2850 const GUID *context)
2852 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2853 AudioSession *session = This->session;
2854 int i;
2856 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2857 wine_dbgstr_guid(context));
2859 if(!levels)
2860 return NULL_PTR_ERR;
2862 if(count != session->channel_count)
2863 return E_INVALIDARG;
2865 if(context)
2866 FIXME("Notifications not supported yet\n");
2868 EnterCriticalSection(&session->lock);
2870 for(i = 0; i < count; ++i)
2871 session->channel_vols[i] = levels[i];
2873 TRACE("OSS doesn't support setting volume\n");
2875 LeaveCriticalSection(&session->lock);
2877 return S_OK;
2880 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2881 IChannelAudioVolume *iface, UINT32 count, float *levels)
2883 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2884 AudioSession *session = This->session;
2885 int i;
2887 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2889 if(!levels)
2890 return NULL_PTR_ERR;
2892 if(count != session->channel_count)
2893 return E_INVALIDARG;
2895 for(i = 0; i < count; ++i)
2896 levels[i] = session->channel_vols[i];
2898 return S_OK;
2901 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2903 ChannelAudioVolume_QueryInterface,
2904 ChannelAudioVolume_AddRef,
2905 ChannelAudioVolume_Release,
2906 ChannelAudioVolume_GetChannelCount,
2907 ChannelAudioVolume_SetChannelVolume,
2908 ChannelAudioVolume_GetChannelVolume,
2909 ChannelAudioVolume_SetAllVolumes,
2910 ChannelAudioVolume_GetAllVolumes
2913 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2914 REFIID riid, void **ppv)
2916 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2918 if(!ppv)
2919 return E_POINTER;
2920 *ppv = NULL;
2922 if(IsEqualIID(riid, &IID_IUnknown) ||
2923 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2924 IsEqualIID(riid, &IID_IAudioSessionManager2))
2925 *ppv = iface;
2926 if(*ppv){
2927 IUnknown_AddRef((IUnknown*)*ppv);
2928 return S_OK;
2931 WARN("Unknown interface %s\n", debugstr_guid(riid));
2932 return E_NOINTERFACE;
2935 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2937 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2938 ULONG ref;
2939 ref = InterlockedIncrement(&This->ref);
2940 TRACE("(%p) Refcount now %u\n", This, ref);
2941 return ref;
2944 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2946 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2947 ULONG ref;
2948 ref = InterlockedDecrement(&This->ref);
2949 TRACE("(%p) Refcount now %u\n", This, ref);
2950 if(!ref)
2951 HeapFree(GetProcessHeap(), 0, This);
2952 return ref;
2955 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2956 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2957 IAudioSessionControl **out)
2959 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2960 AudioSession *session;
2961 AudioSessionWrapper *wrapper;
2962 HRESULT hr;
2964 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2965 flags, out);
2967 hr = get_audio_session(session_guid, This->device, 0, &session);
2968 if(FAILED(hr))
2969 return hr;
2971 wrapper = AudioSessionWrapper_Create(NULL);
2972 if(!wrapper)
2973 return E_OUTOFMEMORY;
2975 wrapper->session = session;
2977 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2979 return S_OK;
2982 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2983 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2984 ISimpleAudioVolume **out)
2986 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2987 AudioSession *session;
2988 AudioSessionWrapper *wrapper;
2989 HRESULT hr;
2991 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2992 flags, out);
2994 hr = get_audio_session(session_guid, This->device, 0, &session);
2995 if(FAILED(hr))
2996 return hr;
2998 wrapper = AudioSessionWrapper_Create(NULL);
2999 if(!wrapper)
3000 return E_OUTOFMEMORY;
3002 wrapper->session = session;
3004 *out = &wrapper->ISimpleAudioVolume_iface;
3006 return S_OK;
3009 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3010 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3012 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3013 FIXME("(%p)->(%p) - stub\n", This, out);
3014 return E_NOTIMPL;
3017 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3018 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3020 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3021 FIXME("(%p)->(%p) - stub\n", This, notification);
3022 return E_NOTIMPL;
3025 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3026 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3028 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3029 FIXME("(%p)->(%p) - stub\n", This, notification);
3030 return E_NOTIMPL;
3033 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3034 IAudioSessionManager2 *iface, const WCHAR *session_id,
3035 IAudioVolumeDuckNotification *notification)
3037 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3038 FIXME("(%p)->(%p) - stub\n", This, notification);
3039 return E_NOTIMPL;
3042 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3043 IAudioSessionManager2 *iface,
3044 IAudioVolumeDuckNotification *notification)
3046 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3047 FIXME("(%p)->(%p) - stub\n", This, notification);
3048 return E_NOTIMPL;
3051 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3053 AudioSessionManager_QueryInterface,
3054 AudioSessionManager_AddRef,
3055 AudioSessionManager_Release,
3056 AudioSessionManager_GetAudioSessionControl,
3057 AudioSessionManager_GetSimpleAudioVolume,
3058 AudioSessionManager_GetSessionEnumerator,
3059 AudioSessionManager_RegisterSessionNotification,
3060 AudioSessionManager_UnregisterSessionNotification,
3061 AudioSessionManager_RegisterDuckNotification,
3062 AudioSessionManager_UnregisterDuckNotification
3065 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3066 IAudioSessionManager2 **out)
3068 SessionMgr *This;
3070 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3071 if(!This)
3072 return E_OUTOFMEMORY;
3074 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3075 This->device = device;
3076 This->ref = 1;
3078 *out = &This->IAudioSessionManager2_iface;
3080 return S_OK;