oleaut32/tests: Test more return values.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blob1686c8c96c10965647a75a3286c929685b7ae98f
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 COBJMACROS
20 #include "config.h"
22 #include <stdarg.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioctl.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <math.h>
34 #include <sys/soundcard.h>
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winreg.h"
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42 #include "wine/list.h"
44 #include "ole2.h"
45 #include "mmdeviceapi.h"
46 #include "devpkey.h"
47 #include "dshow.h"
48 #include "dsound.h"
50 #include "initguid.h"
51 #include "endpointvolume.h"
52 #include "audiopolicy.h"
53 #include "audioclient.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(oss);
57 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
59 static const REFERENCE_TIME DefaultPeriod = 100000;
60 static const REFERENCE_TIME MinimumPeriod = 50000;
62 struct ACImpl;
63 typedef struct ACImpl ACImpl;
65 typedef struct _AudioSession {
66 GUID guid;
67 struct list clients;
69 IMMDevice *device;
71 float master_vol;
72 UINT32 channel_count;
73 float *channel_vols;
74 BOOL mute;
76 CRITICAL_SECTION lock;
78 struct list entry;
79 } AudioSession;
81 typedef struct _AudioSessionWrapper {
82 IAudioSessionControl2 IAudioSessionControl2_iface;
83 IChannelAudioVolume IChannelAudioVolume_iface;
84 ISimpleAudioVolume ISimpleAudioVolume_iface;
86 LONG ref;
88 ACImpl *client;
89 AudioSession *session;
90 } AudioSessionWrapper;
92 struct ACImpl {
93 IAudioClient IAudioClient_iface;
94 IAudioRenderClient IAudioRenderClient_iface;
95 IAudioCaptureClient IAudioCaptureClient_iface;
96 IAudioClock IAudioClock_iface;
97 IAudioClock2 IAudioClock2_iface;
98 IAudioStreamVolume IAudioStreamVolume_iface;
100 LONG ref;
102 IMMDevice *parent;
103 IUnknown *pUnkFTMarshal;
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, in_oss_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_key_devicesW[] = {'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','\\','d','e','v','i','c','e','s',0};
155 static const WCHAR guidW[] = {'g','u','i','d',0};
157 static HANDLE g_timer_q;
159 static CRITICAL_SECTION g_sessions_lock;
160 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
162 0, 0, &g_sessions_lock,
163 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
164 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
166 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
167 static struct list g_sessions = LIST_INIT(g_sessions);
169 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
171 static const IAudioClientVtbl AudioClient_Vtbl;
172 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
173 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
174 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
175 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
176 static const IAudioClockVtbl AudioClock_Vtbl;
177 static const IAudioClock2Vtbl AudioClock2_Vtbl;
178 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
179 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
180 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
182 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
184 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
187 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
189 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
192 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
194 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
197 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
199 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
202 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
204 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
207 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
209 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
212 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
214 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
217 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
219 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
222 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
224 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
227 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
229 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
232 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
234 switch (reason)
236 case DLL_PROCESS_ATTACH:
237 g_timer_q = CreateTimerQueue();
238 if(!g_timer_q)
239 return FALSE;
240 break;
242 case DLL_PROCESS_DETACH:
243 if (!reserved)
245 OSSDevice *iter, *iter2;
247 DeleteCriticalSection(&g_sessions_lock);
249 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
250 HeapFree(GetProcessHeap(), 0, iter);
253 break;
255 return TRUE;
258 /* From <dlls/mmdevapi/mmdevapi.h> */
259 enum DriverPriority {
260 Priority_Unavailable = 0,
261 Priority_Low,
262 Priority_Neutral,
263 Priority_Preferred
266 int WINAPI AUDDRV_GetPriority(void)
268 int mixer_fd;
269 oss_sysinfo sysinfo;
271 /* Attempt to determine if we are running on OSS or ALSA's OSS
272 * compatibility layer. There is no official way to do that, so just check
273 * for validity as best as possible, without rejecting valid OSS
274 * implementations. */
276 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
277 if(mixer_fd < 0){
278 TRACE("Priority_Unavailable: open failed\n");
279 return Priority_Unavailable;
282 sysinfo.version[0] = 0xFF;
283 sysinfo.versionnum = ~0;
284 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
285 TRACE("Priority_Unavailable: ioctl failed\n");
286 close(mixer_fd);
287 return Priority_Unavailable;
290 close(mixer_fd);
292 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
293 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
294 return Priority_Low;
296 if(sysinfo.versionnum & 0x80000000){
297 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
298 return Priority_Low;
301 TRACE("Priority_Preferred: Seems like valid OSS!\n");
303 return Priority_Preferred;
306 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
307 GUID *guid)
309 HKEY key;
310 BOOL opened = FALSE;
311 LONG lr;
313 if(!drv_key){
314 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
315 NULL, &drv_key, NULL);
316 if(lr != ERROR_SUCCESS){
317 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
318 return;
320 opened = TRUE;
323 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
324 NULL, &key, NULL);
325 if(lr != ERROR_SUCCESS){
326 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
327 goto exit;
330 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
331 sizeof(GUID));
332 if(lr != ERROR_SUCCESS)
333 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
335 RegCloseKey(key);
336 exit:
337 if(opened)
338 RegCloseKey(drv_key);
341 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
343 HKEY key = NULL, dev_key;
344 DWORD type, size = sizeof(*guid);
345 WCHAR key_name[256];
347 if(flow == eCapture)
348 key_name[0] = '1';
349 else
350 key_name[0] = '0';
351 key_name[1] = ',';
352 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
353 (sizeof(key_name) / sizeof(*key_name)) - 2);
355 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
356 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
357 if(RegQueryValueExW(dev_key, guidW, 0, &type,
358 (BYTE*)guid, &size) == ERROR_SUCCESS){
359 if(type == REG_BINARY){
360 RegCloseKey(dev_key);
361 RegCloseKey(key);
362 return;
364 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
365 wine_dbgstr_w(key_name), type);
367 RegCloseKey(dev_key);
371 CoCreateGuid(guid);
373 set_device_guid(flow, key, key_name, guid);
375 if(key)
376 RegCloseKey(key);
379 static const char *oss_clean_devnode(const char *devnode)
381 static char ret[OSS_DEVNODE_SIZE];
383 const char *dot, *slash;
384 size_t len;
386 dot = strrchr(devnode, '.');
387 if(!dot)
388 return devnode;
390 slash = strrchr(devnode, '/');
391 if(slash && dot < slash)
392 return devnode;
394 len = dot - devnode;
396 memcpy(ret, devnode, len);
397 ret[len] = '\0';
399 return ret;
402 static UINT get_default_index(EDataFlow flow)
404 int fd = -1, err;
405 UINT i;
406 oss_audioinfo ai;
407 const char *devnode;
408 OSSDevice *dev_item;
410 if(flow == eRender)
411 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
412 else
413 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
415 if(fd < 0){
416 WARN("Couldn't open default device!\n");
417 return 0;
420 ai.dev = -1;
421 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
422 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
423 close(fd);
424 return 0;
427 close(fd);
429 TRACE("Default devnode: %s\n", ai.devnode);
430 devnode = oss_clean_devnode(ai.devnode);
431 i = 0;
432 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
433 if(dev_item->flow == flow){
434 if(!strcmp(devnode, dev_item->devnode))
435 return i;
436 ++i;
440 WARN("Couldn't find default device! Choosing first.\n");
441 return 0;
444 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
445 UINT *num, UINT *def_index)
447 int i, mixer_fd;
448 oss_sysinfo sysinfo;
449 static int print_once = 0;
451 static const WCHAR outW[] = {'O','u','t',':',' ',0};
452 static const WCHAR inW[] = {'I','n',':',' ',0};
454 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
456 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
457 if(mixer_fd < 0){
458 ERR("OSS /dev/mixer doesn't seem to exist\n");
459 return AUDCLNT_E_SERVICE_NOT_RUNNING;
462 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
463 close(mixer_fd);
465 if(errno == EINVAL){
466 ERR("OSS version too old, need at least OSSv4\n");
467 return AUDCLNT_E_SERVICE_NOT_RUNNING;
470 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
471 return E_FAIL;
474 if(!print_once){
475 TRACE("OSS sysinfo:\n");
476 TRACE("product: %s\n", sysinfo.product);
477 TRACE("version: %s\n", sysinfo.version);
478 TRACE("versionnum: %x\n", sysinfo.versionnum);
479 TRACE("numaudios: %d\n", sysinfo.numaudios);
480 TRACE("nummixers: %d\n", sysinfo.nummixers);
481 TRACE("numcards: %d\n", sysinfo.numcards);
482 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
483 print_once = 1;
486 if(sysinfo.numaudios <= 0){
487 WARN("No audio devices!\n");
488 close(mixer_fd);
489 return AUDCLNT_E_SERVICE_NOT_RUNNING;
492 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
493 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
495 *num = 0;
496 for(i = 0; i < sysinfo.numaudios; ++i){
497 oss_audioinfo ai = {0};
498 const char *devnode;
499 OSSDevice *dev_item;
500 int fd;
502 ai.dev = i;
503 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
504 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
505 strerror(errno));
506 continue;
509 devnode = oss_clean_devnode(ai.devnode);
511 /* check for duplicates */
512 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
513 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
514 break;
516 if(&dev_item->entry != &g_devices)
517 continue;
519 if(flow == eRender)
520 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
521 else
522 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
523 if(fd < 0){
524 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
525 devnode, errno, strerror(errno));
526 continue;
528 close(fd);
530 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
531 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
532 size_t len, prefix_len;
533 const WCHAR *prefix;
535 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
537 dev_item->flow = flow;
538 get_device_guid(flow, devnode, &dev_item->guid);
539 strcpy(dev_item->devnode, devnode);
541 (*guids)[*num] = dev_item->guid;
543 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
544 if(flow == eRender){
545 prefix = outW;
546 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
547 len += prefix_len;
548 }else{
549 prefix = inW;
550 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
551 len += prefix_len;
553 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
554 len * sizeof(WCHAR));
555 if(!(*ids)[*num]){
556 for(i = 0; i < *num; ++i)
557 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
558 HeapFree(GetProcessHeap(), 0, *ids);
559 HeapFree(GetProcessHeap(), 0, *guids);
560 HeapFree(GetProcessHeap(), 0, dev_item);
561 close(mixer_fd);
562 return E_OUTOFMEMORY;
564 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
565 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
566 (*ids)[*num] + prefix_len, len - prefix_len);
568 list_add_tail(&g_devices, &dev_item->entry);
570 (*num)++;
574 close(mixer_fd);
576 *def_index = get_default_index(flow);
578 return S_OK;
581 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
583 OSSDevice *dev_item;
584 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
585 if(IsEqualGUID(guid, &dev_item->guid))
586 return dev_item;
587 return NULL;
590 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
591 IAudioClient **out)
593 ACImpl *This;
594 const OSSDevice *oss_dev;
595 HRESULT hr;
597 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
599 oss_dev = get_ossdevice_from_guid(guid);
600 if(!oss_dev){
601 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
602 return AUDCLNT_E_DEVICE_INVALIDATED;
605 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
606 if(!This)
607 return E_OUTOFMEMORY;
609 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
610 (IUnknown **)&This->pUnkFTMarshal);
611 if (FAILED(hr)) {
612 HeapFree(GetProcessHeap(), 0, This);
613 return hr;
616 if(oss_dev->flow == eRender)
617 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
618 else if(oss_dev->flow == eCapture)
619 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
620 else{
621 HeapFree(GetProcessHeap(), 0, This);
622 return E_INVALIDARG;
624 if(This->fd < 0){
625 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
626 strerror(errno));
627 HeapFree(GetProcessHeap(), 0, This);
628 return AUDCLNT_E_DEVICE_INVALIDATED;
631 This->dataflow = oss_dev->flow;
633 This->ai.dev = -1;
634 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
635 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
636 errno, strerror(errno));
637 close(This->fd);
638 HeapFree(GetProcessHeap(), 0, This);
639 return E_FAIL;
642 strcpy(This->devnode, oss_dev->devnode);
644 TRACE("OSS audioinfo:\n");
645 TRACE("devnode: %s\n", This->ai.devnode);
646 TRACE("name: %s\n", This->ai.name);
647 TRACE("busy: %x\n", This->ai.busy);
648 TRACE("caps: %x\n", This->ai.caps);
649 TRACE("iformats: %x\n", This->ai.iformats);
650 TRACE("oformats: %x\n", This->ai.oformats);
651 TRACE("enabled: %d\n", This->ai.enabled);
652 TRACE("min_rate: %d\n", This->ai.min_rate);
653 TRACE("max_rate: %d\n", This->ai.max_rate);
654 TRACE("min_channels: %d\n", This->ai.min_channels);
655 TRACE("max_channels: %d\n", This->ai.max_channels);
657 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
658 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
659 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
660 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
661 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
662 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
664 InitializeCriticalSection(&This->lock);
665 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
667 This->parent = dev;
668 IMMDevice_AddRef(This->parent);
670 IAudioClient_AddRef(&This->IAudioClient_iface);
672 *out = &This->IAudioClient_iface;
674 return S_OK;
677 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
678 REFIID riid, void **ppv)
680 ACImpl *This = impl_from_IAudioClient(iface);
681 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
683 if(!ppv)
684 return E_POINTER;
685 *ppv = NULL;
686 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
687 *ppv = iface;
688 else if(IsEqualIID(riid, &IID_IMarshal))
689 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
690 if(*ppv){
691 IUnknown_AddRef((IUnknown*)*ppv);
692 return S_OK;
694 WARN("Unknown interface %s\n", debugstr_guid(riid));
695 return E_NOINTERFACE;
698 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
700 ACImpl *This = impl_from_IAudioClient(iface);
701 ULONG ref;
702 ref = InterlockedIncrement(&This->ref);
703 TRACE("(%p) Refcount now %u\n", This, ref);
704 return ref;
707 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
709 ACImpl *This = impl_from_IAudioClient(iface);
710 ULONG ref;
712 ref = InterlockedDecrement(&This->ref);
713 TRACE("(%p) Refcount now %u\n", This, ref);
714 if(!ref){
715 if(This->timer){
716 HANDLE event;
717 DWORD wait;
718 event = CreateEventW(NULL, TRUE, FALSE, NULL);
719 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
720 wait = wait && GetLastError() == ERROR_IO_PENDING;
721 if(event && wait)
722 WaitForSingleObject(event, INFINITE);
723 CloseHandle(event);
726 IAudioClient_Stop(iface);
727 IMMDevice_Release(This->parent);
728 IUnknown_Release(This->pUnkFTMarshal);
729 This->lock.DebugInfo->Spare[0] = 0;
730 DeleteCriticalSection(&This->lock);
731 close(This->fd);
732 if(This->initted){
733 EnterCriticalSection(&g_sessions_lock);
734 list_remove(&This->entry);
735 LeaveCriticalSection(&g_sessions_lock);
737 HeapFree(GetProcessHeap(), 0, This->vols);
738 HeapFree(GetProcessHeap(), 0, This->local_buffer);
739 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
740 CoTaskMemFree(This->fmt);
741 HeapFree(GetProcessHeap(), 0, This);
743 return ref;
746 static void dump_fmt(const WAVEFORMATEX *fmt)
748 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
749 switch(fmt->wFormatTag){
750 case WAVE_FORMAT_PCM:
751 TRACE("WAVE_FORMAT_PCM");
752 break;
753 case WAVE_FORMAT_IEEE_FLOAT:
754 TRACE("WAVE_FORMAT_IEEE_FLOAT");
755 break;
756 case WAVE_FORMAT_EXTENSIBLE:
757 TRACE("WAVE_FORMAT_EXTENSIBLE");
758 break;
759 default:
760 TRACE("Unknown");
761 break;
763 TRACE(")\n");
765 TRACE("nChannels: %u\n", fmt->nChannels);
766 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
767 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
768 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
769 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
770 TRACE("cbSize: %u\n", fmt->cbSize);
772 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
773 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
774 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
775 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
776 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
780 static DWORD get_channel_mask(unsigned int channels)
782 switch(channels){
783 case 0:
784 return 0;
785 case 1:
786 return KSAUDIO_SPEAKER_MONO;
787 case 2:
788 return KSAUDIO_SPEAKER_STEREO;
789 case 3:
790 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
791 case 4:
792 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
793 case 5:
794 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
795 case 6:
796 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
797 case 7:
798 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
799 case 8:
800 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
802 FIXME("Unknown speaker configuration: %u\n", channels);
803 return 0;
806 static int get_oss_format(const WAVEFORMATEX *fmt)
808 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
810 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
811 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
812 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
813 switch(fmt->wBitsPerSample){
814 case 8:
815 return AFMT_U8;
816 case 16:
817 return AFMT_S16_LE;
818 case 24:
819 return AFMT_S24_LE;
820 case 32:
821 return AFMT_S32_LE;
823 return -1;
826 #ifdef AFMT_FLOAT
827 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
828 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
829 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
830 if(fmt->wBitsPerSample != 32)
831 return -1;
833 return AFMT_FLOAT;
835 #endif
837 return -1;
840 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
842 WAVEFORMATEX *ret;
843 size_t size;
845 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
846 size = sizeof(WAVEFORMATEXTENSIBLE);
847 else
848 size = sizeof(WAVEFORMATEX);
850 ret = CoTaskMemAlloc(size);
851 if(!ret)
852 return NULL;
854 memcpy(ret, fmt, size);
856 ret->cbSize = size - sizeof(WAVEFORMATEX);
858 return ret;
861 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
862 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
864 int tmp, oss_format;
865 double tenth;
866 HRESULT ret = S_OK;
867 WAVEFORMATEX *closest = NULL;
869 tmp = oss_format = get_oss_format(fmt);
870 if(oss_format < 0)
871 return AUDCLNT_E_UNSUPPORTED_FORMAT;
872 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
873 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
874 return E_FAIL;
876 if(tmp != oss_format){
877 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
878 return AUDCLNT_E_UNSUPPORTED_FORMAT;
881 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
882 (fmt->nAvgBytesPerSec == 0 ||
883 fmt->nBlockAlign == 0 ||
884 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
885 return E_INVALIDARG;
887 if(fmt->nChannels == 0)
888 return AUDCLNT_E_UNSUPPORTED_FORMAT;
890 closest = clone_format(fmt);
891 if(!closest)
892 return E_OUTOFMEMORY;
894 tmp = fmt->nSamplesPerSec;
895 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
896 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
897 CoTaskMemFree(closest);
898 return E_FAIL;
900 tenth = fmt->nSamplesPerSec * 0.1;
901 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
902 ret = S_FALSE;
903 closest->nSamplesPerSec = tmp;
906 tmp = fmt->nChannels;
907 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
908 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
909 CoTaskMemFree(closest);
910 return E_FAIL;
912 if(tmp != fmt->nChannels){
913 ret = S_FALSE;
914 closest->nChannels = tmp;
917 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
918 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
920 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
921 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
922 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
923 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
924 ret = S_FALSE;
926 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
927 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
928 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
929 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
930 ret = S_FALSE;
933 if(ret == S_FALSE && !out)
934 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
936 if(ret == S_FALSE && out){
937 closest->nBlockAlign =
938 closest->nChannels * closest->wBitsPerSample / 8;
939 closest->nAvgBytesPerSec =
940 closest->nBlockAlign * closest->nSamplesPerSec;
941 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
942 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
943 *out = closest;
944 } else
945 CoTaskMemFree(closest);
947 TRACE("returning: %08x\n", ret);
948 return ret;
951 static void session_init_vols(AudioSession *session, UINT channels)
953 if(session->channel_count < channels){
954 UINT i;
956 if(session->channel_vols)
957 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
958 session->channel_vols, sizeof(float) * channels);
959 else
960 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
961 sizeof(float) * channels);
962 if(!session->channel_vols)
963 return;
965 for(i = session->channel_count; i < channels; ++i)
966 session->channel_vols[i] = 1.f;
968 session->channel_count = channels;
972 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
973 UINT num_channels)
975 AudioSession *ret;
977 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
978 if(!ret)
979 return NULL;
981 memcpy(&ret->guid, guid, sizeof(GUID));
983 ret->device = device;
985 list_init(&ret->clients);
987 list_add_head(&g_sessions, &ret->entry);
989 InitializeCriticalSection(&ret->lock);
990 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
992 session_init_vols(ret, num_channels);
994 ret->master_vol = 1.f;
996 return ret;
999 /* if channels == 0, then this will return or create a session with
1000 * matching dataflow and GUID. otherwise, channels must also match */
1001 static HRESULT get_audio_session(const GUID *sessionguid,
1002 IMMDevice *device, UINT channels, AudioSession **out)
1004 AudioSession *session;
1006 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1007 *out = create_session(&GUID_NULL, device, channels);
1008 if(!*out)
1009 return E_OUTOFMEMORY;
1011 return S_OK;
1014 *out = NULL;
1015 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1016 if(session->device == device &&
1017 IsEqualGUID(sessionguid, &session->guid)){
1018 session_init_vols(session, channels);
1019 *out = session;
1020 break;
1024 if(!*out){
1025 *out = create_session(sessionguid, device, channels);
1026 if(!*out)
1027 return E_OUTOFMEMORY;
1030 return S_OK;
1033 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1034 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1035 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1036 const GUID *sessionguid)
1038 ACImpl *This = impl_from_IAudioClient(iface);
1039 int i;
1040 HRESULT hr;
1042 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1043 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1045 if(!fmt)
1046 return E_POINTER;
1048 dump_fmt(fmt);
1050 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1051 return AUDCLNT_E_NOT_INITIALIZED;
1053 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1054 AUDCLNT_STREAMFLAGS_LOOPBACK |
1055 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1056 AUDCLNT_STREAMFLAGS_NOPERSIST |
1057 AUDCLNT_STREAMFLAGS_RATEADJUST |
1058 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1059 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1060 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1061 TRACE("Unknown flags: %08x\n", flags);
1062 return E_INVALIDARG;
1065 if(mode == AUDCLNT_SHAREMODE_SHARED){
1066 period = DefaultPeriod;
1067 if( duration < 3 * period)
1068 duration = 3 * period;
1069 }else{
1070 if(!period)
1071 period = DefaultPeriod; /* not minimum */
1072 if(period < MinimumPeriod || period > 5000000)
1073 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1074 if(duration > 20000000) /* the smaller the period, the lower this limit */
1075 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1076 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1077 if(duration != period)
1078 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1079 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1080 return AUDCLNT_E_DEVICE_IN_USE;
1081 }else{
1082 if( duration < 8 * period)
1083 duration = 8 * period; /* may grow above 2s */
1087 EnterCriticalSection(&This->lock);
1089 if(This->initted){
1090 LeaveCriticalSection(&This->lock);
1091 return AUDCLNT_E_ALREADY_INITIALIZED;
1094 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1095 if(FAILED(hr)){
1096 LeaveCriticalSection(&This->lock);
1097 return hr;
1100 This->fmt = clone_format(fmt);
1101 if(!This->fmt){
1102 LeaveCriticalSection(&This->lock);
1103 return E_OUTOFMEMORY;
1106 This->period_us = period / 10;
1107 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1109 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1110 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1111 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1112 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1113 This->bufsize_frames * fmt->nBlockAlign);
1114 if(!This->local_buffer){
1115 CoTaskMemFree(This->fmt);
1116 This->fmt = NULL;
1117 LeaveCriticalSection(&This->lock);
1118 return E_OUTOFMEMORY;
1121 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1122 if(!This->vols){
1123 CoTaskMemFree(This->fmt);
1124 This->fmt = NULL;
1125 LeaveCriticalSection(&This->lock);
1126 return E_OUTOFMEMORY;
1129 for(i = 0; i < fmt->nChannels; ++i)
1130 This->vols[i] = 1.f;
1132 This->share = mode;
1133 This->flags = flags;
1134 This->oss_bufsize_bytes = 0;
1136 EnterCriticalSection(&g_sessions_lock);
1138 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1139 &This->session);
1140 if(FAILED(hr)){
1141 LeaveCriticalSection(&g_sessions_lock);
1142 HeapFree(GetProcessHeap(), 0, This->vols);
1143 This->vols = NULL;
1144 CoTaskMemFree(This->fmt);
1145 This->fmt = NULL;
1146 LeaveCriticalSection(&This->lock);
1147 return hr;
1150 list_add_tail(&This->session->clients, &This->entry);
1152 LeaveCriticalSection(&g_sessions_lock);
1154 This->initted = TRUE;
1156 LeaveCriticalSection(&This->lock);
1158 return S_OK;
1161 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1162 UINT32 *frames)
1164 ACImpl *This = impl_from_IAudioClient(iface);
1166 TRACE("(%p)->(%p)\n", This, frames);
1168 if(!frames)
1169 return E_POINTER;
1171 EnterCriticalSection(&This->lock);
1173 if(!This->initted){
1174 LeaveCriticalSection(&This->lock);
1175 return AUDCLNT_E_NOT_INITIALIZED;
1178 *frames = This->bufsize_frames;
1180 TRACE("buffer size: %u\n", *frames);
1182 LeaveCriticalSection(&This->lock);
1184 return S_OK;
1187 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1188 REFERENCE_TIME *latency)
1190 ACImpl *This = impl_from_IAudioClient(iface);
1192 TRACE("(%p)->(%p)\n", This, latency);
1194 if(!latency)
1195 return E_POINTER;
1197 EnterCriticalSection(&This->lock);
1199 if(!This->initted){
1200 LeaveCriticalSection(&This->lock);
1201 return AUDCLNT_E_NOT_INITIALIZED;
1204 /* pretend we process audio in Period chunks, so max latency includes
1205 * the period time. Some native machines add .6666ms in shared mode. */
1206 *latency = (REFERENCE_TIME)This->period_us * 10 + 6666;
1208 LeaveCriticalSection(&This->lock);
1210 return S_OK;
1213 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1214 UINT32 *numpad)
1216 ACImpl *This = impl_from_IAudioClient(iface);
1218 TRACE("(%p)->(%p)\n", This, numpad);
1220 if(!numpad)
1221 return E_POINTER;
1223 EnterCriticalSection(&This->lock);
1225 if(!This->initted){
1226 LeaveCriticalSection(&This->lock);
1227 return AUDCLNT_E_NOT_INITIALIZED;
1230 *numpad = This->held_frames;
1232 TRACE("padding: %u\n", *numpad);
1234 LeaveCriticalSection(&This->lock);
1236 return S_OK;
1239 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1240 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1241 WAVEFORMATEX **outpwfx)
1243 ACImpl *This = impl_from_IAudioClient(iface);
1244 int fd = -1;
1245 HRESULT ret;
1247 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1249 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1250 return E_POINTER;
1252 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1253 return E_INVALIDARG;
1255 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1256 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1257 return E_INVALIDARG;
1259 dump_fmt(pwfx);
1261 if(outpwfx){
1262 *outpwfx = NULL;
1263 if(mode != AUDCLNT_SHAREMODE_SHARED)
1264 outpwfx = NULL;
1267 if(This->dataflow == eRender)
1268 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1269 else if(This->dataflow == eCapture)
1270 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1272 if(fd < 0){
1273 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1274 strerror(errno));
1275 return AUDCLNT_E_DEVICE_INVALIDATED;
1278 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1280 close(fd);
1282 return ret;
1285 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1286 WAVEFORMATEX **pwfx)
1288 ACImpl *This = impl_from_IAudioClient(iface);
1289 WAVEFORMATEXTENSIBLE *fmt;
1290 int formats;
1292 TRACE("(%p)->(%p)\n", This, pwfx);
1294 if(!pwfx)
1295 return E_POINTER;
1296 *pwfx = NULL;
1298 if(This->dataflow == eRender)
1299 formats = This->ai.oformats;
1300 else if(This->dataflow == eCapture)
1301 formats = This->ai.iformats;
1302 else
1303 return E_UNEXPECTED;
1305 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1306 if(!fmt)
1307 return E_OUTOFMEMORY;
1309 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1310 if(formats & AFMT_S16_LE){
1311 fmt->Format.wBitsPerSample = 16;
1312 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1313 #ifdef AFMT_FLOAT
1314 }else if(formats & AFMT_FLOAT){
1315 fmt->Format.wBitsPerSample = 32;
1316 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1317 #endif
1318 }else if(formats & AFMT_U8){
1319 fmt->Format.wBitsPerSample = 8;
1320 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1321 }else if(formats & AFMT_S32_LE){
1322 fmt->Format.wBitsPerSample = 32;
1323 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1324 }else if(formats & AFMT_S24_LE){
1325 fmt->Format.wBitsPerSample = 24;
1326 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1327 }else{
1328 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1329 CoTaskMemFree(fmt);
1330 return E_FAIL;
1333 /* some OSS drivers are buggy, so set reasonable defaults if
1334 * the reported values seem wacky */
1335 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1336 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1337 fmt->Format.nChannels = 2;
1339 if(This->ai.max_rate == 0)
1340 fmt->Format.nSamplesPerSec = 44100;
1341 else
1342 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1343 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1344 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1346 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1348 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1349 fmt->Format.nChannels) / 8;
1350 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1351 fmt->Format.nBlockAlign;
1353 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1354 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1356 *pwfx = (WAVEFORMATEX*)fmt;
1357 dump_fmt(*pwfx);
1359 return S_OK;
1362 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1363 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1365 ACImpl *This = impl_from_IAudioClient(iface);
1367 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1369 if(!defperiod && !minperiod)
1370 return E_POINTER;
1372 if(defperiod)
1373 *defperiod = DefaultPeriod;
1374 if(minperiod)
1375 *minperiod = MinimumPeriod;
1377 return S_OK;
1380 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1382 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1383 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1384 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1385 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1386 This->fmt->wBitsPerSample == 8)
1387 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1388 else
1389 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1392 static void oss_write_data(ACImpl *This)
1394 ssize_t written_bytes;
1395 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1396 SIZE_T to_write_frames, to_write_bytes, advanced;
1397 audio_buf_info bi;
1398 BYTE *buf;
1400 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1401 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1402 return;
1405 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1407 if(bi.bytes > This->oss_bufsize_bytes){
1408 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1409 bi.bytes, This->oss_bufsize_bytes);
1410 This->oss_bufsize_bytes = bi.bytes;
1411 in_oss_frames = 0;
1412 }else
1413 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1415 if(in_oss_frames > This->in_oss_frames){
1416 TRACE("Capping reported frames from %u to %u\n",
1417 in_oss_frames, This->in_oss_frames);
1418 in_oss_frames = This->in_oss_frames;
1421 write_limit = 0;
1422 while(write_limit + in_oss_frames < max_period * 3)
1423 write_limit += max_period;
1424 if(write_limit == 0)
1425 return;
1427 /* vvvvvv - in_oss_frames
1428 * [--xxxxxxxxxx]
1429 * [xxxxxxxxxx--]
1430 * ^^^^^^^^^^ - held_frames
1431 * ^ - lcl_offs_frames
1433 advanced = This->in_oss_frames - in_oss_frames;
1434 if(advanced > This->held_frames)
1435 advanced = This->held_frames;
1436 This->lcl_offs_frames += advanced;
1437 This->lcl_offs_frames %= This->bufsize_frames;
1438 This->held_frames -= advanced;
1439 This->in_oss_frames = in_oss_frames;
1440 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
1441 advanced, This->lcl_offs_frames, This->held_frames, This->in_oss_frames);
1444 if(This->held_frames == This->in_oss_frames)
1445 return;
1447 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1448 new_frames = This->held_frames - This->in_oss_frames;
1450 if(write_offs_frames + new_frames > This->bufsize_frames)
1451 to_write_frames = This->bufsize_frames - write_offs_frames;
1452 else
1453 to_write_frames = new_frames;
1455 to_write_frames = min(to_write_frames, write_limit);
1456 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1457 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
1458 write_offs_frames, to_write_frames + write_offs_frames,
1459 This->bufsize_frames);
1461 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1463 if(This->session->mute)
1464 silence_buffer(This, buf, to_write_frames);
1466 written_bytes = write(This->fd, buf, to_write_bytes);
1467 if(written_bytes < 0){
1468 /* EAGAIN is OSS buffer full, log that too */
1469 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1470 return;
1472 written_frames = written_bytes / This->fmt->nBlockAlign;
1474 This->in_oss_frames += written_frames;
1476 if(written_frames < to_write_frames){
1477 /* OSS buffer probably full */
1478 return;
1481 if(new_frames > written_frames && written_frames < write_limit){
1482 /* wrapped and have some data back at the start to write */
1484 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1485 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1487 if(This->session->mute)
1488 silence_buffer(This, This->local_buffer, to_write_frames);
1490 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
1492 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1493 if(written_bytes < 0){
1494 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1495 return;
1497 written_frames = written_bytes / This->fmt->nBlockAlign;
1498 This->in_oss_frames += written_frames;
1502 static void oss_read_data(ACImpl *This)
1504 UINT64 pos, readable;
1505 ssize_t nread;
1507 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1508 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1510 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1511 readable);
1512 if(nread < 0){
1513 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1514 return;
1517 This->held_frames += nread / This->fmt->nBlockAlign;
1519 if(This->held_frames > This->bufsize_frames){
1520 WARN("Overflow of unread data\n");
1521 This->lcl_offs_frames += This->held_frames;
1522 This->lcl_offs_frames %= This->bufsize_frames;
1523 This->held_frames = This->bufsize_frames;
1527 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1529 ACImpl *This = user;
1531 EnterCriticalSection(&This->lock);
1533 if(This->playing){
1534 if(This->dataflow == eRender && This->held_frames)
1535 oss_write_data(This);
1536 else if(This->dataflow == eCapture)
1537 oss_read_data(This);
1540 LeaveCriticalSection(&This->lock);
1542 if(This->event)
1543 SetEvent(This->event);
1546 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1548 ACImpl *This = impl_from_IAudioClient(iface);
1550 TRACE("(%p)\n", This);
1552 EnterCriticalSection(&This->lock);
1554 if(!This->initted){
1555 LeaveCriticalSection(&This->lock);
1556 return AUDCLNT_E_NOT_INITIALIZED;
1559 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1560 LeaveCriticalSection(&This->lock);
1561 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1564 if(This->playing){
1565 LeaveCriticalSection(&This->lock);
1566 return AUDCLNT_E_NOT_STOPPED;
1569 if(!This->timer){
1570 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1571 oss_period_callback, This, 0, This->period_us / 1000,
1572 WT_EXECUTEINTIMERTHREAD))
1573 ERR("Unable to create period timer: %u\n", GetLastError());
1576 This->playing = TRUE;
1578 LeaveCriticalSection(&This->lock);
1580 return S_OK;
1583 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1585 ACImpl *This = impl_from_IAudioClient(iface);
1587 TRACE("(%p)\n", This);
1589 EnterCriticalSection(&This->lock);
1591 if(!This->initted){
1592 LeaveCriticalSection(&This->lock);
1593 return AUDCLNT_E_NOT_INITIALIZED;
1596 if(!This->playing){
1597 LeaveCriticalSection(&This->lock);
1598 return S_FALSE;
1601 This->playing = FALSE;
1602 This->in_oss_frames = 0;
1604 LeaveCriticalSection(&This->lock);
1606 return S_OK;
1609 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1611 ACImpl *This = impl_from_IAudioClient(iface);
1613 TRACE("(%p)\n", This);
1615 EnterCriticalSection(&This->lock);
1617 if(!This->initted){
1618 LeaveCriticalSection(&This->lock);
1619 return AUDCLNT_E_NOT_INITIALIZED;
1622 if(This->playing){
1623 LeaveCriticalSection(&This->lock);
1624 return AUDCLNT_E_NOT_STOPPED;
1627 if(This->getbuf_last){
1628 LeaveCriticalSection(&This->lock);
1629 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1632 if(This->dataflow == eRender){
1633 This->written_frames = 0;
1634 This->last_pos_frames = 0;
1635 }else{
1636 This->written_frames += This->held_frames;
1638 This->held_frames = 0;
1639 This->lcl_offs_frames = 0;
1640 This->in_oss_frames = 0;
1642 LeaveCriticalSection(&This->lock);
1644 return S_OK;
1647 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1648 HANDLE event)
1650 ACImpl *This = impl_from_IAudioClient(iface);
1652 TRACE("(%p)->(%p)\n", This, event);
1654 if(!event)
1655 return E_INVALIDARG;
1657 EnterCriticalSection(&This->lock);
1659 if(!This->initted){
1660 LeaveCriticalSection(&This->lock);
1661 return AUDCLNT_E_NOT_INITIALIZED;
1664 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1665 LeaveCriticalSection(&This->lock);
1666 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1669 if (This->event){
1670 LeaveCriticalSection(&This->lock);
1671 FIXME("called twice\n");
1672 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1675 This->event = event;
1677 LeaveCriticalSection(&This->lock);
1679 return S_OK;
1682 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1683 void **ppv)
1685 ACImpl *This = impl_from_IAudioClient(iface);
1687 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1689 if(!ppv)
1690 return E_POINTER;
1691 *ppv = NULL;
1693 EnterCriticalSection(&This->lock);
1695 if(!This->initted){
1696 LeaveCriticalSection(&This->lock);
1697 return AUDCLNT_E_NOT_INITIALIZED;
1700 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1701 if(This->dataflow != eRender){
1702 LeaveCriticalSection(&This->lock);
1703 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1705 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1706 *ppv = &This->IAudioRenderClient_iface;
1707 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1708 if(This->dataflow != eCapture){
1709 LeaveCriticalSection(&This->lock);
1710 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1712 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1713 *ppv = &This->IAudioCaptureClient_iface;
1714 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1715 IAudioClock_AddRef(&This->IAudioClock_iface);
1716 *ppv = &This->IAudioClock_iface;
1717 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1718 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1719 *ppv = &This->IAudioStreamVolume_iface;
1720 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1721 if(!This->session_wrapper){
1722 This->session_wrapper = AudioSessionWrapper_Create(This);
1723 if(!This->session_wrapper){
1724 LeaveCriticalSection(&This->lock);
1725 return E_OUTOFMEMORY;
1727 }else
1728 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1730 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1731 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1732 if(!This->session_wrapper){
1733 This->session_wrapper = AudioSessionWrapper_Create(This);
1734 if(!This->session_wrapper){
1735 LeaveCriticalSection(&This->lock);
1736 return E_OUTOFMEMORY;
1738 }else
1739 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1741 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1742 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1743 if(!This->session_wrapper){
1744 This->session_wrapper = AudioSessionWrapper_Create(This);
1745 if(!This->session_wrapper){
1746 LeaveCriticalSection(&This->lock);
1747 return E_OUTOFMEMORY;
1749 }else
1750 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1752 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1755 if(*ppv){
1756 LeaveCriticalSection(&This->lock);
1757 return S_OK;
1760 LeaveCriticalSection(&This->lock);
1762 FIXME("stub %s\n", debugstr_guid(riid));
1763 return E_NOINTERFACE;
1766 static const IAudioClientVtbl AudioClient_Vtbl =
1768 AudioClient_QueryInterface,
1769 AudioClient_AddRef,
1770 AudioClient_Release,
1771 AudioClient_Initialize,
1772 AudioClient_GetBufferSize,
1773 AudioClient_GetStreamLatency,
1774 AudioClient_GetCurrentPadding,
1775 AudioClient_IsFormatSupported,
1776 AudioClient_GetMixFormat,
1777 AudioClient_GetDevicePeriod,
1778 AudioClient_Start,
1779 AudioClient_Stop,
1780 AudioClient_Reset,
1781 AudioClient_SetEventHandle,
1782 AudioClient_GetService
1785 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1786 IAudioRenderClient *iface, REFIID riid, void **ppv)
1788 ACImpl *This = impl_from_IAudioRenderClient(iface);
1789 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1791 if(!ppv)
1792 return E_POINTER;
1793 *ppv = NULL;
1795 if(IsEqualIID(riid, &IID_IUnknown) ||
1796 IsEqualIID(riid, &IID_IAudioRenderClient))
1797 *ppv = iface;
1798 else if(IsEqualIID(riid, &IID_IMarshal))
1799 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1800 if(*ppv){
1801 IUnknown_AddRef((IUnknown*)*ppv);
1802 return S_OK;
1805 WARN("Unknown interface %s\n", debugstr_guid(riid));
1806 return E_NOINTERFACE;
1809 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1811 ACImpl *This = impl_from_IAudioRenderClient(iface);
1812 return AudioClient_AddRef(&This->IAudioClient_iface);
1815 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1817 ACImpl *This = impl_from_IAudioRenderClient(iface);
1818 return AudioClient_Release(&This->IAudioClient_iface);
1821 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1822 UINT32 frames, BYTE **data)
1824 ACImpl *This = impl_from_IAudioRenderClient(iface);
1825 UINT32 write_pos;
1827 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1829 if(!data)
1830 return E_POINTER;
1832 *data = NULL;
1834 EnterCriticalSection(&This->lock);
1836 if(This->getbuf_last){
1837 LeaveCriticalSection(&This->lock);
1838 return AUDCLNT_E_OUT_OF_ORDER;
1841 if(!frames){
1842 LeaveCriticalSection(&This->lock);
1843 return S_OK;
1846 if(This->held_frames + frames > This->bufsize_frames){
1847 LeaveCriticalSection(&This->lock);
1848 return AUDCLNT_E_BUFFER_TOO_LARGE;
1851 write_pos =
1852 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1853 if(write_pos + frames > This->bufsize_frames){
1854 if(This->tmp_buffer_frames < frames){
1855 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1856 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1857 frames * This->fmt->nBlockAlign);
1858 if(!This->tmp_buffer){
1859 LeaveCriticalSection(&This->lock);
1860 return E_OUTOFMEMORY;
1862 This->tmp_buffer_frames = frames;
1864 *data = This->tmp_buffer;
1865 This->getbuf_last = -frames;
1866 }else{
1867 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1868 This->getbuf_last = frames;
1871 silence_buffer(This, *data, frames);
1873 LeaveCriticalSection(&This->lock);
1875 return S_OK;
1878 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1880 UINT32 write_offs_frames =
1881 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1882 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1883 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1884 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1885 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1887 if(written_bytes <= chunk_bytes){
1888 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1889 }else{
1890 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1891 memcpy(This->local_buffer, buffer + chunk_bytes,
1892 written_bytes - chunk_bytes);
1896 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1897 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1899 ACImpl *This = impl_from_IAudioRenderClient(iface);
1900 BYTE *buffer;
1902 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1904 EnterCriticalSection(&This->lock);
1906 if(!written_frames){
1907 This->getbuf_last = 0;
1908 LeaveCriticalSection(&This->lock);
1909 return S_OK;
1912 if(!This->getbuf_last){
1913 LeaveCriticalSection(&This->lock);
1914 return AUDCLNT_E_OUT_OF_ORDER;
1917 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1918 LeaveCriticalSection(&This->lock);
1919 return AUDCLNT_E_INVALID_SIZE;
1922 if(This->getbuf_last >= 0)
1923 buffer = This->local_buffer + This->fmt->nBlockAlign *
1924 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1925 else
1926 buffer = This->tmp_buffer;
1928 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1929 silence_buffer(This, buffer, written_frames);
1931 if(This->getbuf_last < 0)
1932 oss_wrap_buffer(This, buffer, written_frames);
1934 This->held_frames += written_frames;
1935 This->written_frames += written_frames;
1936 This->getbuf_last = 0;
1938 LeaveCriticalSection(&This->lock);
1940 return S_OK;
1943 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1944 AudioRenderClient_QueryInterface,
1945 AudioRenderClient_AddRef,
1946 AudioRenderClient_Release,
1947 AudioRenderClient_GetBuffer,
1948 AudioRenderClient_ReleaseBuffer
1951 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1952 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1954 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1955 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1957 if(!ppv)
1958 return E_POINTER;
1959 *ppv = NULL;
1961 if(IsEqualIID(riid, &IID_IUnknown) ||
1962 IsEqualIID(riid, &IID_IAudioCaptureClient))
1963 *ppv = iface;
1964 else if(IsEqualIID(riid, &IID_IMarshal))
1965 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1966 if(*ppv){
1967 IUnknown_AddRef((IUnknown*)*ppv);
1968 return S_OK;
1971 WARN("Unknown interface %s\n", debugstr_guid(riid));
1972 return E_NOINTERFACE;
1975 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1977 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1978 return IAudioClient_AddRef(&This->IAudioClient_iface);
1981 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1983 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1984 return IAudioClient_Release(&This->IAudioClient_iface);
1987 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1988 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1989 UINT64 *qpcpos)
1991 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1993 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1994 devpos, qpcpos);
1996 if(!data || !frames || !flags)
1997 return E_POINTER;
1999 EnterCriticalSection(&This->lock);
2001 if(This->getbuf_last){
2002 LeaveCriticalSection(&This->lock);
2003 return AUDCLNT_E_OUT_OF_ORDER;
2006 if(This->held_frames < This->period_frames){
2007 *frames = 0;
2008 LeaveCriticalSection(&This->lock);
2009 return AUDCLNT_S_BUFFER_EMPTY;
2012 *flags = 0;
2014 *frames = This->period_frames;
2016 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2017 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2018 if(This->tmp_buffer_frames < *frames){
2019 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2020 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2021 *frames * This->fmt->nBlockAlign);
2022 if(!This->tmp_buffer){
2023 LeaveCriticalSection(&This->lock);
2024 return E_OUTOFMEMORY;
2026 This->tmp_buffer_frames = *frames;
2029 *data = This->tmp_buffer;
2030 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2031 This->fmt->nBlockAlign;
2032 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2033 frames_bytes = *frames * This->fmt->nBlockAlign;
2034 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2035 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2036 frames_bytes - chunk_bytes);
2037 }else
2038 *data = This->local_buffer +
2039 This->lcl_offs_frames * This->fmt->nBlockAlign;
2041 This->getbuf_last = *frames;
2043 if(devpos)
2044 *devpos = This->written_frames;
2045 if(qpcpos){
2046 LARGE_INTEGER stamp, freq;
2047 QueryPerformanceCounter(&stamp);
2048 QueryPerformanceFrequency(&freq);
2049 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2052 LeaveCriticalSection(&This->lock);
2054 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2057 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2058 IAudioCaptureClient *iface, UINT32 done)
2060 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2062 TRACE("(%p)->(%u)\n", This, done);
2064 EnterCriticalSection(&This->lock);
2066 if(!done){
2067 This->getbuf_last = 0;
2068 LeaveCriticalSection(&This->lock);
2069 return S_OK;
2072 if(!This->getbuf_last){
2073 LeaveCriticalSection(&This->lock);
2074 return AUDCLNT_E_OUT_OF_ORDER;
2077 if(This->getbuf_last != done){
2078 LeaveCriticalSection(&This->lock);
2079 return AUDCLNT_E_INVALID_SIZE;
2082 This->written_frames += done;
2083 This->held_frames -= done;
2084 This->lcl_offs_frames += done;
2085 This->lcl_offs_frames %= This->bufsize_frames;
2086 This->getbuf_last = 0;
2088 LeaveCriticalSection(&This->lock);
2090 return S_OK;
2093 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2094 IAudioCaptureClient *iface, UINT32 *frames)
2096 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2098 TRACE("(%p)->(%p)\n", This, frames);
2100 if(!frames)
2101 return E_POINTER;
2103 EnterCriticalSection(&This->lock);
2105 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2107 LeaveCriticalSection(&This->lock);
2109 return S_OK;
2112 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2114 AudioCaptureClient_QueryInterface,
2115 AudioCaptureClient_AddRef,
2116 AudioCaptureClient_Release,
2117 AudioCaptureClient_GetBuffer,
2118 AudioCaptureClient_ReleaseBuffer,
2119 AudioCaptureClient_GetNextPacketSize
2122 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2123 REFIID riid, void **ppv)
2125 ACImpl *This = impl_from_IAudioClock(iface);
2127 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2129 if(!ppv)
2130 return E_POINTER;
2131 *ppv = NULL;
2133 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2134 *ppv = iface;
2135 else if(IsEqualIID(riid, &IID_IAudioClock2))
2136 *ppv = &This->IAudioClock2_iface;
2137 if(*ppv){
2138 IUnknown_AddRef((IUnknown*)*ppv);
2139 return S_OK;
2142 WARN("Unknown interface %s\n", debugstr_guid(riid));
2143 return E_NOINTERFACE;
2146 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2148 ACImpl *This = impl_from_IAudioClock(iface);
2149 return IAudioClient_AddRef(&This->IAudioClient_iface);
2152 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2154 ACImpl *This = impl_from_IAudioClock(iface);
2155 return IAudioClient_Release(&This->IAudioClient_iface);
2158 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2160 ACImpl *This = impl_from_IAudioClock(iface);
2162 TRACE("(%p)->(%p)\n", This, freq);
2164 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2165 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2166 else
2167 *freq = This->fmt->nSamplesPerSec;
2169 return S_OK;
2172 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2173 UINT64 *qpctime)
2175 ACImpl *This = impl_from_IAudioClock(iface);
2177 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2179 if(!pos)
2180 return E_POINTER;
2182 EnterCriticalSection(&This->lock);
2184 if(This->dataflow == eRender){
2185 *pos = This->written_frames - This->held_frames;
2186 if(*pos < This->last_pos_frames)
2187 *pos = This->last_pos_frames;
2188 }else if(This->dataflow == eCapture){
2189 audio_buf_info bi;
2190 UINT32 held;
2192 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2193 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2194 held = 0;
2195 }else{
2196 if(bi.bytes <= bi.fragsize)
2197 held = 0;
2198 else
2199 held = bi.bytes / This->fmt->nBlockAlign;
2202 *pos = This->written_frames + held;
2205 This->last_pos_frames = *pos;
2207 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2208 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2209 *pos *= This->fmt->nBlockAlign;
2211 LeaveCriticalSection(&This->lock);
2213 if(qpctime){
2214 LARGE_INTEGER stamp, freq;
2215 QueryPerformanceCounter(&stamp);
2216 QueryPerformanceFrequency(&freq);
2217 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2220 return S_OK;
2223 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2224 DWORD *chars)
2226 ACImpl *This = impl_from_IAudioClock(iface);
2228 TRACE("(%p)->(%p)\n", This, chars);
2230 if(!chars)
2231 return E_POINTER;
2233 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2235 return S_OK;
2238 static const IAudioClockVtbl AudioClock_Vtbl =
2240 AudioClock_QueryInterface,
2241 AudioClock_AddRef,
2242 AudioClock_Release,
2243 AudioClock_GetFrequency,
2244 AudioClock_GetPosition,
2245 AudioClock_GetCharacteristics
2248 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2249 REFIID riid, void **ppv)
2251 ACImpl *This = impl_from_IAudioClock2(iface);
2252 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2255 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2257 ACImpl *This = impl_from_IAudioClock2(iface);
2258 return IAudioClient_AddRef(&This->IAudioClient_iface);
2261 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2263 ACImpl *This = impl_from_IAudioClock2(iface);
2264 return IAudioClient_Release(&This->IAudioClient_iface);
2267 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2268 UINT64 *pos, UINT64 *qpctime)
2270 ACImpl *This = impl_from_IAudioClock2(iface);
2272 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2274 return E_NOTIMPL;
2277 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2279 AudioClock2_QueryInterface,
2280 AudioClock2_AddRef,
2281 AudioClock2_Release,
2282 AudioClock2_GetDevicePosition
2285 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2287 AudioSessionWrapper *ret;
2289 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2290 sizeof(AudioSessionWrapper));
2291 if(!ret)
2292 return NULL;
2294 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2295 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2296 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2298 ret->ref = 1;
2300 ret->client = client;
2301 if(client){
2302 ret->session = client->session;
2303 AudioClient_AddRef(&client->IAudioClient_iface);
2306 return ret;
2309 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2310 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2312 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2314 if(!ppv)
2315 return E_POINTER;
2316 *ppv = NULL;
2318 if(IsEqualIID(riid, &IID_IUnknown) ||
2319 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2320 IsEqualIID(riid, &IID_IAudioSessionControl2))
2321 *ppv = iface;
2322 if(*ppv){
2323 IUnknown_AddRef((IUnknown*)*ppv);
2324 return S_OK;
2327 WARN("Unknown interface %s\n", debugstr_guid(riid));
2328 return E_NOINTERFACE;
2331 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2333 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2334 ULONG ref;
2335 ref = InterlockedIncrement(&This->ref);
2336 TRACE("(%p) Refcount now %u\n", This, ref);
2337 return ref;
2340 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2342 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2343 ULONG ref;
2344 ref = InterlockedDecrement(&This->ref);
2345 TRACE("(%p) Refcount now %u\n", This, ref);
2346 if(!ref){
2347 if(This->client){
2348 EnterCriticalSection(&This->client->lock);
2349 This->client->session_wrapper = NULL;
2350 LeaveCriticalSection(&This->client->lock);
2351 AudioClient_Release(&This->client->IAudioClient_iface);
2353 HeapFree(GetProcessHeap(), 0, This);
2355 return ref;
2358 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2359 AudioSessionState *state)
2361 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2362 ACImpl *client;
2364 TRACE("(%p)->(%p)\n", This, state);
2366 if(!state)
2367 return NULL_PTR_ERR;
2369 EnterCriticalSection(&g_sessions_lock);
2371 if(list_empty(&This->session->clients)){
2372 *state = AudioSessionStateExpired;
2373 LeaveCriticalSection(&g_sessions_lock);
2374 return S_OK;
2377 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2378 EnterCriticalSection(&client->lock);
2379 if(client->playing){
2380 *state = AudioSessionStateActive;
2381 LeaveCriticalSection(&client->lock);
2382 LeaveCriticalSection(&g_sessions_lock);
2383 return S_OK;
2385 LeaveCriticalSection(&client->lock);
2388 LeaveCriticalSection(&g_sessions_lock);
2390 *state = AudioSessionStateInactive;
2392 return S_OK;
2395 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2396 IAudioSessionControl2 *iface, WCHAR **name)
2398 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2400 FIXME("(%p)->(%p) - stub\n", This, name);
2402 return E_NOTIMPL;
2405 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2406 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2408 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2410 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2412 return E_NOTIMPL;
2415 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2416 IAudioSessionControl2 *iface, WCHAR **path)
2418 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2420 FIXME("(%p)->(%p) - stub\n", This, path);
2422 return E_NOTIMPL;
2425 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2426 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2428 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2430 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2432 return E_NOTIMPL;
2435 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2436 IAudioSessionControl2 *iface, GUID *group)
2438 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2440 FIXME("(%p)->(%p) - stub\n", This, group);
2442 return E_NOTIMPL;
2445 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2446 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2448 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2450 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2451 debugstr_guid(session));
2453 return E_NOTIMPL;
2456 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2457 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2459 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2461 FIXME("(%p)->(%p) - stub\n", This, events);
2463 return S_OK;
2466 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2467 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2469 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2471 FIXME("(%p)->(%p) - stub\n", This, events);
2473 return S_OK;
2476 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2477 IAudioSessionControl2 *iface, WCHAR **id)
2479 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2481 FIXME("(%p)->(%p) - stub\n", This, id);
2483 return E_NOTIMPL;
2486 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2487 IAudioSessionControl2 *iface, WCHAR **id)
2489 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2491 FIXME("(%p)->(%p) - stub\n", This, id);
2493 return E_NOTIMPL;
2496 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2497 IAudioSessionControl2 *iface, DWORD *pid)
2499 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2501 TRACE("(%p)->(%p)\n", This, pid);
2503 if(!pid)
2504 return E_POINTER;
2506 *pid = GetCurrentProcessId();
2508 return S_OK;
2511 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2512 IAudioSessionControl2 *iface)
2514 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2516 TRACE("(%p)\n", This);
2518 return S_FALSE;
2521 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2522 IAudioSessionControl2 *iface, BOOL optout)
2524 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2526 TRACE("(%p)->(%d)\n", This, optout);
2528 return S_OK;
2531 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2533 AudioSessionControl_QueryInterface,
2534 AudioSessionControl_AddRef,
2535 AudioSessionControl_Release,
2536 AudioSessionControl_GetState,
2537 AudioSessionControl_GetDisplayName,
2538 AudioSessionControl_SetDisplayName,
2539 AudioSessionControl_GetIconPath,
2540 AudioSessionControl_SetIconPath,
2541 AudioSessionControl_GetGroupingParam,
2542 AudioSessionControl_SetGroupingParam,
2543 AudioSessionControl_RegisterAudioSessionNotification,
2544 AudioSessionControl_UnregisterAudioSessionNotification,
2545 AudioSessionControl_GetSessionIdentifier,
2546 AudioSessionControl_GetSessionInstanceIdentifier,
2547 AudioSessionControl_GetProcessId,
2548 AudioSessionControl_IsSystemSoundsSession,
2549 AudioSessionControl_SetDuckingPreference
2552 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2553 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2555 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2557 if(!ppv)
2558 return E_POINTER;
2559 *ppv = NULL;
2561 if(IsEqualIID(riid, &IID_IUnknown) ||
2562 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2563 *ppv = iface;
2564 if(*ppv){
2565 IUnknown_AddRef((IUnknown*)*ppv);
2566 return S_OK;
2569 WARN("Unknown interface %s\n", debugstr_guid(riid));
2570 return E_NOINTERFACE;
2573 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2575 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2576 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2579 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2581 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2582 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2585 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2586 ISimpleAudioVolume *iface, float level, const GUID *context)
2588 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2589 AudioSession *session = This->session;
2591 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2593 if(level < 0.f || level > 1.f)
2594 return E_INVALIDARG;
2596 if(context)
2597 FIXME("Notifications not supported yet\n");
2599 EnterCriticalSection(&session->lock);
2601 session->master_vol = level;
2603 TRACE("OSS doesn't support setting volume\n");
2605 LeaveCriticalSection(&session->lock);
2607 return S_OK;
2610 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2611 ISimpleAudioVolume *iface, float *level)
2613 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2614 AudioSession *session = This->session;
2616 TRACE("(%p)->(%p)\n", session, level);
2618 if(!level)
2619 return NULL_PTR_ERR;
2621 *level = session->master_vol;
2623 return S_OK;
2626 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2627 BOOL mute, const GUID *context)
2629 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2630 AudioSession *session = This->session;
2632 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2634 EnterCriticalSection(&session->lock);
2636 session->mute = mute;
2638 LeaveCriticalSection(&session->lock);
2640 return S_OK;
2643 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2644 BOOL *mute)
2646 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2647 AudioSession *session = This->session;
2649 TRACE("(%p)->(%p)\n", session, mute);
2651 if(!mute)
2652 return NULL_PTR_ERR;
2654 *mute = This->session->mute;
2656 return S_OK;
2659 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2661 SimpleAudioVolume_QueryInterface,
2662 SimpleAudioVolume_AddRef,
2663 SimpleAudioVolume_Release,
2664 SimpleAudioVolume_SetMasterVolume,
2665 SimpleAudioVolume_GetMasterVolume,
2666 SimpleAudioVolume_SetMute,
2667 SimpleAudioVolume_GetMute
2670 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2671 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2673 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2675 if(!ppv)
2676 return E_POINTER;
2677 *ppv = NULL;
2679 if(IsEqualIID(riid, &IID_IUnknown) ||
2680 IsEqualIID(riid, &IID_IAudioStreamVolume))
2681 *ppv = iface;
2682 if(*ppv){
2683 IUnknown_AddRef((IUnknown*)*ppv);
2684 return S_OK;
2687 WARN("Unknown interface %s\n", debugstr_guid(riid));
2688 return E_NOINTERFACE;
2691 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2693 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2694 return IAudioClient_AddRef(&This->IAudioClient_iface);
2697 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2699 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2700 return IAudioClient_Release(&This->IAudioClient_iface);
2703 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2704 IAudioStreamVolume *iface, UINT32 *out)
2706 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2708 TRACE("(%p)->(%p)\n", This, out);
2710 if(!out)
2711 return E_POINTER;
2713 *out = This->fmt->nChannels;
2715 return S_OK;
2718 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2719 IAudioStreamVolume *iface, UINT32 index, float level)
2721 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2723 TRACE("(%p)->(%d, %f)\n", This, index, level);
2725 if(level < 0.f || level > 1.f)
2726 return E_INVALIDARG;
2728 if(index >= This->fmt->nChannels)
2729 return E_INVALIDARG;
2731 EnterCriticalSection(&This->lock);
2733 This->vols[index] = level;
2735 TRACE("OSS doesn't support setting volume\n");
2737 LeaveCriticalSection(&This->lock);
2739 return S_OK;
2742 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2743 IAudioStreamVolume *iface, UINT32 index, float *level)
2745 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2747 TRACE("(%p)->(%d, %p)\n", This, index, level);
2749 if(!level)
2750 return E_POINTER;
2752 if(index >= This->fmt->nChannels)
2753 return E_INVALIDARG;
2755 *level = This->vols[index];
2757 return S_OK;
2760 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2761 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2763 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2764 int i;
2766 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2768 if(!levels)
2769 return E_POINTER;
2771 if(count != This->fmt->nChannels)
2772 return E_INVALIDARG;
2774 EnterCriticalSection(&This->lock);
2776 for(i = 0; i < count; ++i)
2777 This->vols[i] = levels[i];
2779 TRACE("OSS doesn't support setting volume\n");
2781 LeaveCriticalSection(&This->lock);
2783 return S_OK;
2786 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2787 IAudioStreamVolume *iface, UINT32 count, float *levels)
2789 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2790 int i;
2792 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2794 if(!levels)
2795 return E_POINTER;
2797 if(count != This->fmt->nChannels)
2798 return E_INVALIDARG;
2800 EnterCriticalSection(&This->lock);
2802 for(i = 0; i < count; ++i)
2803 levels[i] = This->vols[i];
2805 LeaveCriticalSection(&This->lock);
2807 return S_OK;
2810 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2812 AudioStreamVolume_QueryInterface,
2813 AudioStreamVolume_AddRef,
2814 AudioStreamVolume_Release,
2815 AudioStreamVolume_GetChannelCount,
2816 AudioStreamVolume_SetChannelVolume,
2817 AudioStreamVolume_GetChannelVolume,
2818 AudioStreamVolume_SetAllVolumes,
2819 AudioStreamVolume_GetAllVolumes
2822 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2823 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2825 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2827 if(!ppv)
2828 return E_POINTER;
2829 *ppv = NULL;
2831 if(IsEqualIID(riid, &IID_IUnknown) ||
2832 IsEqualIID(riid, &IID_IChannelAudioVolume))
2833 *ppv = iface;
2834 if(*ppv){
2835 IUnknown_AddRef((IUnknown*)*ppv);
2836 return S_OK;
2839 WARN("Unknown interface %s\n", debugstr_guid(riid));
2840 return E_NOINTERFACE;
2843 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2845 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2846 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2849 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2851 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2852 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2855 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2856 IChannelAudioVolume *iface, UINT32 *out)
2858 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2859 AudioSession *session = This->session;
2861 TRACE("(%p)->(%p)\n", session, out);
2863 if(!out)
2864 return NULL_PTR_ERR;
2866 *out = session->channel_count;
2868 return S_OK;
2871 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2872 IChannelAudioVolume *iface, UINT32 index, float level,
2873 const GUID *context)
2875 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2876 AudioSession *session = This->session;
2878 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2879 wine_dbgstr_guid(context));
2881 if(level < 0.f || level > 1.f)
2882 return E_INVALIDARG;
2884 if(index >= session->channel_count)
2885 return E_INVALIDARG;
2887 if(context)
2888 FIXME("Notifications not supported yet\n");
2890 EnterCriticalSection(&session->lock);
2892 session->channel_vols[index] = level;
2894 TRACE("OSS doesn't support setting volume\n");
2896 LeaveCriticalSection(&session->lock);
2898 return S_OK;
2901 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2902 IChannelAudioVolume *iface, UINT32 index, float *level)
2904 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2905 AudioSession *session = This->session;
2907 TRACE("(%p)->(%d, %p)\n", session, index, level);
2909 if(!level)
2910 return NULL_PTR_ERR;
2912 if(index >= session->channel_count)
2913 return E_INVALIDARG;
2915 *level = session->channel_vols[index];
2917 return S_OK;
2920 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2921 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2922 const GUID *context)
2924 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2925 AudioSession *session = This->session;
2926 int i;
2928 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2929 wine_dbgstr_guid(context));
2931 if(!levels)
2932 return NULL_PTR_ERR;
2934 if(count != session->channel_count)
2935 return E_INVALIDARG;
2937 if(context)
2938 FIXME("Notifications not supported yet\n");
2940 EnterCriticalSection(&session->lock);
2942 for(i = 0; i < count; ++i)
2943 session->channel_vols[i] = levels[i];
2945 TRACE("OSS doesn't support setting volume\n");
2947 LeaveCriticalSection(&session->lock);
2949 return S_OK;
2952 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2953 IChannelAudioVolume *iface, UINT32 count, float *levels)
2955 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2956 AudioSession *session = This->session;
2957 int i;
2959 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2961 if(!levels)
2962 return NULL_PTR_ERR;
2964 if(count != session->channel_count)
2965 return E_INVALIDARG;
2967 for(i = 0; i < count; ++i)
2968 levels[i] = session->channel_vols[i];
2970 return S_OK;
2973 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2975 ChannelAudioVolume_QueryInterface,
2976 ChannelAudioVolume_AddRef,
2977 ChannelAudioVolume_Release,
2978 ChannelAudioVolume_GetChannelCount,
2979 ChannelAudioVolume_SetChannelVolume,
2980 ChannelAudioVolume_GetChannelVolume,
2981 ChannelAudioVolume_SetAllVolumes,
2982 ChannelAudioVolume_GetAllVolumes
2985 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2986 REFIID riid, void **ppv)
2988 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2990 if(!ppv)
2991 return E_POINTER;
2992 *ppv = NULL;
2994 if(IsEqualIID(riid, &IID_IUnknown) ||
2995 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2996 IsEqualIID(riid, &IID_IAudioSessionManager2))
2997 *ppv = iface;
2998 if(*ppv){
2999 IUnknown_AddRef((IUnknown*)*ppv);
3000 return S_OK;
3003 WARN("Unknown interface %s\n", debugstr_guid(riid));
3004 return E_NOINTERFACE;
3007 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3009 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3010 ULONG ref;
3011 ref = InterlockedIncrement(&This->ref);
3012 TRACE("(%p) Refcount now %u\n", This, ref);
3013 return ref;
3016 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3018 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3019 ULONG ref;
3020 ref = InterlockedDecrement(&This->ref);
3021 TRACE("(%p) Refcount now %u\n", This, ref);
3022 if(!ref)
3023 HeapFree(GetProcessHeap(), 0, This);
3024 return ref;
3027 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3028 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3029 IAudioSessionControl **out)
3031 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3032 AudioSession *session;
3033 AudioSessionWrapper *wrapper;
3034 HRESULT hr;
3036 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3037 flags, out);
3039 hr = get_audio_session(session_guid, This->device, 0, &session);
3040 if(FAILED(hr))
3041 return hr;
3043 wrapper = AudioSessionWrapper_Create(NULL);
3044 if(!wrapper)
3045 return E_OUTOFMEMORY;
3047 wrapper->session = session;
3049 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3051 return S_OK;
3054 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3055 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3056 ISimpleAudioVolume **out)
3058 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3059 AudioSession *session;
3060 AudioSessionWrapper *wrapper;
3061 HRESULT hr;
3063 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3064 flags, out);
3066 hr = get_audio_session(session_guid, This->device, 0, &session);
3067 if(FAILED(hr))
3068 return hr;
3070 wrapper = AudioSessionWrapper_Create(NULL);
3071 if(!wrapper)
3072 return E_OUTOFMEMORY;
3074 wrapper->session = session;
3076 *out = &wrapper->ISimpleAudioVolume_iface;
3078 return S_OK;
3081 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3082 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3084 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3085 FIXME("(%p)->(%p) - stub\n", This, out);
3086 return E_NOTIMPL;
3089 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3090 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3092 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3093 FIXME("(%p)->(%p) - stub\n", This, notification);
3094 return E_NOTIMPL;
3097 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3098 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3100 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3101 FIXME("(%p)->(%p) - stub\n", This, notification);
3102 return E_NOTIMPL;
3105 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3106 IAudioSessionManager2 *iface, const WCHAR *session_id,
3107 IAudioVolumeDuckNotification *notification)
3109 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3110 FIXME("(%p)->(%p) - stub\n", This, notification);
3111 return E_NOTIMPL;
3114 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3115 IAudioSessionManager2 *iface,
3116 IAudioVolumeDuckNotification *notification)
3118 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3119 FIXME("(%p)->(%p) - stub\n", This, notification);
3120 return E_NOTIMPL;
3123 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3125 AudioSessionManager_QueryInterface,
3126 AudioSessionManager_AddRef,
3127 AudioSessionManager_Release,
3128 AudioSessionManager_GetAudioSessionControl,
3129 AudioSessionManager_GetSimpleAudioVolume,
3130 AudioSessionManager_GetSessionEnumerator,
3131 AudioSessionManager_RegisterSessionNotification,
3132 AudioSessionManager_UnregisterSessionNotification,
3133 AudioSessionManager_RegisterDuckNotification,
3134 AudioSessionManager_UnregisterDuckNotification
3137 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3138 IAudioSessionManager2 **out)
3140 SessionMgr *This;
3142 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3143 if(!This)
3144 return E_OUTOFMEMORY;
3146 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3147 This->device = device;
3148 This->ref = 1;
3150 *out = &This->IAudioSessionManager2_iface;
3152 return S_OK;