d3d10/effect: Add a helper to read raw variable values.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobdb2c6b23914d5e49ed7f125f16749f1f4cee45d2
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 IAudioClient3 IAudioClient3_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 IAudioClient3Vtbl AudioClient3_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_IAudioClient3(IAudioClient3 *iface)
184 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_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->IAudioClient3_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->IAudioClient3_iface.lpVtbl = &AudioClient3_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 *out = (IAudioClient *)&This->IAudioClient3_iface;
669 IAudioClient3_AddRef(&This->IAudioClient3_iface);
671 return S_OK;
674 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
675 REFIID riid, void **ppv)
677 ACImpl *This = impl_from_IAudioClient3(iface);
678 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
680 if(!ppv)
681 return E_POINTER;
682 *ppv = NULL;
683 if(IsEqualIID(riid, &IID_IUnknown) ||
684 IsEqualIID(riid, &IID_IAudioClient) ||
685 IsEqualIID(riid, &IID_IAudioClient2) ||
686 IsEqualIID(riid, &IID_IAudioClient3))
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(IAudioClient3 *iface)
700 ACImpl *This = impl_from_IAudioClient3(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(IAudioClient3 *iface)
709 ACImpl *This = impl_from_IAudioClient3(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 IAudioClient3_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(IAudioClient3 *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_IAudioClient3(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 E_INVALIDARG;
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 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
1062 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
1063 FIXME("Unknown flags: %08x\n", flags);
1064 return E_INVALIDARG;
1067 if(mode == AUDCLNT_SHAREMODE_SHARED){
1068 period = DefaultPeriod;
1069 if( duration < 3 * period)
1070 duration = 3 * period;
1071 }else{
1072 if(!period)
1073 period = DefaultPeriod; /* not minimum */
1074 if(period < MinimumPeriod || period > 5000000)
1075 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1076 if(duration > 20000000) /* the smaller the period, the lower this limit */
1077 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1078 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1079 if(duration != period)
1080 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1081 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1082 return AUDCLNT_E_DEVICE_IN_USE;
1083 }else{
1084 if( duration < 8 * period)
1085 duration = 8 * period; /* may grow above 2s */
1089 EnterCriticalSection(&This->lock);
1091 if(This->initted){
1092 LeaveCriticalSection(&This->lock);
1093 return AUDCLNT_E_ALREADY_INITIALIZED;
1096 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1097 if(FAILED(hr)){
1098 LeaveCriticalSection(&This->lock);
1099 return hr;
1102 This->fmt = clone_format(fmt);
1103 if(!This->fmt){
1104 LeaveCriticalSection(&This->lock);
1105 return E_OUTOFMEMORY;
1108 This->period_us = period / 10;
1109 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1111 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1112 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1113 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1114 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1115 This->bufsize_frames * fmt->nBlockAlign);
1116 if(!This->local_buffer){
1117 CoTaskMemFree(This->fmt);
1118 This->fmt = NULL;
1119 LeaveCriticalSection(&This->lock);
1120 return E_OUTOFMEMORY;
1123 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1124 if(!This->vols){
1125 CoTaskMemFree(This->fmt);
1126 This->fmt = NULL;
1127 LeaveCriticalSection(&This->lock);
1128 return E_OUTOFMEMORY;
1131 for(i = 0; i < fmt->nChannels; ++i)
1132 This->vols[i] = 1.f;
1134 This->share = mode;
1135 This->flags = flags;
1136 This->oss_bufsize_bytes = 0;
1138 EnterCriticalSection(&g_sessions_lock);
1140 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1141 &This->session);
1142 if(FAILED(hr)){
1143 LeaveCriticalSection(&g_sessions_lock);
1144 HeapFree(GetProcessHeap(), 0, This->vols);
1145 This->vols = NULL;
1146 CoTaskMemFree(This->fmt);
1147 This->fmt = NULL;
1148 LeaveCriticalSection(&This->lock);
1149 return hr;
1152 list_add_tail(&This->session->clients, &This->entry);
1154 LeaveCriticalSection(&g_sessions_lock);
1156 This->initted = TRUE;
1158 LeaveCriticalSection(&This->lock);
1160 return S_OK;
1163 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
1164 UINT32 *frames)
1166 ACImpl *This = impl_from_IAudioClient3(iface);
1168 TRACE("(%p)->(%p)\n", This, frames);
1170 if(!frames)
1171 return E_POINTER;
1173 EnterCriticalSection(&This->lock);
1175 if(!This->initted){
1176 LeaveCriticalSection(&This->lock);
1177 return AUDCLNT_E_NOT_INITIALIZED;
1180 *frames = This->bufsize_frames;
1182 TRACE("buffer size: %u\n", *frames);
1184 LeaveCriticalSection(&This->lock);
1186 return S_OK;
1189 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
1190 REFERENCE_TIME *latency)
1192 ACImpl *This = impl_from_IAudioClient3(iface);
1194 TRACE("(%p)->(%p)\n", This, latency);
1196 if(!latency)
1197 return E_POINTER;
1199 EnterCriticalSection(&This->lock);
1201 if(!This->initted){
1202 LeaveCriticalSection(&This->lock);
1203 return AUDCLNT_E_NOT_INITIALIZED;
1206 /* pretend we process audio in Period chunks, so max latency includes
1207 * the period time. Some native machines add .6666ms in shared mode. */
1208 *latency = (REFERENCE_TIME)This->period_us * 10 + 6666;
1210 LeaveCriticalSection(&This->lock);
1212 return S_OK;
1215 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
1216 UINT32 *numpad)
1218 ACImpl *This = impl_from_IAudioClient3(iface);
1220 TRACE("(%p)->(%p)\n", This, numpad);
1222 if(!numpad)
1223 return E_POINTER;
1225 EnterCriticalSection(&This->lock);
1227 if(!This->initted){
1228 LeaveCriticalSection(&This->lock);
1229 return AUDCLNT_E_NOT_INITIALIZED;
1232 *numpad = This->held_frames;
1234 TRACE("padding: %u\n", *numpad);
1236 LeaveCriticalSection(&This->lock);
1238 return S_OK;
1241 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
1242 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1243 WAVEFORMATEX **outpwfx)
1245 ACImpl *This = impl_from_IAudioClient3(iface);
1246 int fd = -1;
1247 HRESULT ret;
1249 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1251 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1252 return E_POINTER;
1254 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1255 return E_INVALIDARG;
1257 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1258 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1259 return E_INVALIDARG;
1261 dump_fmt(pwfx);
1263 if(outpwfx){
1264 *outpwfx = NULL;
1265 if(mode != AUDCLNT_SHAREMODE_SHARED)
1266 outpwfx = NULL;
1269 if(This->dataflow == eRender)
1270 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1271 else if(This->dataflow == eCapture)
1272 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1274 if(fd < 0){
1275 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1276 strerror(errno));
1277 return AUDCLNT_E_DEVICE_INVALIDATED;
1280 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1282 close(fd);
1284 return ret;
1287 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1288 WAVEFORMATEX **pwfx)
1290 ACImpl *This = impl_from_IAudioClient3(iface);
1291 WAVEFORMATEXTENSIBLE *fmt;
1292 int formats;
1294 TRACE("(%p)->(%p)\n", This, pwfx);
1296 if(!pwfx)
1297 return E_POINTER;
1298 *pwfx = NULL;
1300 if(This->dataflow == eRender)
1301 formats = This->ai.oformats;
1302 else if(This->dataflow == eCapture)
1303 formats = This->ai.iformats;
1304 else
1305 return E_UNEXPECTED;
1307 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1308 if(!fmt)
1309 return E_OUTOFMEMORY;
1311 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1312 if(formats & AFMT_S16_LE){
1313 fmt->Format.wBitsPerSample = 16;
1314 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1315 #ifdef AFMT_FLOAT
1316 }else if(formats & AFMT_FLOAT){
1317 fmt->Format.wBitsPerSample = 32;
1318 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1319 #endif
1320 }else if(formats & AFMT_U8){
1321 fmt->Format.wBitsPerSample = 8;
1322 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1323 }else if(formats & AFMT_S32_LE){
1324 fmt->Format.wBitsPerSample = 32;
1325 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1326 }else if(formats & AFMT_S24_LE){
1327 fmt->Format.wBitsPerSample = 24;
1328 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1329 }else{
1330 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1331 CoTaskMemFree(fmt);
1332 return E_FAIL;
1335 /* some OSS drivers are buggy, so set reasonable defaults if
1336 * the reported values seem wacky */
1337 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1338 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1339 fmt->Format.nChannels = 2;
1341 /* For most hardware on Windows, users must choose a configuration with an even
1342 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1343 * channels, but those channels are still reported to applications from
1344 * GetMixFormat! Some applications behave badly if given an odd number of
1345 * channels (e.g. 2.1). */
1346 if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1))
1348 if(fmt->Format.nChannels < This->ai.max_channels)
1349 fmt->Format.nChannels += 1;
1350 else
1351 /* We could "fake" more channels and downmix the emulated channels,
1352 * but at that point you really ought to tweak your OSS setup or
1353 * just use PulseAudio. */
1354 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
1357 if(This->ai.max_rate == 0)
1358 fmt->Format.nSamplesPerSec = 44100;
1359 else
1360 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1361 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1362 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1364 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1366 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1367 fmt->Format.nChannels) / 8;
1368 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1369 fmt->Format.nBlockAlign;
1371 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1372 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1374 *pwfx = (WAVEFORMATEX*)fmt;
1375 dump_fmt(*pwfx);
1377 return S_OK;
1380 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1381 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1383 ACImpl *This = impl_from_IAudioClient3(iface);
1385 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1387 if(!defperiod && !minperiod)
1388 return E_POINTER;
1390 if(defperiod)
1391 *defperiod = DefaultPeriod;
1392 if(minperiod)
1393 *minperiod = MinimumPeriod;
1395 return S_OK;
1398 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1400 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1401 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1402 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1403 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1404 This->fmt->wBitsPerSample == 8)
1405 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1406 else
1407 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1410 static void oss_write_data(ACImpl *This)
1412 ssize_t written_bytes;
1413 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1414 SIZE_T to_write_frames, to_write_bytes, advanced;
1415 audio_buf_info bi;
1416 BYTE *buf;
1418 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1419 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1420 return;
1423 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1425 if(bi.bytes > This->oss_bufsize_bytes){
1426 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1427 bi.bytes, This->oss_bufsize_bytes);
1428 This->oss_bufsize_bytes = bi.bytes;
1429 in_oss_frames = 0;
1430 }else
1431 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1433 if(in_oss_frames > This->in_oss_frames){
1434 TRACE("Capping reported frames from %u to %u\n",
1435 in_oss_frames, This->in_oss_frames);
1436 in_oss_frames = This->in_oss_frames;
1439 write_limit = 0;
1440 while(write_limit + in_oss_frames < max_period * 3)
1441 write_limit += max_period;
1442 if(write_limit == 0)
1443 return;
1445 /* vvvvvv - in_oss_frames
1446 * [--xxxxxxxxxx]
1447 * [xxxxxxxxxx--]
1448 * ^^^^^^^^^^ - held_frames
1449 * ^ - lcl_offs_frames
1451 advanced = This->in_oss_frames - in_oss_frames;
1452 if(advanced > This->held_frames)
1453 advanced = This->held_frames;
1454 This->lcl_offs_frames += advanced;
1455 This->lcl_offs_frames %= This->bufsize_frames;
1456 This->held_frames -= advanced;
1457 This->in_oss_frames = in_oss_frames;
1458 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
1459 advanced, This->lcl_offs_frames, This->held_frames, This->in_oss_frames);
1462 if(This->held_frames == This->in_oss_frames)
1463 return;
1465 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1466 new_frames = This->held_frames - This->in_oss_frames;
1468 if(write_offs_frames + new_frames > This->bufsize_frames)
1469 to_write_frames = This->bufsize_frames - write_offs_frames;
1470 else
1471 to_write_frames = new_frames;
1473 to_write_frames = min(to_write_frames, write_limit);
1474 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1475 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
1476 write_offs_frames, to_write_frames + write_offs_frames,
1477 This->bufsize_frames);
1479 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1481 if(This->session->mute)
1482 silence_buffer(This, buf, to_write_frames);
1484 written_bytes = write(This->fd, buf, to_write_bytes);
1485 if(written_bytes < 0){
1486 /* EAGAIN is OSS buffer full, log that too */
1487 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1488 return;
1490 written_frames = written_bytes / This->fmt->nBlockAlign;
1492 This->in_oss_frames += written_frames;
1494 if(written_frames < to_write_frames){
1495 /* OSS buffer probably full */
1496 return;
1499 if(new_frames > written_frames && written_frames < write_limit){
1500 /* wrapped and have some data back at the start to write */
1502 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1503 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1505 if(This->session->mute)
1506 silence_buffer(This, This->local_buffer, to_write_frames);
1508 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
1510 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1511 if(written_bytes < 0){
1512 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1513 return;
1515 written_frames = written_bytes / This->fmt->nBlockAlign;
1516 This->in_oss_frames += written_frames;
1520 static void oss_read_data(ACImpl *This)
1522 UINT64 pos, readable;
1523 ssize_t nread;
1525 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1526 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1528 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1529 readable);
1530 if(nread < 0){
1531 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1532 return;
1535 This->held_frames += nread / This->fmt->nBlockAlign;
1537 if(This->held_frames > This->bufsize_frames){
1538 WARN("Overflow of unread data\n");
1539 This->lcl_offs_frames += This->held_frames;
1540 This->lcl_offs_frames %= This->bufsize_frames;
1541 This->held_frames = This->bufsize_frames;
1545 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1547 ACImpl *This = user;
1549 EnterCriticalSection(&This->lock);
1551 if(This->playing){
1552 if(This->dataflow == eRender && This->held_frames)
1553 oss_write_data(This);
1554 else if(This->dataflow == eCapture)
1555 oss_read_data(This);
1558 LeaveCriticalSection(&This->lock);
1560 if(This->event)
1561 SetEvent(This->event);
1564 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
1566 ACImpl *This = impl_from_IAudioClient3(iface);
1568 TRACE("(%p)\n", This);
1570 EnterCriticalSection(&This->lock);
1572 if(!This->initted){
1573 LeaveCriticalSection(&This->lock);
1574 return AUDCLNT_E_NOT_INITIALIZED;
1577 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1578 LeaveCriticalSection(&This->lock);
1579 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1582 if(This->playing){
1583 LeaveCriticalSection(&This->lock);
1584 return AUDCLNT_E_NOT_STOPPED;
1587 if(!This->timer){
1588 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1589 oss_period_callback, This, 0, This->period_us / 1000,
1590 WT_EXECUTEINTIMERTHREAD))
1591 ERR("Unable to create period timer: %u\n", GetLastError());
1594 This->playing = TRUE;
1596 LeaveCriticalSection(&This->lock);
1598 return S_OK;
1601 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
1603 ACImpl *This = impl_from_IAudioClient3(iface);
1605 TRACE("(%p)\n", This);
1607 EnterCriticalSection(&This->lock);
1609 if(!This->initted){
1610 LeaveCriticalSection(&This->lock);
1611 return AUDCLNT_E_NOT_INITIALIZED;
1614 if(!This->playing){
1615 LeaveCriticalSection(&This->lock);
1616 return S_FALSE;
1619 This->playing = FALSE;
1620 This->in_oss_frames = 0;
1622 LeaveCriticalSection(&This->lock);
1624 return S_OK;
1627 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
1629 ACImpl *This = impl_from_IAudioClient3(iface);
1631 TRACE("(%p)\n", This);
1633 EnterCriticalSection(&This->lock);
1635 if(!This->initted){
1636 LeaveCriticalSection(&This->lock);
1637 return AUDCLNT_E_NOT_INITIALIZED;
1640 if(This->playing){
1641 LeaveCriticalSection(&This->lock);
1642 return AUDCLNT_E_NOT_STOPPED;
1645 if(This->getbuf_last){
1646 LeaveCriticalSection(&This->lock);
1647 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1650 if(This->dataflow == eRender){
1651 This->written_frames = 0;
1652 This->last_pos_frames = 0;
1653 }else{
1654 This->written_frames += This->held_frames;
1656 This->held_frames = 0;
1657 This->lcl_offs_frames = 0;
1658 This->in_oss_frames = 0;
1660 LeaveCriticalSection(&This->lock);
1662 return S_OK;
1665 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
1666 HANDLE event)
1668 ACImpl *This = impl_from_IAudioClient3(iface);
1670 TRACE("(%p)->(%p)\n", This, event);
1672 if(!event)
1673 return E_INVALIDARG;
1675 EnterCriticalSection(&This->lock);
1677 if(!This->initted){
1678 LeaveCriticalSection(&This->lock);
1679 return AUDCLNT_E_NOT_INITIALIZED;
1682 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1683 LeaveCriticalSection(&This->lock);
1684 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1687 if (This->event){
1688 LeaveCriticalSection(&This->lock);
1689 FIXME("called twice\n");
1690 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1693 This->event = event;
1695 LeaveCriticalSection(&This->lock);
1697 return S_OK;
1700 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
1701 void **ppv)
1703 ACImpl *This = impl_from_IAudioClient3(iface);
1705 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1707 if(!ppv)
1708 return E_POINTER;
1709 *ppv = NULL;
1711 EnterCriticalSection(&This->lock);
1713 if(!This->initted){
1714 LeaveCriticalSection(&This->lock);
1715 return AUDCLNT_E_NOT_INITIALIZED;
1718 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1719 if(This->dataflow != eRender){
1720 LeaveCriticalSection(&This->lock);
1721 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1723 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1724 *ppv = &This->IAudioRenderClient_iface;
1725 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1726 if(This->dataflow != eCapture){
1727 LeaveCriticalSection(&This->lock);
1728 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1730 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1731 *ppv = &This->IAudioCaptureClient_iface;
1732 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1733 IAudioClock_AddRef(&This->IAudioClock_iface);
1734 *ppv = &This->IAudioClock_iface;
1735 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1736 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1737 *ppv = &This->IAudioStreamVolume_iface;
1738 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1739 if(!This->session_wrapper){
1740 This->session_wrapper = AudioSessionWrapper_Create(This);
1741 if(!This->session_wrapper){
1742 LeaveCriticalSection(&This->lock);
1743 return E_OUTOFMEMORY;
1745 }else
1746 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1748 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1749 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1750 if(!This->session_wrapper){
1751 This->session_wrapper = AudioSessionWrapper_Create(This);
1752 if(!This->session_wrapper){
1753 LeaveCriticalSection(&This->lock);
1754 return E_OUTOFMEMORY;
1756 }else
1757 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1759 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1760 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1761 if(!This->session_wrapper){
1762 This->session_wrapper = AudioSessionWrapper_Create(This);
1763 if(!This->session_wrapper){
1764 LeaveCriticalSection(&This->lock);
1765 return E_OUTOFMEMORY;
1767 }else
1768 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1770 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1773 if(*ppv){
1774 LeaveCriticalSection(&This->lock);
1775 return S_OK;
1778 LeaveCriticalSection(&This->lock);
1780 FIXME("stub %s\n", debugstr_guid(riid));
1781 return E_NOINTERFACE;
1784 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1785 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1787 ACImpl *This = impl_from_IAudioClient3(iface);
1789 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1791 if(!offload_capable)
1792 return E_INVALIDARG;
1794 *offload_capable = FALSE;
1796 return S_OK;
1799 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1800 const AudioClientProperties *prop)
1802 ACImpl *This = impl_from_IAudioClient3(iface);
1803 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1805 TRACE("(%p)->(%p)\n", This, prop);
1807 if(!legacy_prop)
1808 return E_POINTER;
1810 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1811 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1812 legacy_prop->bIsOffload,
1813 legacy_prop->eCategory,
1814 prop->Options);
1815 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1816 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1817 legacy_prop->bIsOffload,
1818 legacy_prop->eCategory);
1819 }else{
1820 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1821 return E_INVALIDARG;
1825 if(legacy_prop->bIsOffload)
1826 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1828 return S_OK;
1831 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1832 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1833 REFERENCE_TIME *max_duration)
1835 ACImpl *This = impl_from_IAudioClient3(iface);
1837 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1839 return E_NOTIMPL;
1842 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1843 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1844 UINT32 *min_period_frames, UINT32 *max_period_frames)
1846 ACImpl *This = impl_from_IAudioClient3(iface);
1848 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1849 min_period_frames, max_period_frames);
1851 return E_NOTIMPL;
1854 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1855 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1857 ACImpl *This = impl_from_IAudioClient3(iface);
1859 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1861 return E_NOTIMPL;
1864 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1865 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1866 const GUID *session_guid)
1868 ACImpl *This = impl_from_IAudioClient3(iface);
1870 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1872 return E_NOTIMPL;
1875 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1877 AudioClient_QueryInterface,
1878 AudioClient_AddRef,
1879 AudioClient_Release,
1880 AudioClient_Initialize,
1881 AudioClient_GetBufferSize,
1882 AudioClient_GetStreamLatency,
1883 AudioClient_GetCurrentPadding,
1884 AudioClient_IsFormatSupported,
1885 AudioClient_GetMixFormat,
1886 AudioClient_GetDevicePeriod,
1887 AudioClient_Start,
1888 AudioClient_Stop,
1889 AudioClient_Reset,
1890 AudioClient_SetEventHandle,
1891 AudioClient_GetService,
1892 AudioClient_IsOffloadCapable,
1893 AudioClient_SetClientProperties,
1894 AudioClient_GetBufferSizeLimits,
1895 AudioClient_GetSharedModeEnginePeriod,
1896 AudioClient_GetCurrentSharedModeEnginePeriod,
1897 AudioClient_InitializeSharedAudioStream,
1900 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1901 IAudioRenderClient *iface, REFIID riid, void **ppv)
1903 ACImpl *This = impl_from_IAudioRenderClient(iface);
1904 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1906 if(!ppv)
1907 return E_POINTER;
1908 *ppv = NULL;
1910 if(IsEqualIID(riid, &IID_IUnknown) ||
1911 IsEqualIID(riid, &IID_IAudioRenderClient))
1912 *ppv = iface;
1913 else if(IsEqualIID(riid, &IID_IMarshal))
1914 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1915 if(*ppv){
1916 IUnknown_AddRef((IUnknown*)*ppv);
1917 return S_OK;
1920 WARN("Unknown interface %s\n", debugstr_guid(riid));
1921 return E_NOINTERFACE;
1924 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1926 ACImpl *This = impl_from_IAudioRenderClient(iface);
1927 return AudioClient_AddRef(&This->IAudioClient3_iface);
1930 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1932 ACImpl *This = impl_from_IAudioRenderClient(iface);
1933 return AudioClient_Release(&This->IAudioClient3_iface);
1936 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1937 UINT32 frames, BYTE **data)
1939 ACImpl *This = impl_from_IAudioRenderClient(iface);
1940 UINT32 write_pos;
1942 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1944 if(!data)
1945 return E_POINTER;
1947 *data = NULL;
1949 EnterCriticalSection(&This->lock);
1951 if(This->getbuf_last){
1952 LeaveCriticalSection(&This->lock);
1953 return AUDCLNT_E_OUT_OF_ORDER;
1956 if(!frames){
1957 LeaveCriticalSection(&This->lock);
1958 return S_OK;
1961 if(This->held_frames + frames > This->bufsize_frames){
1962 LeaveCriticalSection(&This->lock);
1963 return AUDCLNT_E_BUFFER_TOO_LARGE;
1966 write_pos =
1967 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1968 if(write_pos + frames > This->bufsize_frames){
1969 if(This->tmp_buffer_frames < frames){
1970 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1971 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1972 frames * This->fmt->nBlockAlign);
1973 if(!This->tmp_buffer){
1974 LeaveCriticalSection(&This->lock);
1975 return E_OUTOFMEMORY;
1977 This->tmp_buffer_frames = frames;
1979 *data = This->tmp_buffer;
1980 This->getbuf_last = -frames;
1981 }else{
1982 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1983 This->getbuf_last = frames;
1986 silence_buffer(This, *data, frames);
1988 LeaveCriticalSection(&This->lock);
1990 return S_OK;
1993 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1995 UINT32 write_offs_frames =
1996 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1997 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1998 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1999 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2000 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2002 if(written_bytes <= chunk_bytes){
2003 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2004 }else{
2005 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2006 memcpy(This->local_buffer, buffer + chunk_bytes,
2007 written_bytes - chunk_bytes);
2011 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2012 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2014 ACImpl *This = impl_from_IAudioRenderClient(iface);
2015 BYTE *buffer;
2017 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2019 EnterCriticalSection(&This->lock);
2021 if(!written_frames){
2022 This->getbuf_last = 0;
2023 LeaveCriticalSection(&This->lock);
2024 return S_OK;
2027 if(!This->getbuf_last){
2028 LeaveCriticalSection(&This->lock);
2029 return AUDCLNT_E_OUT_OF_ORDER;
2032 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2033 LeaveCriticalSection(&This->lock);
2034 return AUDCLNT_E_INVALID_SIZE;
2037 if(This->getbuf_last >= 0)
2038 buffer = This->local_buffer + This->fmt->nBlockAlign *
2039 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
2040 else
2041 buffer = This->tmp_buffer;
2043 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2044 silence_buffer(This, buffer, written_frames);
2046 if(This->getbuf_last < 0)
2047 oss_wrap_buffer(This, buffer, written_frames);
2049 This->held_frames += written_frames;
2050 This->written_frames += written_frames;
2051 This->getbuf_last = 0;
2053 LeaveCriticalSection(&This->lock);
2055 return S_OK;
2058 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2059 AudioRenderClient_QueryInterface,
2060 AudioRenderClient_AddRef,
2061 AudioRenderClient_Release,
2062 AudioRenderClient_GetBuffer,
2063 AudioRenderClient_ReleaseBuffer
2066 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2067 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2069 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2070 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2072 if(!ppv)
2073 return E_POINTER;
2074 *ppv = NULL;
2076 if(IsEqualIID(riid, &IID_IUnknown) ||
2077 IsEqualIID(riid, &IID_IAudioCaptureClient))
2078 *ppv = iface;
2079 else if(IsEqualIID(riid, &IID_IMarshal))
2080 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2081 if(*ppv){
2082 IUnknown_AddRef((IUnknown*)*ppv);
2083 return S_OK;
2086 WARN("Unknown interface %s\n", debugstr_guid(riid));
2087 return E_NOINTERFACE;
2090 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2092 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2093 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2096 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2098 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2099 return IAudioClient3_Release(&This->IAudioClient3_iface);
2102 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2103 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2104 UINT64 *qpcpos)
2106 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2108 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2109 devpos, qpcpos);
2111 if(!data)
2112 return E_POINTER;
2114 *data = NULL;
2116 if(!frames || !flags)
2117 return E_POINTER;
2119 EnterCriticalSection(&This->lock);
2121 if(This->getbuf_last){
2122 LeaveCriticalSection(&This->lock);
2123 return AUDCLNT_E_OUT_OF_ORDER;
2126 if(This->held_frames < This->period_frames){
2127 *frames = 0;
2128 LeaveCriticalSection(&This->lock);
2129 return AUDCLNT_S_BUFFER_EMPTY;
2132 *flags = 0;
2134 *frames = This->period_frames;
2136 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2137 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2138 if(This->tmp_buffer_frames < *frames){
2139 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2140 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2141 *frames * This->fmt->nBlockAlign);
2142 if(!This->tmp_buffer){
2143 LeaveCriticalSection(&This->lock);
2144 return E_OUTOFMEMORY;
2146 This->tmp_buffer_frames = *frames;
2149 *data = This->tmp_buffer;
2150 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2151 This->fmt->nBlockAlign;
2152 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2153 frames_bytes = *frames * This->fmt->nBlockAlign;
2154 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2155 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2156 frames_bytes - chunk_bytes);
2157 }else
2158 *data = This->local_buffer +
2159 This->lcl_offs_frames * This->fmt->nBlockAlign;
2161 This->getbuf_last = *frames;
2163 if(devpos)
2164 *devpos = This->written_frames;
2165 if(qpcpos){
2166 LARGE_INTEGER stamp, freq;
2167 QueryPerformanceCounter(&stamp);
2168 QueryPerformanceFrequency(&freq);
2169 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2172 LeaveCriticalSection(&This->lock);
2174 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2177 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2178 IAudioCaptureClient *iface, UINT32 done)
2180 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2182 TRACE("(%p)->(%u)\n", This, done);
2184 EnterCriticalSection(&This->lock);
2186 if(!done){
2187 This->getbuf_last = 0;
2188 LeaveCriticalSection(&This->lock);
2189 return S_OK;
2192 if(!This->getbuf_last){
2193 LeaveCriticalSection(&This->lock);
2194 return AUDCLNT_E_OUT_OF_ORDER;
2197 if(This->getbuf_last != done){
2198 LeaveCriticalSection(&This->lock);
2199 return AUDCLNT_E_INVALID_SIZE;
2202 This->written_frames += done;
2203 This->held_frames -= done;
2204 This->lcl_offs_frames += done;
2205 This->lcl_offs_frames %= This->bufsize_frames;
2206 This->getbuf_last = 0;
2208 LeaveCriticalSection(&This->lock);
2210 return S_OK;
2213 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2214 IAudioCaptureClient *iface, UINT32 *frames)
2216 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2218 TRACE("(%p)->(%p)\n", This, frames);
2220 if(!frames)
2221 return E_POINTER;
2223 EnterCriticalSection(&This->lock);
2225 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2227 LeaveCriticalSection(&This->lock);
2229 return S_OK;
2232 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2234 AudioCaptureClient_QueryInterface,
2235 AudioCaptureClient_AddRef,
2236 AudioCaptureClient_Release,
2237 AudioCaptureClient_GetBuffer,
2238 AudioCaptureClient_ReleaseBuffer,
2239 AudioCaptureClient_GetNextPacketSize
2242 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2243 REFIID riid, void **ppv)
2245 ACImpl *This = impl_from_IAudioClock(iface);
2247 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2249 if(!ppv)
2250 return E_POINTER;
2251 *ppv = NULL;
2253 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2254 *ppv = iface;
2255 else if(IsEqualIID(riid, &IID_IAudioClock2))
2256 *ppv = &This->IAudioClock2_iface;
2257 if(*ppv){
2258 IUnknown_AddRef((IUnknown*)*ppv);
2259 return S_OK;
2262 WARN("Unknown interface %s\n", debugstr_guid(riid));
2263 return E_NOINTERFACE;
2266 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2268 ACImpl *This = impl_from_IAudioClock(iface);
2269 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2272 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2274 ACImpl *This = impl_from_IAudioClock(iface);
2275 return IAudioClient3_Release(&This->IAudioClient3_iface);
2278 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2280 ACImpl *This = impl_from_IAudioClock(iface);
2282 TRACE("(%p)->(%p)\n", This, freq);
2284 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2285 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2286 else
2287 *freq = This->fmt->nSamplesPerSec;
2289 return S_OK;
2292 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2293 UINT64 *qpctime)
2295 ACImpl *This = impl_from_IAudioClock(iface);
2297 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2299 if(!pos)
2300 return E_POINTER;
2302 EnterCriticalSection(&This->lock);
2304 if(This->dataflow == eRender){
2305 *pos = This->written_frames - This->held_frames;
2306 if(*pos < This->last_pos_frames)
2307 *pos = This->last_pos_frames;
2308 }else if(This->dataflow == eCapture){
2309 audio_buf_info bi;
2310 UINT32 held;
2312 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2313 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2314 held = 0;
2315 }else{
2316 if(bi.bytes <= bi.fragsize)
2317 held = 0;
2318 else
2319 held = bi.bytes / This->fmt->nBlockAlign;
2322 *pos = This->written_frames + held;
2325 This->last_pos_frames = *pos;
2327 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2328 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2329 *pos *= This->fmt->nBlockAlign;
2331 LeaveCriticalSection(&This->lock);
2333 if(qpctime){
2334 LARGE_INTEGER stamp, freq;
2335 QueryPerformanceCounter(&stamp);
2336 QueryPerformanceFrequency(&freq);
2337 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2340 return S_OK;
2343 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2344 DWORD *chars)
2346 ACImpl *This = impl_from_IAudioClock(iface);
2348 TRACE("(%p)->(%p)\n", This, chars);
2350 if(!chars)
2351 return E_POINTER;
2353 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2355 return S_OK;
2358 static const IAudioClockVtbl AudioClock_Vtbl =
2360 AudioClock_QueryInterface,
2361 AudioClock_AddRef,
2362 AudioClock_Release,
2363 AudioClock_GetFrequency,
2364 AudioClock_GetPosition,
2365 AudioClock_GetCharacteristics
2368 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2369 REFIID riid, void **ppv)
2371 ACImpl *This = impl_from_IAudioClock2(iface);
2372 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2375 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2377 ACImpl *This = impl_from_IAudioClock2(iface);
2378 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2381 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2383 ACImpl *This = impl_from_IAudioClock2(iface);
2384 return IAudioClient3_Release(&This->IAudioClient3_iface);
2387 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2388 UINT64 *pos, UINT64 *qpctime)
2390 ACImpl *This = impl_from_IAudioClock2(iface);
2392 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2394 return E_NOTIMPL;
2397 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2399 AudioClock2_QueryInterface,
2400 AudioClock2_AddRef,
2401 AudioClock2_Release,
2402 AudioClock2_GetDevicePosition
2405 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2407 AudioSessionWrapper *ret;
2409 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2410 sizeof(AudioSessionWrapper));
2411 if(!ret)
2412 return NULL;
2414 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2415 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2416 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2418 ret->ref = 1;
2420 ret->client = client;
2421 if(client){
2422 ret->session = client->session;
2423 AudioClient_AddRef(&client->IAudioClient3_iface);
2426 return ret;
2429 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2430 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2432 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2434 if(!ppv)
2435 return E_POINTER;
2436 *ppv = NULL;
2438 if(IsEqualIID(riid, &IID_IUnknown) ||
2439 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2440 IsEqualIID(riid, &IID_IAudioSessionControl2))
2441 *ppv = iface;
2442 if(*ppv){
2443 IUnknown_AddRef((IUnknown*)*ppv);
2444 return S_OK;
2447 WARN("Unknown interface %s\n", debugstr_guid(riid));
2448 return E_NOINTERFACE;
2451 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2453 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2454 ULONG ref;
2455 ref = InterlockedIncrement(&This->ref);
2456 TRACE("(%p) Refcount now %u\n", This, ref);
2457 return ref;
2460 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2462 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2463 ULONG ref;
2464 ref = InterlockedDecrement(&This->ref);
2465 TRACE("(%p) Refcount now %u\n", This, ref);
2466 if(!ref){
2467 if(This->client){
2468 EnterCriticalSection(&This->client->lock);
2469 This->client->session_wrapper = NULL;
2470 LeaveCriticalSection(&This->client->lock);
2471 AudioClient_Release(&This->client->IAudioClient3_iface);
2473 HeapFree(GetProcessHeap(), 0, This);
2475 return ref;
2478 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2479 AudioSessionState *state)
2481 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2482 ACImpl *client;
2484 TRACE("(%p)->(%p)\n", This, state);
2486 if(!state)
2487 return NULL_PTR_ERR;
2489 EnterCriticalSection(&g_sessions_lock);
2491 if(list_empty(&This->session->clients)){
2492 *state = AudioSessionStateExpired;
2493 LeaveCriticalSection(&g_sessions_lock);
2494 return S_OK;
2497 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2498 EnterCriticalSection(&client->lock);
2499 if(client->playing){
2500 *state = AudioSessionStateActive;
2501 LeaveCriticalSection(&client->lock);
2502 LeaveCriticalSection(&g_sessions_lock);
2503 return S_OK;
2505 LeaveCriticalSection(&client->lock);
2508 LeaveCriticalSection(&g_sessions_lock);
2510 *state = AudioSessionStateInactive;
2512 return S_OK;
2515 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2516 IAudioSessionControl2 *iface, WCHAR **name)
2518 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2520 FIXME("(%p)->(%p) - stub\n", This, name);
2522 return E_NOTIMPL;
2525 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2526 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2528 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2530 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2532 return E_NOTIMPL;
2535 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2536 IAudioSessionControl2 *iface, WCHAR **path)
2538 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2540 FIXME("(%p)->(%p) - stub\n", This, path);
2542 return E_NOTIMPL;
2545 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2546 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2548 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2550 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2552 return E_NOTIMPL;
2555 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2556 IAudioSessionControl2 *iface, GUID *group)
2558 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2560 FIXME("(%p)->(%p) - stub\n", This, group);
2562 return E_NOTIMPL;
2565 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2566 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2568 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2570 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2571 debugstr_guid(session));
2573 return E_NOTIMPL;
2576 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2577 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2579 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2581 FIXME("(%p)->(%p) - stub\n", This, events);
2583 return S_OK;
2586 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2587 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2589 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2591 FIXME("(%p)->(%p) - stub\n", This, events);
2593 return S_OK;
2596 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2597 IAudioSessionControl2 *iface, WCHAR **id)
2599 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2601 FIXME("(%p)->(%p) - stub\n", This, id);
2603 return E_NOTIMPL;
2606 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2607 IAudioSessionControl2 *iface, WCHAR **id)
2609 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2611 FIXME("(%p)->(%p) - stub\n", This, id);
2613 return E_NOTIMPL;
2616 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2617 IAudioSessionControl2 *iface, DWORD *pid)
2619 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2621 TRACE("(%p)->(%p)\n", This, pid);
2623 if(!pid)
2624 return E_POINTER;
2626 *pid = GetCurrentProcessId();
2628 return S_OK;
2631 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2632 IAudioSessionControl2 *iface)
2634 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2636 TRACE("(%p)\n", This);
2638 return S_FALSE;
2641 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2642 IAudioSessionControl2 *iface, BOOL optout)
2644 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2646 TRACE("(%p)->(%d)\n", This, optout);
2648 return S_OK;
2651 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2653 AudioSessionControl_QueryInterface,
2654 AudioSessionControl_AddRef,
2655 AudioSessionControl_Release,
2656 AudioSessionControl_GetState,
2657 AudioSessionControl_GetDisplayName,
2658 AudioSessionControl_SetDisplayName,
2659 AudioSessionControl_GetIconPath,
2660 AudioSessionControl_SetIconPath,
2661 AudioSessionControl_GetGroupingParam,
2662 AudioSessionControl_SetGroupingParam,
2663 AudioSessionControl_RegisterAudioSessionNotification,
2664 AudioSessionControl_UnregisterAudioSessionNotification,
2665 AudioSessionControl_GetSessionIdentifier,
2666 AudioSessionControl_GetSessionInstanceIdentifier,
2667 AudioSessionControl_GetProcessId,
2668 AudioSessionControl_IsSystemSoundsSession,
2669 AudioSessionControl_SetDuckingPreference
2672 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2673 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2675 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2677 if(!ppv)
2678 return E_POINTER;
2679 *ppv = NULL;
2681 if(IsEqualIID(riid, &IID_IUnknown) ||
2682 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2683 *ppv = iface;
2684 if(*ppv){
2685 IUnknown_AddRef((IUnknown*)*ppv);
2686 return S_OK;
2689 WARN("Unknown interface %s\n", debugstr_guid(riid));
2690 return E_NOINTERFACE;
2693 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2695 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2696 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2699 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2701 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2702 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2705 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2706 ISimpleAudioVolume *iface, float level, const GUID *context)
2708 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2709 AudioSession *session = This->session;
2711 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2713 if(level < 0.f || level > 1.f)
2714 return E_INVALIDARG;
2716 if(context)
2717 FIXME("Notifications not supported yet\n");
2719 EnterCriticalSection(&session->lock);
2721 session->master_vol = level;
2723 TRACE("OSS doesn't support setting volume\n");
2725 LeaveCriticalSection(&session->lock);
2727 return S_OK;
2730 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2731 ISimpleAudioVolume *iface, float *level)
2733 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2734 AudioSession *session = This->session;
2736 TRACE("(%p)->(%p)\n", session, level);
2738 if(!level)
2739 return NULL_PTR_ERR;
2741 *level = session->master_vol;
2743 return S_OK;
2746 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2747 BOOL mute, const GUID *context)
2749 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2750 AudioSession *session = This->session;
2752 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2754 EnterCriticalSection(&session->lock);
2756 session->mute = mute;
2758 LeaveCriticalSection(&session->lock);
2760 return S_OK;
2763 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2764 BOOL *mute)
2766 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2767 AudioSession *session = This->session;
2769 TRACE("(%p)->(%p)\n", session, mute);
2771 if(!mute)
2772 return NULL_PTR_ERR;
2774 *mute = This->session->mute;
2776 return S_OK;
2779 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2781 SimpleAudioVolume_QueryInterface,
2782 SimpleAudioVolume_AddRef,
2783 SimpleAudioVolume_Release,
2784 SimpleAudioVolume_SetMasterVolume,
2785 SimpleAudioVolume_GetMasterVolume,
2786 SimpleAudioVolume_SetMute,
2787 SimpleAudioVolume_GetMute
2790 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2791 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2793 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2795 if(!ppv)
2796 return E_POINTER;
2797 *ppv = NULL;
2799 if(IsEqualIID(riid, &IID_IUnknown) ||
2800 IsEqualIID(riid, &IID_IAudioStreamVolume))
2801 *ppv = iface;
2802 if(*ppv){
2803 IUnknown_AddRef((IUnknown*)*ppv);
2804 return S_OK;
2807 WARN("Unknown interface %s\n", debugstr_guid(riid));
2808 return E_NOINTERFACE;
2811 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2813 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2814 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2817 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2819 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2820 return IAudioClient3_Release(&This->IAudioClient3_iface);
2823 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2824 IAudioStreamVolume *iface, UINT32 *out)
2826 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2828 TRACE("(%p)->(%p)\n", This, out);
2830 if(!out)
2831 return E_POINTER;
2833 *out = This->fmt->nChannels;
2835 return S_OK;
2838 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2839 IAudioStreamVolume *iface, UINT32 index, float level)
2841 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2843 TRACE("(%p)->(%d, %f)\n", This, index, level);
2845 if(level < 0.f || level > 1.f)
2846 return E_INVALIDARG;
2848 if(index >= This->fmt->nChannels)
2849 return E_INVALIDARG;
2851 EnterCriticalSection(&This->lock);
2853 This->vols[index] = level;
2855 TRACE("OSS doesn't support setting volume\n");
2857 LeaveCriticalSection(&This->lock);
2859 return S_OK;
2862 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2863 IAudioStreamVolume *iface, UINT32 index, float *level)
2865 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2867 TRACE("(%p)->(%d, %p)\n", This, index, level);
2869 if(!level)
2870 return E_POINTER;
2872 if(index >= This->fmt->nChannels)
2873 return E_INVALIDARG;
2875 *level = This->vols[index];
2877 return S_OK;
2880 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2881 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2883 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2884 int i;
2886 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2888 if(!levels)
2889 return E_POINTER;
2891 if(count != This->fmt->nChannels)
2892 return E_INVALIDARG;
2894 EnterCriticalSection(&This->lock);
2896 for(i = 0; i < count; ++i)
2897 This->vols[i] = levels[i];
2899 TRACE("OSS doesn't support setting volume\n");
2901 LeaveCriticalSection(&This->lock);
2903 return S_OK;
2906 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2907 IAudioStreamVolume *iface, UINT32 count, float *levels)
2909 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2910 int i;
2912 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2914 if(!levels)
2915 return E_POINTER;
2917 if(count != This->fmt->nChannels)
2918 return E_INVALIDARG;
2920 EnterCriticalSection(&This->lock);
2922 for(i = 0; i < count; ++i)
2923 levels[i] = This->vols[i];
2925 LeaveCriticalSection(&This->lock);
2927 return S_OK;
2930 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2932 AudioStreamVolume_QueryInterface,
2933 AudioStreamVolume_AddRef,
2934 AudioStreamVolume_Release,
2935 AudioStreamVolume_GetChannelCount,
2936 AudioStreamVolume_SetChannelVolume,
2937 AudioStreamVolume_GetChannelVolume,
2938 AudioStreamVolume_SetAllVolumes,
2939 AudioStreamVolume_GetAllVolumes
2942 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2943 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2945 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2947 if(!ppv)
2948 return E_POINTER;
2949 *ppv = NULL;
2951 if(IsEqualIID(riid, &IID_IUnknown) ||
2952 IsEqualIID(riid, &IID_IChannelAudioVolume))
2953 *ppv = iface;
2954 if(*ppv){
2955 IUnknown_AddRef((IUnknown*)*ppv);
2956 return S_OK;
2959 WARN("Unknown interface %s\n", debugstr_guid(riid));
2960 return E_NOINTERFACE;
2963 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2965 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2966 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2969 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2971 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2972 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2975 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2976 IChannelAudioVolume *iface, UINT32 *out)
2978 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2979 AudioSession *session = This->session;
2981 TRACE("(%p)->(%p)\n", session, out);
2983 if(!out)
2984 return NULL_PTR_ERR;
2986 *out = session->channel_count;
2988 return S_OK;
2991 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2992 IChannelAudioVolume *iface, UINT32 index, float level,
2993 const GUID *context)
2995 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2996 AudioSession *session = This->session;
2998 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2999 wine_dbgstr_guid(context));
3001 if(level < 0.f || level > 1.f)
3002 return E_INVALIDARG;
3004 if(index >= session->channel_count)
3005 return E_INVALIDARG;
3007 if(context)
3008 FIXME("Notifications not supported yet\n");
3010 EnterCriticalSection(&session->lock);
3012 session->channel_vols[index] = level;
3014 TRACE("OSS doesn't support setting volume\n");
3016 LeaveCriticalSection(&session->lock);
3018 return S_OK;
3021 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3022 IChannelAudioVolume *iface, UINT32 index, float *level)
3024 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3025 AudioSession *session = This->session;
3027 TRACE("(%p)->(%d, %p)\n", session, index, level);
3029 if(!level)
3030 return NULL_PTR_ERR;
3032 if(index >= session->channel_count)
3033 return E_INVALIDARG;
3035 *level = session->channel_vols[index];
3037 return S_OK;
3040 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3041 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3042 const GUID *context)
3044 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3045 AudioSession *session = This->session;
3046 int i;
3048 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3049 wine_dbgstr_guid(context));
3051 if(!levels)
3052 return NULL_PTR_ERR;
3054 if(count != session->channel_count)
3055 return E_INVALIDARG;
3057 if(context)
3058 FIXME("Notifications not supported yet\n");
3060 EnterCriticalSection(&session->lock);
3062 for(i = 0; i < count; ++i)
3063 session->channel_vols[i] = levels[i];
3065 TRACE("OSS doesn't support setting volume\n");
3067 LeaveCriticalSection(&session->lock);
3069 return S_OK;
3072 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3073 IChannelAudioVolume *iface, UINT32 count, float *levels)
3075 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3076 AudioSession *session = This->session;
3077 int i;
3079 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3081 if(!levels)
3082 return NULL_PTR_ERR;
3084 if(count != session->channel_count)
3085 return E_INVALIDARG;
3087 for(i = 0; i < count; ++i)
3088 levels[i] = session->channel_vols[i];
3090 return S_OK;
3093 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3095 ChannelAudioVolume_QueryInterface,
3096 ChannelAudioVolume_AddRef,
3097 ChannelAudioVolume_Release,
3098 ChannelAudioVolume_GetChannelCount,
3099 ChannelAudioVolume_SetChannelVolume,
3100 ChannelAudioVolume_GetChannelVolume,
3101 ChannelAudioVolume_SetAllVolumes,
3102 ChannelAudioVolume_GetAllVolumes
3105 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3106 REFIID riid, void **ppv)
3108 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3110 if(!ppv)
3111 return E_POINTER;
3112 *ppv = NULL;
3114 if(IsEqualIID(riid, &IID_IUnknown) ||
3115 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3116 IsEqualIID(riid, &IID_IAudioSessionManager2))
3117 *ppv = iface;
3118 if(*ppv){
3119 IUnknown_AddRef((IUnknown*)*ppv);
3120 return S_OK;
3123 WARN("Unknown interface %s\n", debugstr_guid(riid));
3124 return E_NOINTERFACE;
3127 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3129 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3130 ULONG ref;
3131 ref = InterlockedIncrement(&This->ref);
3132 TRACE("(%p) Refcount now %u\n", This, ref);
3133 return ref;
3136 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3138 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3139 ULONG ref;
3140 ref = InterlockedDecrement(&This->ref);
3141 TRACE("(%p) Refcount now %u\n", This, ref);
3142 if(!ref)
3143 HeapFree(GetProcessHeap(), 0, This);
3144 return ref;
3147 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3148 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3149 IAudioSessionControl **out)
3151 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3152 AudioSession *session;
3153 AudioSessionWrapper *wrapper;
3154 HRESULT hr;
3156 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3157 flags, out);
3159 hr = get_audio_session(session_guid, This->device, 0, &session);
3160 if(FAILED(hr))
3161 return hr;
3163 wrapper = AudioSessionWrapper_Create(NULL);
3164 if(!wrapper)
3165 return E_OUTOFMEMORY;
3167 wrapper->session = session;
3169 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3171 return S_OK;
3174 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3175 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3176 ISimpleAudioVolume **out)
3178 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3179 AudioSession *session;
3180 AudioSessionWrapper *wrapper;
3181 HRESULT hr;
3183 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3184 flags, out);
3186 hr = get_audio_session(session_guid, This->device, 0, &session);
3187 if(FAILED(hr))
3188 return hr;
3190 wrapper = AudioSessionWrapper_Create(NULL);
3191 if(!wrapper)
3192 return E_OUTOFMEMORY;
3194 wrapper->session = session;
3196 *out = &wrapper->ISimpleAudioVolume_iface;
3198 return S_OK;
3201 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3202 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3204 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3205 FIXME("(%p)->(%p) - stub\n", This, out);
3206 return E_NOTIMPL;
3209 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3210 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3212 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3213 FIXME("(%p)->(%p) - stub\n", This, notification);
3214 return E_NOTIMPL;
3217 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3218 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3220 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3221 FIXME("(%p)->(%p) - stub\n", This, notification);
3222 return E_NOTIMPL;
3225 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3226 IAudioSessionManager2 *iface, const WCHAR *session_id,
3227 IAudioVolumeDuckNotification *notification)
3229 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3230 FIXME("(%p)->(%p) - stub\n", This, notification);
3231 return E_NOTIMPL;
3234 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3235 IAudioSessionManager2 *iface,
3236 IAudioVolumeDuckNotification *notification)
3238 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3239 FIXME("(%p)->(%p) - stub\n", This, notification);
3240 return E_NOTIMPL;
3243 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3245 AudioSessionManager_QueryInterface,
3246 AudioSessionManager_AddRef,
3247 AudioSessionManager_Release,
3248 AudioSessionManager_GetAudioSessionControl,
3249 AudioSessionManager_GetSimpleAudioVolume,
3250 AudioSessionManager_GetSessionEnumerator,
3251 AudioSessionManager_RegisterSessionNotification,
3252 AudioSessionManager_UnregisterSessionNotification,
3253 AudioSessionManager_RegisterDuckNotification,
3254 AudioSessionManager_UnregisterDuckNotification
3257 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3258 IAudioSessionManager2 **out)
3260 SessionMgr *This;
3262 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3263 if(!This)
3264 return E_OUTOFMEMORY;
3266 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3267 This->device = device;
3268 This->ref = 1;
3270 *out = &This->IAudioSessionManager2_iface;
3272 return S_OK;