makefiles: Don't use standard libs for programs that specify -nodefaultlibs.
[wine/zf.git] / dlls / wineoss.drv / mmdevdrv.c
bloba08e7f561b57eaa06a68ea2f3ab20dd2374cf7d8
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, ARRAY_SIZE(key_name) - 2);
354 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
355 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
356 if(RegQueryValueExW(dev_key, guidW, 0, &type,
357 (BYTE*)guid, &size) == ERROR_SUCCESS){
358 if(type == REG_BINARY){
359 RegCloseKey(dev_key);
360 RegCloseKey(key);
361 return;
363 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
364 wine_dbgstr_w(key_name), type);
366 RegCloseKey(dev_key);
370 CoCreateGuid(guid);
372 set_device_guid(flow, key, key_name, guid);
374 if(key)
375 RegCloseKey(key);
378 static const char *oss_clean_devnode(const char *devnode)
380 static char ret[OSS_DEVNODE_SIZE];
382 const char *dot, *slash;
383 size_t len;
385 dot = strrchr(devnode, '.');
386 if(!dot)
387 return devnode;
389 slash = strrchr(devnode, '/');
390 if(slash && dot < slash)
391 return devnode;
393 len = dot - devnode;
395 memcpy(ret, devnode, len);
396 ret[len] = '\0';
398 return ret;
401 static UINT get_default_index(EDataFlow flow)
403 int fd = -1, err;
404 UINT i;
405 oss_audioinfo ai;
406 const char *devnode;
407 OSSDevice *dev_item;
409 if(flow == eRender)
410 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
411 else
412 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
414 if(fd < 0){
415 WARN("Couldn't open default device!\n");
416 return 0;
419 ai.dev = -1;
420 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
421 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
422 close(fd);
423 return 0;
426 close(fd);
428 TRACE("Default devnode: %s\n", ai.devnode);
429 devnode = oss_clean_devnode(ai.devnode);
430 i = 0;
431 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
432 if(dev_item->flow == flow){
433 if(!strcmp(devnode, dev_item->devnode))
434 return i;
435 ++i;
439 WARN("Couldn't find default device! Choosing first.\n");
440 return 0;
443 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
444 UINT *num, UINT *def_index)
446 int i, mixer_fd;
447 oss_sysinfo sysinfo;
448 static int print_once = 0;
450 static const WCHAR outW[] = {'O','u','t',':',' ',0};
451 static const WCHAR inW[] = {'I','n',':',' ',0};
453 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
455 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
456 if(mixer_fd < 0){
457 ERR("OSS /dev/mixer doesn't seem to exist\n");
458 return AUDCLNT_E_SERVICE_NOT_RUNNING;
461 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
462 close(mixer_fd);
464 if(errno == EINVAL){
465 ERR("OSS version too old, need at least OSSv4\n");
466 return AUDCLNT_E_SERVICE_NOT_RUNNING;
469 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
470 return E_FAIL;
473 if(!print_once){
474 TRACE("OSS sysinfo:\n");
475 TRACE("product: %s\n", sysinfo.product);
476 TRACE("version: %s\n", sysinfo.version);
477 TRACE("versionnum: %x\n", sysinfo.versionnum);
478 TRACE("numaudios: %d\n", sysinfo.numaudios);
479 TRACE("nummixers: %d\n", sysinfo.nummixers);
480 TRACE("numcards: %d\n", sysinfo.numcards);
481 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
482 print_once = 1;
485 if(sysinfo.numaudios <= 0){
486 WARN("No audio devices!\n");
487 close(mixer_fd);
488 return AUDCLNT_E_SERVICE_NOT_RUNNING;
491 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
492 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
494 *num = 0;
495 for(i = 0; i < sysinfo.numaudios; ++i){
496 oss_audioinfo ai = {0};
497 const char *devnode;
498 OSSDevice *dev_item;
499 int fd;
501 ai.dev = i;
502 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
503 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
504 strerror(errno));
505 continue;
508 devnode = oss_clean_devnode(ai.devnode);
510 /* check for duplicates */
511 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
512 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
513 break;
515 if(&dev_item->entry != &g_devices)
516 continue;
518 if(flow == eRender)
519 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
520 else
521 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
522 if(fd < 0){
523 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
524 devnode, errno, strerror(errno));
525 continue;
527 close(fd);
529 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
530 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
531 size_t len, prefix_len;
532 const WCHAR *prefix;
534 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
536 dev_item->flow = flow;
537 get_device_guid(flow, devnode, &dev_item->guid);
538 strcpy(dev_item->devnode, devnode);
540 (*guids)[*num] = dev_item->guid;
542 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
543 if(flow == eRender){
544 prefix = outW;
545 prefix_len = ARRAY_SIZE(outW) - 1;
546 len += prefix_len;
547 }else{
548 prefix = inW;
549 prefix_len = ARRAY_SIZE(inW) - 1;
550 len += prefix_len;
552 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
553 len * sizeof(WCHAR));
554 if(!(*ids)[*num]){
555 for(i = 0; i < *num; ++i)
556 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
557 HeapFree(GetProcessHeap(), 0, *ids);
558 HeapFree(GetProcessHeap(), 0, *guids);
559 HeapFree(GetProcessHeap(), 0, dev_item);
560 close(mixer_fd);
561 return E_OUTOFMEMORY;
563 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
564 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
565 (*ids)[*num] + prefix_len, len - prefix_len);
567 list_add_tail(&g_devices, &dev_item->entry);
569 (*num)++;
573 close(mixer_fd);
575 *def_index = get_default_index(flow);
577 return S_OK;
580 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
582 OSSDevice *dev_item;
583 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
584 if(IsEqualGUID(guid, &dev_item->guid))
585 return dev_item;
586 return NULL;
589 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
590 IAudioClient **out)
592 ACImpl *This;
593 const OSSDevice *oss_dev;
594 HRESULT hr;
596 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
598 oss_dev = get_ossdevice_from_guid(guid);
599 if(!oss_dev){
600 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
601 return AUDCLNT_E_DEVICE_INVALIDATED;
604 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
605 if(!This)
606 return E_OUTOFMEMORY;
608 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface, &This->pUnkFTMarshal);
609 if (FAILED(hr)) {
610 HeapFree(GetProcessHeap(), 0, This);
611 return hr;
614 if(oss_dev->flow == eRender)
615 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
616 else if(oss_dev->flow == eCapture)
617 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
618 else{
619 HeapFree(GetProcessHeap(), 0, This);
620 return E_INVALIDARG;
622 if(This->fd < 0){
623 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
624 strerror(errno));
625 HeapFree(GetProcessHeap(), 0, This);
626 return AUDCLNT_E_DEVICE_INVALIDATED;
629 This->dataflow = oss_dev->flow;
631 This->ai.dev = -1;
632 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
633 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
634 errno, strerror(errno));
635 close(This->fd);
636 HeapFree(GetProcessHeap(), 0, This);
637 return E_FAIL;
640 strcpy(This->devnode, oss_dev->devnode);
642 TRACE("OSS audioinfo:\n");
643 TRACE("devnode: %s\n", This->ai.devnode);
644 TRACE("name: %s\n", This->ai.name);
645 TRACE("busy: %x\n", This->ai.busy);
646 TRACE("caps: %x\n", This->ai.caps);
647 TRACE("iformats: %x\n", This->ai.iformats);
648 TRACE("oformats: %x\n", This->ai.oformats);
649 TRACE("enabled: %d\n", This->ai.enabled);
650 TRACE("min_rate: %d\n", This->ai.min_rate);
651 TRACE("max_rate: %d\n", This->ai.max_rate);
652 TRACE("min_channels: %d\n", This->ai.min_channels);
653 TRACE("max_channels: %d\n", This->ai.max_channels);
655 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
656 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
657 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
658 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
659 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
660 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
662 InitializeCriticalSection(&This->lock);
663 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
665 This->parent = dev;
666 IMMDevice_AddRef(This->parent);
668 IAudioClient_AddRef(&This->IAudioClient_iface);
670 *out = &This->IAudioClient_iface;
672 return S_OK;
675 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
676 REFIID riid, void **ppv)
678 ACImpl *This = impl_from_IAudioClient(iface);
679 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
681 if(!ppv)
682 return E_POINTER;
683 *ppv = NULL;
684 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
685 *ppv = iface;
686 else if(IsEqualIID(riid, &IID_IMarshal))
687 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
688 if(*ppv){
689 IUnknown_AddRef((IUnknown*)*ppv);
690 return S_OK;
692 WARN("Unknown interface %s\n", debugstr_guid(riid));
693 return E_NOINTERFACE;
696 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
698 ACImpl *This = impl_from_IAudioClient(iface);
699 ULONG ref;
700 ref = InterlockedIncrement(&This->ref);
701 TRACE("(%p) Refcount now %u\n", This, ref);
702 return ref;
705 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
707 ACImpl *This = impl_from_IAudioClient(iface);
708 ULONG ref;
710 ref = InterlockedDecrement(&This->ref);
711 TRACE("(%p) Refcount now %u\n", This, ref);
712 if(!ref){
713 if(This->timer){
714 HANDLE event;
715 DWORD wait;
716 event = CreateEventW(NULL, TRUE, FALSE, NULL);
717 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
718 wait = wait && GetLastError() == ERROR_IO_PENDING;
719 if(event && wait)
720 WaitForSingleObject(event, INFINITE);
721 CloseHandle(event);
724 IAudioClient_Stop(iface);
725 IMMDevice_Release(This->parent);
726 IUnknown_Release(This->pUnkFTMarshal);
727 This->lock.DebugInfo->Spare[0] = 0;
728 DeleteCriticalSection(&This->lock);
729 close(This->fd);
730 if(This->initted){
731 EnterCriticalSection(&g_sessions_lock);
732 list_remove(&This->entry);
733 LeaveCriticalSection(&g_sessions_lock);
735 HeapFree(GetProcessHeap(), 0, This->vols);
736 HeapFree(GetProcessHeap(), 0, This->local_buffer);
737 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
738 CoTaskMemFree(This->fmt);
739 HeapFree(GetProcessHeap(), 0, This);
741 return ref;
744 static void dump_fmt(const WAVEFORMATEX *fmt)
746 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
747 switch(fmt->wFormatTag){
748 case WAVE_FORMAT_PCM:
749 TRACE("WAVE_FORMAT_PCM");
750 break;
751 case WAVE_FORMAT_IEEE_FLOAT:
752 TRACE("WAVE_FORMAT_IEEE_FLOAT");
753 break;
754 case WAVE_FORMAT_EXTENSIBLE:
755 TRACE("WAVE_FORMAT_EXTENSIBLE");
756 break;
757 default:
758 TRACE("Unknown");
759 break;
761 TRACE(")\n");
763 TRACE("nChannels: %u\n", fmt->nChannels);
764 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
765 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
766 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
767 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
768 TRACE("cbSize: %u\n", fmt->cbSize);
770 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
771 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
772 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
773 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
774 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
778 static DWORD get_channel_mask(unsigned int channels)
780 switch(channels){
781 case 0:
782 return 0;
783 case 1:
784 return KSAUDIO_SPEAKER_MONO;
785 case 2:
786 return KSAUDIO_SPEAKER_STEREO;
787 case 3:
788 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
789 case 4:
790 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
791 case 5:
792 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
793 case 6:
794 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
795 case 7:
796 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
797 case 8:
798 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
800 FIXME("Unknown speaker configuration: %u\n", channels);
801 return 0;
804 static int get_oss_format(const WAVEFORMATEX *fmt)
806 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
808 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
809 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
810 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
811 switch(fmt->wBitsPerSample){
812 case 8:
813 return AFMT_U8;
814 case 16:
815 return AFMT_S16_LE;
816 case 24:
817 return AFMT_S24_LE;
818 case 32:
819 return AFMT_S32_LE;
821 return -1;
824 #ifdef AFMT_FLOAT
825 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
826 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
827 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
828 if(fmt->wBitsPerSample != 32)
829 return -1;
831 return AFMT_FLOAT;
833 #endif
835 return -1;
838 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
840 WAVEFORMATEX *ret;
841 size_t size;
843 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
844 size = sizeof(WAVEFORMATEXTENSIBLE);
845 else
846 size = sizeof(WAVEFORMATEX);
848 ret = CoTaskMemAlloc(size);
849 if(!ret)
850 return NULL;
852 memcpy(ret, fmt, size);
854 ret->cbSize = size - sizeof(WAVEFORMATEX);
856 return ret;
859 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
860 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
862 int tmp, oss_format;
863 double tenth;
864 HRESULT ret = S_OK;
865 WAVEFORMATEX *closest = NULL;
867 tmp = oss_format = get_oss_format(fmt);
868 if(oss_format < 0)
869 return AUDCLNT_E_UNSUPPORTED_FORMAT;
870 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
871 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
872 return E_FAIL;
874 if(tmp != oss_format){
875 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
876 return AUDCLNT_E_UNSUPPORTED_FORMAT;
879 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
880 (fmt->nAvgBytesPerSec == 0 ||
881 fmt->nBlockAlign == 0 ||
882 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
883 return E_INVALIDARG;
885 if(fmt->nChannels == 0)
886 return AUDCLNT_E_UNSUPPORTED_FORMAT;
888 closest = clone_format(fmt);
889 if(!closest)
890 return E_OUTOFMEMORY;
892 tmp = fmt->nSamplesPerSec;
893 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
894 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
895 CoTaskMemFree(closest);
896 return E_FAIL;
898 tenth = fmt->nSamplesPerSec * 0.1;
899 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
900 ret = S_FALSE;
901 closest->nSamplesPerSec = tmp;
904 tmp = fmt->nChannels;
905 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
906 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
907 CoTaskMemFree(closest);
908 return E_FAIL;
910 if(tmp != fmt->nChannels){
911 ret = S_FALSE;
912 closest->nChannels = tmp;
915 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
916 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
918 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
919 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
920 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
921 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
922 ret = S_FALSE;
924 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
925 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
926 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
927 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
928 ret = S_FALSE;
931 if(ret == S_FALSE && !out)
932 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
934 if(ret == S_FALSE && out){
935 closest->nBlockAlign =
936 closest->nChannels * closest->wBitsPerSample / 8;
937 closest->nAvgBytesPerSec =
938 closest->nBlockAlign * closest->nSamplesPerSec;
939 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
940 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
941 *out = closest;
942 } else
943 CoTaskMemFree(closest);
945 TRACE("returning: %08x\n", ret);
946 return ret;
949 static void session_init_vols(AudioSession *session, UINT channels)
951 if(session->channel_count < channels){
952 UINT i;
954 if(session->channel_vols)
955 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
956 session->channel_vols, sizeof(float) * channels);
957 else
958 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
959 sizeof(float) * channels);
960 if(!session->channel_vols)
961 return;
963 for(i = session->channel_count; i < channels; ++i)
964 session->channel_vols[i] = 1.f;
966 session->channel_count = channels;
970 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
971 UINT num_channels)
973 AudioSession *ret;
975 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
976 if(!ret)
977 return NULL;
979 memcpy(&ret->guid, guid, sizeof(GUID));
981 ret->device = device;
983 list_init(&ret->clients);
985 list_add_head(&g_sessions, &ret->entry);
987 InitializeCriticalSection(&ret->lock);
988 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
990 session_init_vols(ret, num_channels);
992 ret->master_vol = 1.f;
994 return ret;
997 /* if channels == 0, then this will return or create a session with
998 * matching dataflow and GUID. otherwise, channels must also match */
999 static HRESULT get_audio_session(const GUID *sessionguid,
1000 IMMDevice *device, UINT channels, AudioSession **out)
1002 AudioSession *session;
1004 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1005 *out = create_session(&GUID_NULL, device, channels);
1006 if(!*out)
1007 return E_OUTOFMEMORY;
1009 return S_OK;
1012 *out = NULL;
1013 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1014 if(session->device == device &&
1015 IsEqualGUID(sessionguid, &session->guid)){
1016 session_init_vols(session, channels);
1017 *out = session;
1018 break;
1022 if(!*out){
1023 *out = create_session(sessionguid, device, channels);
1024 if(!*out)
1025 return E_OUTOFMEMORY;
1028 return S_OK;
1031 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1032 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1033 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1034 const GUID *sessionguid)
1036 ACImpl *This = impl_from_IAudioClient(iface);
1037 int i;
1038 HRESULT hr;
1040 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1041 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1043 if(!fmt)
1044 return E_POINTER;
1046 dump_fmt(fmt);
1048 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1049 return E_INVALIDARG;
1051 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1052 AUDCLNT_STREAMFLAGS_LOOPBACK |
1053 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1054 AUDCLNT_STREAMFLAGS_NOPERSIST |
1055 AUDCLNT_STREAMFLAGS_RATEADJUST |
1056 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1057 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1058 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1059 TRACE("Unknown flags: %08x\n", flags);
1060 return E_INVALIDARG;
1063 if(mode == AUDCLNT_SHAREMODE_SHARED){
1064 period = DefaultPeriod;
1065 if( duration < 3 * period)
1066 duration = 3 * period;
1067 }else{
1068 if(!period)
1069 period = DefaultPeriod; /* not minimum */
1070 if(period < MinimumPeriod || period > 5000000)
1071 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1072 if(duration > 20000000) /* the smaller the period, the lower this limit */
1073 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1074 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1075 if(duration != period)
1076 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1077 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1078 return AUDCLNT_E_DEVICE_IN_USE;
1079 }else{
1080 if( duration < 8 * period)
1081 duration = 8 * period; /* may grow above 2s */
1085 EnterCriticalSection(&This->lock);
1087 if(This->initted){
1088 LeaveCriticalSection(&This->lock);
1089 return AUDCLNT_E_ALREADY_INITIALIZED;
1092 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1093 if(FAILED(hr)){
1094 LeaveCriticalSection(&This->lock);
1095 return hr;
1098 This->fmt = clone_format(fmt);
1099 if(!This->fmt){
1100 LeaveCriticalSection(&This->lock);
1101 return E_OUTOFMEMORY;
1104 This->period_us = period / 10;
1105 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1107 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1108 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1109 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1110 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1111 This->bufsize_frames * fmt->nBlockAlign);
1112 if(!This->local_buffer){
1113 CoTaskMemFree(This->fmt);
1114 This->fmt = NULL;
1115 LeaveCriticalSection(&This->lock);
1116 return E_OUTOFMEMORY;
1119 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1120 if(!This->vols){
1121 CoTaskMemFree(This->fmt);
1122 This->fmt = NULL;
1123 LeaveCriticalSection(&This->lock);
1124 return E_OUTOFMEMORY;
1127 for(i = 0; i < fmt->nChannels; ++i)
1128 This->vols[i] = 1.f;
1130 This->share = mode;
1131 This->flags = flags;
1132 This->oss_bufsize_bytes = 0;
1134 EnterCriticalSection(&g_sessions_lock);
1136 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1137 &This->session);
1138 if(FAILED(hr)){
1139 LeaveCriticalSection(&g_sessions_lock);
1140 HeapFree(GetProcessHeap(), 0, This->vols);
1141 This->vols = NULL;
1142 CoTaskMemFree(This->fmt);
1143 This->fmt = NULL;
1144 LeaveCriticalSection(&This->lock);
1145 return hr;
1148 list_add_tail(&This->session->clients, &This->entry);
1150 LeaveCriticalSection(&g_sessions_lock);
1152 This->initted = TRUE;
1154 LeaveCriticalSection(&This->lock);
1156 return S_OK;
1159 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1160 UINT32 *frames)
1162 ACImpl *This = impl_from_IAudioClient(iface);
1164 TRACE("(%p)->(%p)\n", This, frames);
1166 if(!frames)
1167 return E_POINTER;
1169 EnterCriticalSection(&This->lock);
1171 if(!This->initted){
1172 LeaveCriticalSection(&This->lock);
1173 return AUDCLNT_E_NOT_INITIALIZED;
1176 *frames = This->bufsize_frames;
1178 TRACE("buffer size: %u\n", *frames);
1180 LeaveCriticalSection(&This->lock);
1182 return S_OK;
1185 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1186 REFERENCE_TIME *latency)
1188 ACImpl *This = impl_from_IAudioClient(iface);
1190 TRACE("(%p)->(%p)\n", This, latency);
1192 if(!latency)
1193 return E_POINTER;
1195 EnterCriticalSection(&This->lock);
1197 if(!This->initted){
1198 LeaveCriticalSection(&This->lock);
1199 return AUDCLNT_E_NOT_INITIALIZED;
1202 /* pretend we process audio in Period chunks, so max latency includes
1203 * the period time. Some native machines add .6666ms in shared mode. */
1204 *latency = (REFERENCE_TIME)This->period_us * 10 + 6666;
1206 LeaveCriticalSection(&This->lock);
1208 return S_OK;
1211 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1212 UINT32 *numpad)
1214 ACImpl *This = impl_from_IAudioClient(iface);
1216 TRACE("(%p)->(%p)\n", This, numpad);
1218 if(!numpad)
1219 return E_POINTER;
1221 EnterCriticalSection(&This->lock);
1223 if(!This->initted){
1224 LeaveCriticalSection(&This->lock);
1225 return AUDCLNT_E_NOT_INITIALIZED;
1228 *numpad = This->held_frames;
1230 TRACE("padding: %u\n", *numpad);
1232 LeaveCriticalSection(&This->lock);
1234 return S_OK;
1237 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1238 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1239 WAVEFORMATEX **outpwfx)
1241 ACImpl *This = impl_from_IAudioClient(iface);
1242 int fd = -1;
1243 HRESULT ret;
1245 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1247 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1248 return E_POINTER;
1250 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1251 return E_INVALIDARG;
1253 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1254 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1255 return E_INVALIDARG;
1257 dump_fmt(pwfx);
1259 if(outpwfx){
1260 *outpwfx = NULL;
1261 if(mode != AUDCLNT_SHAREMODE_SHARED)
1262 outpwfx = NULL;
1265 if(This->dataflow == eRender)
1266 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1267 else if(This->dataflow == eCapture)
1268 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1270 if(fd < 0){
1271 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1272 strerror(errno));
1273 return AUDCLNT_E_DEVICE_INVALIDATED;
1276 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1278 close(fd);
1280 return ret;
1283 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1284 WAVEFORMATEX **pwfx)
1286 ACImpl *This = impl_from_IAudioClient(iface);
1287 WAVEFORMATEXTENSIBLE *fmt;
1288 int formats;
1290 TRACE("(%p)->(%p)\n", This, pwfx);
1292 if(!pwfx)
1293 return E_POINTER;
1294 *pwfx = NULL;
1296 if(This->dataflow == eRender)
1297 formats = This->ai.oformats;
1298 else if(This->dataflow == eCapture)
1299 formats = This->ai.iformats;
1300 else
1301 return E_UNEXPECTED;
1303 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1304 if(!fmt)
1305 return E_OUTOFMEMORY;
1307 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1308 if(formats & AFMT_S16_LE){
1309 fmt->Format.wBitsPerSample = 16;
1310 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1311 #ifdef AFMT_FLOAT
1312 }else if(formats & AFMT_FLOAT){
1313 fmt->Format.wBitsPerSample = 32;
1314 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1315 #endif
1316 }else if(formats & AFMT_U8){
1317 fmt->Format.wBitsPerSample = 8;
1318 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1319 }else if(formats & AFMT_S32_LE){
1320 fmt->Format.wBitsPerSample = 32;
1321 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1322 }else if(formats & AFMT_S24_LE){
1323 fmt->Format.wBitsPerSample = 24;
1324 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1325 }else{
1326 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1327 CoTaskMemFree(fmt);
1328 return E_FAIL;
1331 /* some OSS drivers are buggy, so set reasonable defaults if
1332 * the reported values seem wacky */
1333 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1334 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1335 fmt->Format.nChannels = 2;
1337 /* For most hardware on Windows, users must choose a configuration with an even
1338 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1339 * channels, but those channels are still reported to applications from
1340 * GetMixFormat! Some applications behave badly if given an odd number of
1341 * channels (e.g. 2.1). */
1342 if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1))
1344 if(fmt->Format.nChannels < This->ai.max_channels)
1345 fmt->Format.nChannels += 1;
1346 else
1347 /* We could "fake" more channels and downmix the emulated channels,
1348 * but at that point you really ought to tweak your OSS setup or
1349 * just use PulseAudio. */
1350 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
1353 if(This->ai.max_rate == 0)
1354 fmt->Format.nSamplesPerSec = 44100;
1355 else
1356 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1357 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1358 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1360 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1362 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1363 fmt->Format.nChannels) / 8;
1364 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1365 fmt->Format.nBlockAlign;
1367 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1368 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1370 *pwfx = (WAVEFORMATEX*)fmt;
1371 dump_fmt(*pwfx);
1373 return S_OK;
1376 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1377 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1379 ACImpl *This = impl_from_IAudioClient(iface);
1381 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1383 if(!defperiod && !minperiod)
1384 return E_POINTER;
1386 if(defperiod)
1387 *defperiod = DefaultPeriod;
1388 if(minperiod)
1389 *minperiod = MinimumPeriod;
1391 return S_OK;
1394 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1396 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1397 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1398 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1399 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1400 This->fmt->wBitsPerSample == 8)
1401 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1402 else
1403 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1406 static void oss_write_data(ACImpl *This)
1408 ssize_t written_bytes;
1409 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1410 SIZE_T to_write_frames, to_write_bytes, advanced;
1411 audio_buf_info bi;
1412 BYTE *buf;
1414 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1415 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1416 return;
1419 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1421 if(bi.bytes > This->oss_bufsize_bytes){
1422 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1423 bi.bytes, This->oss_bufsize_bytes);
1424 This->oss_bufsize_bytes = bi.bytes;
1425 in_oss_frames = 0;
1426 }else
1427 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1429 if(in_oss_frames > This->in_oss_frames){
1430 TRACE("Capping reported frames from %u to %u\n",
1431 in_oss_frames, This->in_oss_frames);
1432 in_oss_frames = This->in_oss_frames;
1435 write_limit = 0;
1436 while(write_limit + in_oss_frames < max_period * 3)
1437 write_limit += max_period;
1438 if(write_limit == 0)
1439 return;
1441 /* vvvvvv - in_oss_frames
1442 * [--xxxxxxxxxx]
1443 * [xxxxxxxxxx--]
1444 * ^^^^^^^^^^ - held_frames
1445 * ^ - lcl_offs_frames
1447 advanced = This->in_oss_frames - in_oss_frames;
1448 if(advanced > This->held_frames)
1449 advanced = This->held_frames;
1450 This->lcl_offs_frames += advanced;
1451 This->lcl_offs_frames %= This->bufsize_frames;
1452 This->held_frames -= advanced;
1453 This->in_oss_frames = in_oss_frames;
1454 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
1455 advanced, This->lcl_offs_frames, This->held_frames, This->in_oss_frames);
1458 if(This->held_frames == This->in_oss_frames)
1459 return;
1461 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1462 new_frames = This->held_frames - This->in_oss_frames;
1464 if(write_offs_frames + new_frames > This->bufsize_frames)
1465 to_write_frames = This->bufsize_frames - write_offs_frames;
1466 else
1467 to_write_frames = new_frames;
1469 to_write_frames = min(to_write_frames, write_limit);
1470 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1471 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
1472 write_offs_frames, to_write_frames + write_offs_frames,
1473 This->bufsize_frames);
1475 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1477 if(This->session->mute)
1478 silence_buffer(This, buf, to_write_frames);
1480 written_bytes = write(This->fd, buf, to_write_bytes);
1481 if(written_bytes < 0){
1482 /* EAGAIN is OSS buffer full, log that too */
1483 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1484 return;
1486 written_frames = written_bytes / This->fmt->nBlockAlign;
1488 This->in_oss_frames += written_frames;
1490 if(written_frames < to_write_frames){
1491 /* OSS buffer probably full */
1492 return;
1495 if(new_frames > written_frames && written_frames < write_limit){
1496 /* wrapped and have some data back at the start to write */
1498 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1499 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1501 if(This->session->mute)
1502 silence_buffer(This, This->local_buffer, to_write_frames);
1504 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
1506 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1507 if(written_bytes < 0){
1508 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1509 return;
1511 written_frames = written_bytes / This->fmt->nBlockAlign;
1512 This->in_oss_frames += written_frames;
1516 static void oss_read_data(ACImpl *This)
1518 UINT64 pos, readable;
1519 ssize_t nread;
1521 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1522 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1524 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1525 readable);
1526 if(nread < 0){
1527 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1528 return;
1531 This->held_frames += nread / This->fmt->nBlockAlign;
1533 if(This->held_frames > This->bufsize_frames){
1534 WARN("Overflow of unread data\n");
1535 This->lcl_offs_frames += This->held_frames;
1536 This->lcl_offs_frames %= This->bufsize_frames;
1537 This->held_frames = This->bufsize_frames;
1541 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1543 ACImpl *This = user;
1545 EnterCriticalSection(&This->lock);
1547 if(This->playing){
1548 if(This->dataflow == eRender && This->held_frames)
1549 oss_write_data(This);
1550 else if(This->dataflow == eCapture)
1551 oss_read_data(This);
1554 LeaveCriticalSection(&This->lock);
1556 if(This->event)
1557 SetEvent(This->event);
1560 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1562 ACImpl *This = impl_from_IAudioClient(iface);
1564 TRACE("(%p)\n", This);
1566 EnterCriticalSection(&This->lock);
1568 if(!This->initted){
1569 LeaveCriticalSection(&This->lock);
1570 return AUDCLNT_E_NOT_INITIALIZED;
1573 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1574 LeaveCriticalSection(&This->lock);
1575 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1578 if(This->playing){
1579 LeaveCriticalSection(&This->lock);
1580 return AUDCLNT_E_NOT_STOPPED;
1583 if(!This->timer){
1584 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1585 oss_period_callback, This, 0, This->period_us / 1000,
1586 WT_EXECUTEINTIMERTHREAD))
1587 ERR("Unable to create period timer: %u\n", GetLastError());
1590 This->playing = TRUE;
1592 LeaveCriticalSection(&This->lock);
1594 return S_OK;
1597 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1599 ACImpl *This = impl_from_IAudioClient(iface);
1601 TRACE("(%p)\n", This);
1603 EnterCriticalSection(&This->lock);
1605 if(!This->initted){
1606 LeaveCriticalSection(&This->lock);
1607 return AUDCLNT_E_NOT_INITIALIZED;
1610 if(!This->playing){
1611 LeaveCriticalSection(&This->lock);
1612 return S_FALSE;
1615 This->playing = FALSE;
1616 This->in_oss_frames = 0;
1618 LeaveCriticalSection(&This->lock);
1620 return S_OK;
1623 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1625 ACImpl *This = impl_from_IAudioClient(iface);
1627 TRACE("(%p)\n", This);
1629 EnterCriticalSection(&This->lock);
1631 if(!This->initted){
1632 LeaveCriticalSection(&This->lock);
1633 return AUDCLNT_E_NOT_INITIALIZED;
1636 if(This->playing){
1637 LeaveCriticalSection(&This->lock);
1638 return AUDCLNT_E_NOT_STOPPED;
1641 if(This->getbuf_last){
1642 LeaveCriticalSection(&This->lock);
1643 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1646 if(This->dataflow == eRender){
1647 This->written_frames = 0;
1648 This->last_pos_frames = 0;
1649 }else{
1650 This->written_frames += This->held_frames;
1652 This->held_frames = 0;
1653 This->lcl_offs_frames = 0;
1654 This->in_oss_frames = 0;
1656 LeaveCriticalSection(&This->lock);
1658 return S_OK;
1661 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1662 HANDLE event)
1664 ACImpl *This = impl_from_IAudioClient(iface);
1666 TRACE("(%p)->(%p)\n", This, event);
1668 if(!event)
1669 return E_INVALIDARG;
1671 EnterCriticalSection(&This->lock);
1673 if(!This->initted){
1674 LeaveCriticalSection(&This->lock);
1675 return AUDCLNT_E_NOT_INITIALIZED;
1678 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1679 LeaveCriticalSection(&This->lock);
1680 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1683 if (This->event){
1684 LeaveCriticalSection(&This->lock);
1685 FIXME("called twice\n");
1686 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1689 This->event = event;
1691 LeaveCriticalSection(&This->lock);
1693 return S_OK;
1696 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1697 void **ppv)
1699 ACImpl *This = impl_from_IAudioClient(iface);
1701 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1703 if(!ppv)
1704 return E_POINTER;
1705 *ppv = NULL;
1707 EnterCriticalSection(&This->lock);
1709 if(!This->initted){
1710 LeaveCriticalSection(&This->lock);
1711 return AUDCLNT_E_NOT_INITIALIZED;
1714 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1715 if(This->dataflow != eRender){
1716 LeaveCriticalSection(&This->lock);
1717 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1719 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1720 *ppv = &This->IAudioRenderClient_iface;
1721 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1722 if(This->dataflow != eCapture){
1723 LeaveCriticalSection(&This->lock);
1724 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1726 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1727 *ppv = &This->IAudioCaptureClient_iface;
1728 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1729 IAudioClock_AddRef(&This->IAudioClock_iface);
1730 *ppv = &This->IAudioClock_iface;
1731 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1732 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1733 *ppv = &This->IAudioStreamVolume_iface;
1734 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1735 if(!This->session_wrapper){
1736 This->session_wrapper = AudioSessionWrapper_Create(This);
1737 if(!This->session_wrapper){
1738 LeaveCriticalSection(&This->lock);
1739 return E_OUTOFMEMORY;
1741 }else
1742 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1744 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1745 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1746 if(!This->session_wrapper){
1747 This->session_wrapper = AudioSessionWrapper_Create(This);
1748 if(!This->session_wrapper){
1749 LeaveCriticalSection(&This->lock);
1750 return E_OUTOFMEMORY;
1752 }else
1753 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1755 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1756 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1757 if(!This->session_wrapper){
1758 This->session_wrapper = AudioSessionWrapper_Create(This);
1759 if(!This->session_wrapper){
1760 LeaveCriticalSection(&This->lock);
1761 return E_OUTOFMEMORY;
1763 }else
1764 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1766 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1769 if(*ppv){
1770 LeaveCriticalSection(&This->lock);
1771 return S_OK;
1774 LeaveCriticalSection(&This->lock);
1776 FIXME("stub %s\n", debugstr_guid(riid));
1777 return E_NOINTERFACE;
1780 static const IAudioClientVtbl AudioClient_Vtbl =
1782 AudioClient_QueryInterface,
1783 AudioClient_AddRef,
1784 AudioClient_Release,
1785 AudioClient_Initialize,
1786 AudioClient_GetBufferSize,
1787 AudioClient_GetStreamLatency,
1788 AudioClient_GetCurrentPadding,
1789 AudioClient_IsFormatSupported,
1790 AudioClient_GetMixFormat,
1791 AudioClient_GetDevicePeriod,
1792 AudioClient_Start,
1793 AudioClient_Stop,
1794 AudioClient_Reset,
1795 AudioClient_SetEventHandle,
1796 AudioClient_GetService
1799 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1800 IAudioRenderClient *iface, REFIID riid, void **ppv)
1802 ACImpl *This = impl_from_IAudioRenderClient(iface);
1803 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1805 if(!ppv)
1806 return E_POINTER;
1807 *ppv = NULL;
1809 if(IsEqualIID(riid, &IID_IUnknown) ||
1810 IsEqualIID(riid, &IID_IAudioRenderClient))
1811 *ppv = iface;
1812 else if(IsEqualIID(riid, &IID_IMarshal))
1813 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1814 if(*ppv){
1815 IUnknown_AddRef((IUnknown*)*ppv);
1816 return S_OK;
1819 WARN("Unknown interface %s\n", debugstr_guid(riid));
1820 return E_NOINTERFACE;
1823 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1825 ACImpl *This = impl_from_IAudioRenderClient(iface);
1826 return AudioClient_AddRef(&This->IAudioClient_iface);
1829 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1831 ACImpl *This = impl_from_IAudioRenderClient(iface);
1832 return AudioClient_Release(&This->IAudioClient_iface);
1835 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1836 UINT32 frames, BYTE **data)
1838 ACImpl *This = impl_from_IAudioRenderClient(iface);
1839 UINT32 write_pos;
1841 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1843 if(!data)
1844 return E_POINTER;
1846 *data = NULL;
1848 EnterCriticalSection(&This->lock);
1850 if(This->getbuf_last){
1851 LeaveCriticalSection(&This->lock);
1852 return AUDCLNT_E_OUT_OF_ORDER;
1855 if(!frames){
1856 LeaveCriticalSection(&This->lock);
1857 return S_OK;
1860 if(This->held_frames + frames > This->bufsize_frames){
1861 LeaveCriticalSection(&This->lock);
1862 return AUDCLNT_E_BUFFER_TOO_LARGE;
1865 write_pos =
1866 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1867 if(write_pos + frames > This->bufsize_frames){
1868 if(This->tmp_buffer_frames < frames){
1869 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1870 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1871 frames * This->fmt->nBlockAlign);
1872 if(!This->tmp_buffer){
1873 LeaveCriticalSection(&This->lock);
1874 return E_OUTOFMEMORY;
1876 This->tmp_buffer_frames = frames;
1878 *data = This->tmp_buffer;
1879 This->getbuf_last = -frames;
1880 }else{
1881 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1882 This->getbuf_last = frames;
1885 silence_buffer(This, *data, frames);
1887 LeaveCriticalSection(&This->lock);
1889 return S_OK;
1892 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1894 UINT32 write_offs_frames =
1895 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1896 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1897 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1898 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1899 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1901 if(written_bytes <= chunk_bytes){
1902 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1903 }else{
1904 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1905 memcpy(This->local_buffer, buffer + chunk_bytes,
1906 written_bytes - chunk_bytes);
1910 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1911 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1913 ACImpl *This = impl_from_IAudioRenderClient(iface);
1914 BYTE *buffer;
1916 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1918 EnterCriticalSection(&This->lock);
1920 if(!written_frames){
1921 This->getbuf_last = 0;
1922 LeaveCriticalSection(&This->lock);
1923 return S_OK;
1926 if(!This->getbuf_last){
1927 LeaveCriticalSection(&This->lock);
1928 return AUDCLNT_E_OUT_OF_ORDER;
1931 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1932 LeaveCriticalSection(&This->lock);
1933 return AUDCLNT_E_INVALID_SIZE;
1936 if(This->getbuf_last >= 0)
1937 buffer = This->local_buffer + This->fmt->nBlockAlign *
1938 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1939 else
1940 buffer = This->tmp_buffer;
1942 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1943 silence_buffer(This, buffer, written_frames);
1945 if(This->getbuf_last < 0)
1946 oss_wrap_buffer(This, buffer, written_frames);
1948 This->held_frames += written_frames;
1949 This->written_frames += written_frames;
1950 This->getbuf_last = 0;
1952 LeaveCriticalSection(&This->lock);
1954 return S_OK;
1957 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1958 AudioRenderClient_QueryInterface,
1959 AudioRenderClient_AddRef,
1960 AudioRenderClient_Release,
1961 AudioRenderClient_GetBuffer,
1962 AudioRenderClient_ReleaseBuffer
1965 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1966 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1968 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1969 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1971 if(!ppv)
1972 return E_POINTER;
1973 *ppv = NULL;
1975 if(IsEqualIID(riid, &IID_IUnknown) ||
1976 IsEqualIID(riid, &IID_IAudioCaptureClient))
1977 *ppv = iface;
1978 else if(IsEqualIID(riid, &IID_IMarshal))
1979 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1980 if(*ppv){
1981 IUnknown_AddRef((IUnknown*)*ppv);
1982 return S_OK;
1985 WARN("Unknown interface %s\n", debugstr_guid(riid));
1986 return E_NOINTERFACE;
1989 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1991 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1992 return IAudioClient_AddRef(&This->IAudioClient_iface);
1995 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1997 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1998 return IAudioClient_Release(&This->IAudioClient_iface);
2001 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2002 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2003 UINT64 *qpcpos)
2005 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2007 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2008 devpos, qpcpos);
2010 if(!data)
2011 return E_POINTER;
2013 *data = NULL;
2015 if(!frames || !flags)
2016 return E_POINTER;
2018 EnterCriticalSection(&This->lock);
2020 if(This->getbuf_last){
2021 LeaveCriticalSection(&This->lock);
2022 return AUDCLNT_E_OUT_OF_ORDER;
2025 if(This->held_frames < This->period_frames){
2026 *frames = 0;
2027 LeaveCriticalSection(&This->lock);
2028 return AUDCLNT_S_BUFFER_EMPTY;
2031 *flags = 0;
2033 *frames = This->period_frames;
2035 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2036 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2037 if(This->tmp_buffer_frames < *frames){
2038 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2039 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2040 *frames * This->fmt->nBlockAlign);
2041 if(!This->tmp_buffer){
2042 LeaveCriticalSection(&This->lock);
2043 return E_OUTOFMEMORY;
2045 This->tmp_buffer_frames = *frames;
2048 *data = This->tmp_buffer;
2049 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2050 This->fmt->nBlockAlign;
2051 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2052 frames_bytes = *frames * This->fmt->nBlockAlign;
2053 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2054 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2055 frames_bytes - chunk_bytes);
2056 }else
2057 *data = This->local_buffer +
2058 This->lcl_offs_frames * This->fmt->nBlockAlign;
2060 This->getbuf_last = *frames;
2062 if(devpos)
2063 *devpos = This->written_frames;
2064 if(qpcpos){
2065 LARGE_INTEGER stamp, freq;
2066 QueryPerformanceCounter(&stamp);
2067 QueryPerformanceFrequency(&freq);
2068 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2071 LeaveCriticalSection(&This->lock);
2073 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2076 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2077 IAudioCaptureClient *iface, UINT32 done)
2079 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2081 TRACE("(%p)->(%u)\n", This, done);
2083 EnterCriticalSection(&This->lock);
2085 if(!done){
2086 This->getbuf_last = 0;
2087 LeaveCriticalSection(&This->lock);
2088 return S_OK;
2091 if(!This->getbuf_last){
2092 LeaveCriticalSection(&This->lock);
2093 return AUDCLNT_E_OUT_OF_ORDER;
2096 if(This->getbuf_last != done){
2097 LeaveCriticalSection(&This->lock);
2098 return AUDCLNT_E_INVALID_SIZE;
2101 This->written_frames += done;
2102 This->held_frames -= done;
2103 This->lcl_offs_frames += done;
2104 This->lcl_offs_frames %= This->bufsize_frames;
2105 This->getbuf_last = 0;
2107 LeaveCriticalSection(&This->lock);
2109 return S_OK;
2112 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2113 IAudioCaptureClient *iface, UINT32 *frames)
2115 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2117 TRACE("(%p)->(%p)\n", This, frames);
2119 if(!frames)
2120 return E_POINTER;
2122 EnterCriticalSection(&This->lock);
2124 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2126 LeaveCriticalSection(&This->lock);
2128 return S_OK;
2131 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2133 AudioCaptureClient_QueryInterface,
2134 AudioCaptureClient_AddRef,
2135 AudioCaptureClient_Release,
2136 AudioCaptureClient_GetBuffer,
2137 AudioCaptureClient_ReleaseBuffer,
2138 AudioCaptureClient_GetNextPacketSize
2141 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2142 REFIID riid, void **ppv)
2144 ACImpl *This = impl_from_IAudioClock(iface);
2146 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2148 if(!ppv)
2149 return E_POINTER;
2150 *ppv = NULL;
2152 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2153 *ppv = iface;
2154 else if(IsEqualIID(riid, &IID_IAudioClock2))
2155 *ppv = &This->IAudioClock2_iface;
2156 if(*ppv){
2157 IUnknown_AddRef((IUnknown*)*ppv);
2158 return S_OK;
2161 WARN("Unknown interface %s\n", debugstr_guid(riid));
2162 return E_NOINTERFACE;
2165 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2167 ACImpl *This = impl_from_IAudioClock(iface);
2168 return IAudioClient_AddRef(&This->IAudioClient_iface);
2171 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2173 ACImpl *This = impl_from_IAudioClock(iface);
2174 return IAudioClient_Release(&This->IAudioClient_iface);
2177 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2179 ACImpl *This = impl_from_IAudioClock(iface);
2181 TRACE("(%p)->(%p)\n", This, freq);
2183 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2184 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2185 else
2186 *freq = This->fmt->nSamplesPerSec;
2188 return S_OK;
2191 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2192 UINT64 *qpctime)
2194 ACImpl *This = impl_from_IAudioClock(iface);
2196 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2198 if(!pos)
2199 return E_POINTER;
2201 EnterCriticalSection(&This->lock);
2203 if(This->dataflow == eRender){
2204 *pos = This->written_frames - This->held_frames;
2205 if(*pos < This->last_pos_frames)
2206 *pos = This->last_pos_frames;
2207 }else if(This->dataflow == eCapture){
2208 audio_buf_info bi;
2209 UINT32 held;
2211 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2212 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2213 held = 0;
2214 }else{
2215 if(bi.bytes <= bi.fragsize)
2216 held = 0;
2217 else
2218 held = bi.bytes / This->fmt->nBlockAlign;
2221 *pos = This->written_frames + held;
2224 This->last_pos_frames = *pos;
2226 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2227 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2228 *pos *= This->fmt->nBlockAlign;
2230 LeaveCriticalSection(&This->lock);
2232 if(qpctime){
2233 LARGE_INTEGER stamp, freq;
2234 QueryPerformanceCounter(&stamp);
2235 QueryPerformanceFrequency(&freq);
2236 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2239 return S_OK;
2242 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2243 DWORD *chars)
2245 ACImpl *This = impl_from_IAudioClock(iface);
2247 TRACE("(%p)->(%p)\n", This, chars);
2249 if(!chars)
2250 return E_POINTER;
2252 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2254 return S_OK;
2257 static const IAudioClockVtbl AudioClock_Vtbl =
2259 AudioClock_QueryInterface,
2260 AudioClock_AddRef,
2261 AudioClock_Release,
2262 AudioClock_GetFrequency,
2263 AudioClock_GetPosition,
2264 AudioClock_GetCharacteristics
2267 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2268 REFIID riid, void **ppv)
2270 ACImpl *This = impl_from_IAudioClock2(iface);
2271 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2274 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2276 ACImpl *This = impl_from_IAudioClock2(iface);
2277 return IAudioClient_AddRef(&This->IAudioClient_iface);
2280 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2282 ACImpl *This = impl_from_IAudioClock2(iface);
2283 return IAudioClient_Release(&This->IAudioClient_iface);
2286 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2287 UINT64 *pos, UINT64 *qpctime)
2289 ACImpl *This = impl_from_IAudioClock2(iface);
2291 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2293 return E_NOTIMPL;
2296 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2298 AudioClock2_QueryInterface,
2299 AudioClock2_AddRef,
2300 AudioClock2_Release,
2301 AudioClock2_GetDevicePosition
2304 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2306 AudioSessionWrapper *ret;
2308 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2309 sizeof(AudioSessionWrapper));
2310 if(!ret)
2311 return NULL;
2313 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2314 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2315 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2317 ret->ref = 1;
2319 ret->client = client;
2320 if(client){
2321 ret->session = client->session;
2322 AudioClient_AddRef(&client->IAudioClient_iface);
2325 return ret;
2328 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2329 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2331 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2333 if(!ppv)
2334 return E_POINTER;
2335 *ppv = NULL;
2337 if(IsEqualIID(riid, &IID_IUnknown) ||
2338 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2339 IsEqualIID(riid, &IID_IAudioSessionControl2))
2340 *ppv = iface;
2341 if(*ppv){
2342 IUnknown_AddRef((IUnknown*)*ppv);
2343 return S_OK;
2346 WARN("Unknown interface %s\n", debugstr_guid(riid));
2347 return E_NOINTERFACE;
2350 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2352 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2353 ULONG ref;
2354 ref = InterlockedIncrement(&This->ref);
2355 TRACE("(%p) Refcount now %u\n", This, ref);
2356 return ref;
2359 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2361 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2362 ULONG ref;
2363 ref = InterlockedDecrement(&This->ref);
2364 TRACE("(%p) Refcount now %u\n", This, ref);
2365 if(!ref){
2366 if(This->client){
2367 EnterCriticalSection(&This->client->lock);
2368 This->client->session_wrapper = NULL;
2369 LeaveCriticalSection(&This->client->lock);
2370 AudioClient_Release(&This->client->IAudioClient_iface);
2372 HeapFree(GetProcessHeap(), 0, This);
2374 return ref;
2377 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2378 AudioSessionState *state)
2380 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2381 ACImpl *client;
2383 TRACE("(%p)->(%p)\n", This, state);
2385 if(!state)
2386 return NULL_PTR_ERR;
2388 EnterCriticalSection(&g_sessions_lock);
2390 if(list_empty(&This->session->clients)){
2391 *state = AudioSessionStateExpired;
2392 LeaveCriticalSection(&g_sessions_lock);
2393 return S_OK;
2396 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2397 EnterCriticalSection(&client->lock);
2398 if(client->playing){
2399 *state = AudioSessionStateActive;
2400 LeaveCriticalSection(&client->lock);
2401 LeaveCriticalSection(&g_sessions_lock);
2402 return S_OK;
2404 LeaveCriticalSection(&client->lock);
2407 LeaveCriticalSection(&g_sessions_lock);
2409 *state = AudioSessionStateInactive;
2411 return S_OK;
2414 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2415 IAudioSessionControl2 *iface, WCHAR **name)
2417 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2419 FIXME("(%p)->(%p) - stub\n", This, name);
2421 return E_NOTIMPL;
2424 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2425 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2427 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2429 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2431 return E_NOTIMPL;
2434 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2435 IAudioSessionControl2 *iface, WCHAR **path)
2437 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2439 FIXME("(%p)->(%p) - stub\n", This, path);
2441 return E_NOTIMPL;
2444 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2445 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2447 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2449 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2451 return E_NOTIMPL;
2454 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2455 IAudioSessionControl2 *iface, GUID *group)
2457 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2459 FIXME("(%p)->(%p) - stub\n", This, group);
2461 return E_NOTIMPL;
2464 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2465 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2467 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2469 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2470 debugstr_guid(session));
2472 return E_NOTIMPL;
2475 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2476 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2478 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2480 FIXME("(%p)->(%p) - stub\n", This, events);
2482 return S_OK;
2485 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2486 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2488 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2490 FIXME("(%p)->(%p) - stub\n", This, events);
2492 return S_OK;
2495 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2496 IAudioSessionControl2 *iface, WCHAR **id)
2498 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2500 FIXME("(%p)->(%p) - stub\n", This, id);
2502 return E_NOTIMPL;
2505 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2506 IAudioSessionControl2 *iface, WCHAR **id)
2508 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2510 FIXME("(%p)->(%p) - stub\n", This, id);
2512 return E_NOTIMPL;
2515 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2516 IAudioSessionControl2 *iface, DWORD *pid)
2518 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2520 TRACE("(%p)->(%p)\n", This, pid);
2522 if(!pid)
2523 return E_POINTER;
2525 *pid = GetCurrentProcessId();
2527 return S_OK;
2530 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2531 IAudioSessionControl2 *iface)
2533 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2535 TRACE("(%p)\n", This);
2537 return S_FALSE;
2540 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2541 IAudioSessionControl2 *iface, BOOL optout)
2543 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2545 TRACE("(%p)->(%d)\n", This, optout);
2547 return S_OK;
2550 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2552 AudioSessionControl_QueryInterface,
2553 AudioSessionControl_AddRef,
2554 AudioSessionControl_Release,
2555 AudioSessionControl_GetState,
2556 AudioSessionControl_GetDisplayName,
2557 AudioSessionControl_SetDisplayName,
2558 AudioSessionControl_GetIconPath,
2559 AudioSessionControl_SetIconPath,
2560 AudioSessionControl_GetGroupingParam,
2561 AudioSessionControl_SetGroupingParam,
2562 AudioSessionControl_RegisterAudioSessionNotification,
2563 AudioSessionControl_UnregisterAudioSessionNotification,
2564 AudioSessionControl_GetSessionIdentifier,
2565 AudioSessionControl_GetSessionInstanceIdentifier,
2566 AudioSessionControl_GetProcessId,
2567 AudioSessionControl_IsSystemSoundsSession,
2568 AudioSessionControl_SetDuckingPreference
2571 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2572 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2574 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2576 if(!ppv)
2577 return E_POINTER;
2578 *ppv = NULL;
2580 if(IsEqualIID(riid, &IID_IUnknown) ||
2581 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2582 *ppv = iface;
2583 if(*ppv){
2584 IUnknown_AddRef((IUnknown*)*ppv);
2585 return S_OK;
2588 WARN("Unknown interface %s\n", debugstr_guid(riid));
2589 return E_NOINTERFACE;
2592 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2594 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2595 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2598 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2600 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2601 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2604 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2605 ISimpleAudioVolume *iface, float level, const GUID *context)
2607 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2608 AudioSession *session = This->session;
2610 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2612 if(level < 0.f || level > 1.f)
2613 return E_INVALIDARG;
2615 if(context)
2616 FIXME("Notifications not supported yet\n");
2618 EnterCriticalSection(&session->lock);
2620 session->master_vol = level;
2622 TRACE("OSS doesn't support setting volume\n");
2624 LeaveCriticalSection(&session->lock);
2626 return S_OK;
2629 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2630 ISimpleAudioVolume *iface, float *level)
2632 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2633 AudioSession *session = This->session;
2635 TRACE("(%p)->(%p)\n", session, level);
2637 if(!level)
2638 return NULL_PTR_ERR;
2640 *level = session->master_vol;
2642 return S_OK;
2645 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2646 BOOL mute, const GUID *context)
2648 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2649 AudioSession *session = This->session;
2651 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2653 EnterCriticalSection(&session->lock);
2655 session->mute = mute;
2657 LeaveCriticalSection(&session->lock);
2659 return S_OK;
2662 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2663 BOOL *mute)
2665 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2666 AudioSession *session = This->session;
2668 TRACE("(%p)->(%p)\n", session, mute);
2670 if(!mute)
2671 return NULL_PTR_ERR;
2673 *mute = This->session->mute;
2675 return S_OK;
2678 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2680 SimpleAudioVolume_QueryInterface,
2681 SimpleAudioVolume_AddRef,
2682 SimpleAudioVolume_Release,
2683 SimpleAudioVolume_SetMasterVolume,
2684 SimpleAudioVolume_GetMasterVolume,
2685 SimpleAudioVolume_SetMute,
2686 SimpleAudioVolume_GetMute
2689 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2690 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2692 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2694 if(!ppv)
2695 return E_POINTER;
2696 *ppv = NULL;
2698 if(IsEqualIID(riid, &IID_IUnknown) ||
2699 IsEqualIID(riid, &IID_IAudioStreamVolume))
2700 *ppv = iface;
2701 if(*ppv){
2702 IUnknown_AddRef((IUnknown*)*ppv);
2703 return S_OK;
2706 WARN("Unknown interface %s\n", debugstr_guid(riid));
2707 return E_NOINTERFACE;
2710 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2712 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2713 return IAudioClient_AddRef(&This->IAudioClient_iface);
2716 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2718 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2719 return IAudioClient_Release(&This->IAudioClient_iface);
2722 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2723 IAudioStreamVolume *iface, UINT32 *out)
2725 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2727 TRACE("(%p)->(%p)\n", This, out);
2729 if(!out)
2730 return E_POINTER;
2732 *out = This->fmt->nChannels;
2734 return S_OK;
2737 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2738 IAudioStreamVolume *iface, UINT32 index, float level)
2740 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2742 TRACE("(%p)->(%d, %f)\n", This, index, level);
2744 if(level < 0.f || level > 1.f)
2745 return E_INVALIDARG;
2747 if(index >= This->fmt->nChannels)
2748 return E_INVALIDARG;
2750 EnterCriticalSection(&This->lock);
2752 This->vols[index] = level;
2754 TRACE("OSS doesn't support setting volume\n");
2756 LeaveCriticalSection(&This->lock);
2758 return S_OK;
2761 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2762 IAudioStreamVolume *iface, UINT32 index, float *level)
2764 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2766 TRACE("(%p)->(%d, %p)\n", This, index, level);
2768 if(!level)
2769 return E_POINTER;
2771 if(index >= This->fmt->nChannels)
2772 return E_INVALIDARG;
2774 *level = This->vols[index];
2776 return S_OK;
2779 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2780 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2782 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2783 int i;
2785 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2787 if(!levels)
2788 return E_POINTER;
2790 if(count != This->fmt->nChannels)
2791 return E_INVALIDARG;
2793 EnterCriticalSection(&This->lock);
2795 for(i = 0; i < count; ++i)
2796 This->vols[i] = levels[i];
2798 TRACE("OSS doesn't support setting volume\n");
2800 LeaveCriticalSection(&This->lock);
2802 return S_OK;
2805 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2806 IAudioStreamVolume *iface, UINT32 count, float *levels)
2808 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2809 int i;
2811 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2813 if(!levels)
2814 return E_POINTER;
2816 if(count != This->fmt->nChannels)
2817 return E_INVALIDARG;
2819 EnterCriticalSection(&This->lock);
2821 for(i = 0; i < count; ++i)
2822 levels[i] = This->vols[i];
2824 LeaveCriticalSection(&This->lock);
2826 return S_OK;
2829 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2831 AudioStreamVolume_QueryInterface,
2832 AudioStreamVolume_AddRef,
2833 AudioStreamVolume_Release,
2834 AudioStreamVolume_GetChannelCount,
2835 AudioStreamVolume_SetChannelVolume,
2836 AudioStreamVolume_GetChannelVolume,
2837 AudioStreamVolume_SetAllVolumes,
2838 AudioStreamVolume_GetAllVolumes
2841 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2842 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2844 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2846 if(!ppv)
2847 return E_POINTER;
2848 *ppv = NULL;
2850 if(IsEqualIID(riid, &IID_IUnknown) ||
2851 IsEqualIID(riid, &IID_IChannelAudioVolume))
2852 *ppv = iface;
2853 if(*ppv){
2854 IUnknown_AddRef((IUnknown*)*ppv);
2855 return S_OK;
2858 WARN("Unknown interface %s\n", debugstr_guid(riid));
2859 return E_NOINTERFACE;
2862 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2864 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2865 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2868 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2870 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2871 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2874 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2875 IChannelAudioVolume *iface, UINT32 *out)
2877 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2878 AudioSession *session = This->session;
2880 TRACE("(%p)->(%p)\n", session, out);
2882 if(!out)
2883 return NULL_PTR_ERR;
2885 *out = session->channel_count;
2887 return S_OK;
2890 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2891 IChannelAudioVolume *iface, UINT32 index, float level,
2892 const GUID *context)
2894 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2895 AudioSession *session = This->session;
2897 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2898 wine_dbgstr_guid(context));
2900 if(level < 0.f || level > 1.f)
2901 return E_INVALIDARG;
2903 if(index >= session->channel_count)
2904 return E_INVALIDARG;
2906 if(context)
2907 FIXME("Notifications not supported yet\n");
2909 EnterCriticalSection(&session->lock);
2911 session->channel_vols[index] = level;
2913 TRACE("OSS doesn't support setting volume\n");
2915 LeaveCriticalSection(&session->lock);
2917 return S_OK;
2920 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2921 IChannelAudioVolume *iface, UINT32 index, float *level)
2923 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2924 AudioSession *session = This->session;
2926 TRACE("(%p)->(%d, %p)\n", session, index, level);
2928 if(!level)
2929 return NULL_PTR_ERR;
2931 if(index >= session->channel_count)
2932 return E_INVALIDARG;
2934 *level = session->channel_vols[index];
2936 return S_OK;
2939 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2940 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2941 const GUID *context)
2943 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2944 AudioSession *session = This->session;
2945 int i;
2947 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2948 wine_dbgstr_guid(context));
2950 if(!levels)
2951 return NULL_PTR_ERR;
2953 if(count != session->channel_count)
2954 return E_INVALIDARG;
2956 if(context)
2957 FIXME("Notifications not supported yet\n");
2959 EnterCriticalSection(&session->lock);
2961 for(i = 0; i < count; ++i)
2962 session->channel_vols[i] = levels[i];
2964 TRACE("OSS doesn't support setting volume\n");
2966 LeaveCriticalSection(&session->lock);
2968 return S_OK;
2971 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2972 IChannelAudioVolume *iface, UINT32 count, float *levels)
2974 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2975 AudioSession *session = This->session;
2976 int i;
2978 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2980 if(!levels)
2981 return NULL_PTR_ERR;
2983 if(count != session->channel_count)
2984 return E_INVALIDARG;
2986 for(i = 0; i < count; ++i)
2987 levels[i] = session->channel_vols[i];
2989 return S_OK;
2992 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2994 ChannelAudioVolume_QueryInterface,
2995 ChannelAudioVolume_AddRef,
2996 ChannelAudioVolume_Release,
2997 ChannelAudioVolume_GetChannelCount,
2998 ChannelAudioVolume_SetChannelVolume,
2999 ChannelAudioVolume_GetChannelVolume,
3000 ChannelAudioVolume_SetAllVolumes,
3001 ChannelAudioVolume_GetAllVolumes
3004 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3005 REFIID riid, void **ppv)
3007 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3009 if(!ppv)
3010 return E_POINTER;
3011 *ppv = NULL;
3013 if(IsEqualIID(riid, &IID_IUnknown) ||
3014 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3015 IsEqualIID(riid, &IID_IAudioSessionManager2))
3016 *ppv = iface;
3017 if(*ppv){
3018 IUnknown_AddRef((IUnknown*)*ppv);
3019 return S_OK;
3022 WARN("Unknown interface %s\n", debugstr_guid(riid));
3023 return E_NOINTERFACE;
3026 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3028 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3029 ULONG ref;
3030 ref = InterlockedIncrement(&This->ref);
3031 TRACE("(%p) Refcount now %u\n", This, ref);
3032 return ref;
3035 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3037 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3038 ULONG ref;
3039 ref = InterlockedDecrement(&This->ref);
3040 TRACE("(%p) Refcount now %u\n", This, ref);
3041 if(!ref)
3042 HeapFree(GetProcessHeap(), 0, This);
3043 return ref;
3046 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3047 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3048 IAudioSessionControl **out)
3050 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3051 AudioSession *session;
3052 AudioSessionWrapper *wrapper;
3053 HRESULT hr;
3055 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3056 flags, out);
3058 hr = get_audio_session(session_guid, This->device, 0, &session);
3059 if(FAILED(hr))
3060 return hr;
3062 wrapper = AudioSessionWrapper_Create(NULL);
3063 if(!wrapper)
3064 return E_OUTOFMEMORY;
3066 wrapper->session = session;
3068 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3070 return S_OK;
3073 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3074 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3075 ISimpleAudioVolume **out)
3077 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3078 AudioSession *session;
3079 AudioSessionWrapper *wrapper;
3080 HRESULT hr;
3082 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3083 flags, out);
3085 hr = get_audio_session(session_guid, This->device, 0, &session);
3086 if(FAILED(hr))
3087 return hr;
3089 wrapper = AudioSessionWrapper_Create(NULL);
3090 if(!wrapper)
3091 return E_OUTOFMEMORY;
3093 wrapper->session = session;
3095 *out = &wrapper->ISimpleAudioVolume_iface;
3097 return S_OK;
3100 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3101 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3103 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3104 FIXME("(%p)->(%p) - stub\n", This, out);
3105 return E_NOTIMPL;
3108 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3109 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3111 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3112 FIXME("(%p)->(%p) - stub\n", This, notification);
3113 return E_NOTIMPL;
3116 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3117 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3119 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3120 FIXME("(%p)->(%p) - stub\n", This, notification);
3121 return E_NOTIMPL;
3124 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3125 IAudioSessionManager2 *iface, const WCHAR *session_id,
3126 IAudioVolumeDuckNotification *notification)
3128 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3129 FIXME("(%p)->(%p) - stub\n", This, notification);
3130 return E_NOTIMPL;
3133 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3134 IAudioSessionManager2 *iface,
3135 IAudioVolumeDuckNotification *notification)
3137 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3138 FIXME("(%p)->(%p) - stub\n", This, notification);
3139 return E_NOTIMPL;
3142 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3144 AudioSessionManager_QueryInterface,
3145 AudioSessionManager_AddRef,
3146 AudioSessionManager_Release,
3147 AudioSessionManager_GetAudioSessionControl,
3148 AudioSessionManager_GetSimpleAudioVolume,
3149 AudioSessionManager_GetSessionEnumerator,
3150 AudioSessionManager_RegisterSessionNotification,
3151 AudioSessionManager_UnregisterSessionNotification,
3152 AudioSessionManager_RegisterDuckNotification,
3153 AudioSessionManager_UnregisterDuckNotification
3156 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3157 IAudioSessionManager2 **out)
3159 SessionMgr *This;
3161 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3162 if(!This)
3163 return E_OUTOFMEMORY;
3165 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3166 This->device = device;
3167 This->ref = 1;
3169 *out = &This->IAudioSessionManager2_iface;
3171 return S_OK;