kernel32/tests: Add a test to check some fields in fake dlls.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobfa3f885ea1a16dcc7c4d44a507b05154dbbf763b
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,
609 (IUnknown **)&This->pUnkFTMarshal);
610 if (FAILED(hr)) {
611 HeapFree(GetProcessHeap(), 0, This);
612 return hr;
615 if(oss_dev->flow == eRender)
616 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
617 else if(oss_dev->flow == eCapture)
618 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
619 else{
620 HeapFree(GetProcessHeap(), 0, This);
621 return E_INVALIDARG;
623 if(This->fd < 0){
624 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
625 strerror(errno));
626 HeapFree(GetProcessHeap(), 0, This);
627 return AUDCLNT_E_DEVICE_INVALIDATED;
630 This->dataflow = oss_dev->flow;
632 This->ai.dev = -1;
633 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
634 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
635 errno, strerror(errno));
636 close(This->fd);
637 HeapFree(GetProcessHeap(), 0, This);
638 return E_FAIL;
641 strcpy(This->devnode, oss_dev->devnode);
643 TRACE("OSS audioinfo:\n");
644 TRACE("devnode: %s\n", This->ai.devnode);
645 TRACE("name: %s\n", This->ai.name);
646 TRACE("busy: %x\n", This->ai.busy);
647 TRACE("caps: %x\n", This->ai.caps);
648 TRACE("iformats: %x\n", This->ai.iformats);
649 TRACE("oformats: %x\n", This->ai.oformats);
650 TRACE("enabled: %d\n", This->ai.enabled);
651 TRACE("min_rate: %d\n", This->ai.min_rate);
652 TRACE("max_rate: %d\n", This->ai.max_rate);
653 TRACE("min_channels: %d\n", This->ai.min_channels);
654 TRACE("max_channels: %d\n", This->ai.max_channels);
656 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
657 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
658 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
659 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
660 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
661 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
663 InitializeCriticalSection(&This->lock);
664 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
666 This->parent = dev;
667 IMMDevice_AddRef(This->parent);
669 IAudioClient_AddRef(&This->IAudioClient_iface);
671 *out = &This->IAudioClient_iface;
673 return S_OK;
676 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
677 REFIID riid, void **ppv)
679 ACImpl *This = impl_from_IAudioClient(iface);
680 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
682 if(!ppv)
683 return E_POINTER;
684 *ppv = NULL;
685 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
686 *ppv = iface;
687 else if(IsEqualIID(riid, &IID_IMarshal))
688 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
689 if(*ppv){
690 IUnknown_AddRef((IUnknown*)*ppv);
691 return S_OK;
693 WARN("Unknown interface %s\n", debugstr_guid(riid));
694 return E_NOINTERFACE;
697 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
699 ACImpl *This = impl_from_IAudioClient(iface);
700 ULONG ref;
701 ref = InterlockedIncrement(&This->ref);
702 TRACE("(%p) Refcount now %u\n", This, ref);
703 return ref;
706 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
708 ACImpl *This = impl_from_IAudioClient(iface);
709 ULONG ref;
711 ref = InterlockedDecrement(&This->ref);
712 TRACE("(%p) Refcount now %u\n", This, ref);
713 if(!ref){
714 if(This->timer){
715 HANDLE event;
716 DWORD wait;
717 event = CreateEventW(NULL, TRUE, FALSE, NULL);
718 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
719 wait = wait && GetLastError() == ERROR_IO_PENDING;
720 if(event && wait)
721 WaitForSingleObject(event, INFINITE);
722 CloseHandle(event);
725 IAudioClient_Stop(iface);
726 IMMDevice_Release(This->parent);
727 IUnknown_Release(This->pUnkFTMarshal);
728 This->lock.DebugInfo->Spare[0] = 0;
729 DeleteCriticalSection(&This->lock);
730 close(This->fd);
731 if(This->initted){
732 EnterCriticalSection(&g_sessions_lock);
733 list_remove(&This->entry);
734 LeaveCriticalSection(&g_sessions_lock);
736 HeapFree(GetProcessHeap(), 0, This->vols);
737 HeapFree(GetProcessHeap(), 0, This->local_buffer);
738 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
739 CoTaskMemFree(This->fmt);
740 HeapFree(GetProcessHeap(), 0, This);
742 return ref;
745 static void dump_fmt(const WAVEFORMATEX *fmt)
747 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
748 switch(fmt->wFormatTag){
749 case WAVE_FORMAT_PCM:
750 TRACE("WAVE_FORMAT_PCM");
751 break;
752 case WAVE_FORMAT_IEEE_FLOAT:
753 TRACE("WAVE_FORMAT_IEEE_FLOAT");
754 break;
755 case WAVE_FORMAT_EXTENSIBLE:
756 TRACE("WAVE_FORMAT_EXTENSIBLE");
757 break;
758 default:
759 TRACE("Unknown");
760 break;
762 TRACE(")\n");
764 TRACE("nChannels: %u\n", fmt->nChannels);
765 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
766 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
767 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
768 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
769 TRACE("cbSize: %u\n", fmt->cbSize);
771 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
772 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
773 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
774 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
775 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
779 static DWORD get_channel_mask(unsigned int channels)
781 switch(channels){
782 case 0:
783 return 0;
784 case 1:
785 return KSAUDIO_SPEAKER_MONO;
786 case 2:
787 return KSAUDIO_SPEAKER_STEREO;
788 case 3:
789 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
790 case 4:
791 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
792 case 5:
793 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
794 case 6:
795 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
796 case 7:
797 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
798 case 8:
799 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
801 FIXME("Unknown speaker configuration: %u\n", channels);
802 return 0;
805 static int get_oss_format(const WAVEFORMATEX *fmt)
807 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
809 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
810 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
811 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
812 switch(fmt->wBitsPerSample){
813 case 8:
814 return AFMT_U8;
815 case 16:
816 return AFMT_S16_LE;
817 case 24:
818 return AFMT_S24_LE;
819 case 32:
820 return AFMT_S32_LE;
822 return -1;
825 #ifdef AFMT_FLOAT
826 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
827 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
828 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
829 if(fmt->wBitsPerSample != 32)
830 return -1;
832 return AFMT_FLOAT;
834 #endif
836 return -1;
839 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
841 WAVEFORMATEX *ret;
842 size_t size;
844 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
845 size = sizeof(WAVEFORMATEXTENSIBLE);
846 else
847 size = sizeof(WAVEFORMATEX);
849 ret = CoTaskMemAlloc(size);
850 if(!ret)
851 return NULL;
853 memcpy(ret, fmt, size);
855 ret->cbSize = size - sizeof(WAVEFORMATEX);
857 return ret;
860 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
861 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
863 int tmp, oss_format;
864 double tenth;
865 HRESULT ret = S_OK;
866 WAVEFORMATEX *closest = NULL;
868 tmp = oss_format = get_oss_format(fmt);
869 if(oss_format < 0)
870 return AUDCLNT_E_UNSUPPORTED_FORMAT;
871 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
872 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
873 return E_FAIL;
875 if(tmp != oss_format){
876 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
877 return AUDCLNT_E_UNSUPPORTED_FORMAT;
880 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
881 (fmt->nAvgBytesPerSec == 0 ||
882 fmt->nBlockAlign == 0 ||
883 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
884 return E_INVALIDARG;
886 if(fmt->nChannels == 0)
887 return AUDCLNT_E_UNSUPPORTED_FORMAT;
889 closest = clone_format(fmt);
890 if(!closest)
891 return E_OUTOFMEMORY;
893 tmp = fmt->nSamplesPerSec;
894 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
895 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
896 CoTaskMemFree(closest);
897 return E_FAIL;
899 tenth = fmt->nSamplesPerSec * 0.1;
900 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
901 ret = S_FALSE;
902 closest->nSamplesPerSec = tmp;
905 tmp = fmt->nChannels;
906 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
907 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
908 CoTaskMemFree(closest);
909 return E_FAIL;
911 if(tmp != fmt->nChannels){
912 ret = S_FALSE;
913 closest->nChannels = tmp;
916 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
917 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
919 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
920 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
921 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
922 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
923 ret = S_FALSE;
925 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
926 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
927 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
928 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
929 ret = S_FALSE;
932 if(ret == S_FALSE && !out)
933 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
935 if(ret == S_FALSE && out){
936 closest->nBlockAlign =
937 closest->nChannels * closest->wBitsPerSample / 8;
938 closest->nAvgBytesPerSec =
939 closest->nBlockAlign * closest->nSamplesPerSec;
940 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
941 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
942 *out = closest;
943 } else
944 CoTaskMemFree(closest);
946 TRACE("returning: %08x\n", ret);
947 return ret;
950 static void session_init_vols(AudioSession *session, UINT channels)
952 if(session->channel_count < channels){
953 UINT i;
955 if(session->channel_vols)
956 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
957 session->channel_vols, sizeof(float) * channels);
958 else
959 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
960 sizeof(float) * channels);
961 if(!session->channel_vols)
962 return;
964 for(i = session->channel_count; i < channels; ++i)
965 session->channel_vols[i] = 1.f;
967 session->channel_count = channels;
971 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
972 UINT num_channels)
974 AudioSession *ret;
976 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
977 if(!ret)
978 return NULL;
980 memcpy(&ret->guid, guid, sizeof(GUID));
982 ret->device = device;
984 list_init(&ret->clients);
986 list_add_head(&g_sessions, &ret->entry);
988 InitializeCriticalSection(&ret->lock);
989 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
991 session_init_vols(ret, num_channels);
993 ret->master_vol = 1.f;
995 return ret;
998 /* if channels == 0, then this will return or create a session with
999 * matching dataflow and GUID. otherwise, channels must also match */
1000 static HRESULT get_audio_session(const GUID *sessionguid,
1001 IMMDevice *device, UINT channels, AudioSession **out)
1003 AudioSession *session;
1005 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1006 *out = create_session(&GUID_NULL, device, channels);
1007 if(!*out)
1008 return E_OUTOFMEMORY;
1010 return S_OK;
1013 *out = NULL;
1014 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1015 if(session->device == device &&
1016 IsEqualGUID(sessionguid, &session->guid)){
1017 session_init_vols(session, channels);
1018 *out = session;
1019 break;
1023 if(!*out){
1024 *out = create_session(sessionguid, device, channels);
1025 if(!*out)
1026 return E_OUTOFMEMORY;
1029 return S_OK;
1032 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1033 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1034 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1035 const GUID *sessionguid)
1037 ACImpl *This = impl_from_IAudioClient(iface);
1038 int i;
1039 HRESULT hr;
1041 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1042 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1044 if(!fmt)
1045 return E_POINTER;
1047 dump_fmt(fmt);
1049 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1050 return AUDCLNT_E_NOT_INITIALIZED;
1052 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1053 AUDCLNT_STREAMFLAGS_LOOPBACK |
1054 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1055 AUDCLNT_STREAMFLAGS_NOPERSIST |
1056 AUDCLNT_STREAMFLAGS_RATEADJUST |
1057 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1058 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1059 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1060 TRACE("Unknown flags: %08x\n", flags);
1061 return E_INVALIDARG;
1064 if(mode == AUDCLNT_SHAREMODE_SHARED){
1065 period = DefaultPeriod;
1066 if( duration < 3 * period)
1067 duration = 3 * period;
1068 }else{
1069 if(!period)
1070 period = DefaultPeriod; /* not minimum */
1071 if(period < MinimumPeriod || period > 5000000)
1072 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1073 if(duration > 20000000) /* the smaller the period, the lower this limit */
1074 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1075 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1076 if(duration != period)
1077 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1078 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1079 return AUDCLNT_E_DEVICE_IN_USE;
1080 }else{
1081 if( duration < 8 * period)
1082 duration = 8 * period; /* may grow above 2s */
1086 EnterCriticalSection(&This->lock);
1088 if(This->initted){
1089 LeaveCriticalSection(&This->lock);
1090 return AUDCLNT_E_ALREADY_INITIALIZED;
1093 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1094 if(FAILED(hr)){
1095 LeaveCriticalSection(&This->lock);
1096 return hr;
1099 This->fmt = clone_format(fmt);
1100 if(!This->fmt){
1101 LeaveCriticalSection(&This->lock);
1102 return E_OUTOFMEMORY;
1105 This->period_us = period / 10;
1106 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1108 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1109 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1110 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1111 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1112 This->bufsize_frames * fmt->nBlockAlign);
1113 if(!This->local_buffer){
1114 CoTaskMemFree(This->fmt);
1115 This->fmt = NULL;
1116 LeaveCriticalSection(&This->lock);
1117 return E_OUTOFMEMORY;
1120 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1121 if(!This->vols){
1122 CoTaskMemFree(This->fmt);
1123 This->fmt = NULL;
1124 LeaveCriticalSection(&This->lock);
1125 return E_OUTOFMEMORY;
1128 for(i = 0; i < fmt->nChannels; ++i)
1129 This->vols[i] = 1.f;
1131 This->share = mode;
1132 This->flags = flags;
1133 This->oss_bufsize_bytes = 0;
1135 EnterCriticalSection(&g_sessions_lock);
1137 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1138 &This->session);
1139 if(FAILED(hr)){
1140 LeaveCriticalSection(&g_sessions_lock);
1141 HeapFree(GetProcessHeap(), 0, This->vols);
1142 This->vols = NULL;
1143 CoTaskMemFree(This->fmt);
1144 This->fmt = NULL;
1145 LeaveCriticalSection(&This->lock);
1146 return hr;
1149 list_add_tail(&This->session->clients, &This->entry);
1151 LeaveCriticalSection(&g_sessions_lock);
1153 This->initted = TRUE;
1155 LeaveCriticalSection(&This->lock);
1157 return S_OK;
1160 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1161 UINT32 *frames)
1163 ACImpl *This = impl_from_IAudioClient(iface);
1165 TRACE("(%p)->(%p)\n", This, frames);
1167 if(!frames)
1168 return E_POINTER;
1170 EnterCriticalSection(&This->lock);
1172 if(!This->initted){
1173 LeaveCriticalSection(&This->lock);
1174 return AUDCLNT_E_NOT_INITIALIZED;
1177 *frames = This->bufsize_frames;
1179 TRACE("buffer size: %u\n", *frames);
1181 LeaveCriticalSection(&This->lock);
1183 return S_OK;
1186 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1187 REFERENCE_TIME *latency)
1189 ACImpl *This = impl_from_IAudioClient(iface);
1191 TRACE("(%p)->(%p)\n", This, latency);
1193 if(!latency)
1194 return E_POINTER;
1196 EnterCriticalSection(&This->lock);
1198 if(!This->initted){
1199 LeaveCriticalSection(&This->lock);
1200 return AUDCLNT_E_NOT_INITIALIZED;
1203 /* pretend we process audio in Period chunks, so max latency includes
1204 * the period time. Some native machines add .6666ms in shared mode. */
1205 *latency = (REFERENCE_TIME)This->period_us * 10 + 6666;
1207 LeaveCriticalSection(&This->lock);
1209 return S_OK;
1212 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1213 UINT32 *numpad)
1215 ACImpl *This = impl_from_IAudioClient(iface);
1217 TRACE("(%p)->(%p)\n", This, numpad);
1219 if(!numpad)
1220 return E_POINTER;
1222 EnterCriticalSection(&This->lock);
1224 if(!This->initted){
1225 LeaveCriticalSection(&This->lock);
1226 return AUDCLNT_E_NOT_INITIALIZED;
1229 *numpad = This->held_frames;
1231 TRACE("padding: %u\n", *numpad);
1233 LeaveCriticalSection(&This->lock);
1235 return S_OK;
1238 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1239 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1240 WAVEFORMATEX **outpwfx)
1242 ACImpl *This = impl_from_IAudioClient(iface);
1243 int fd = -1;
1244 HRESULT ret;
1246 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1248 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1249 return E_POINTER;
1251 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1252 return E_INVALIDARG;
1254 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1255 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1256 return E_INVALIDARG;
1258 dump_fmt(pwfx);
1260 if(outpwfx){
1261 *outpwfx = NULL;
1262 if(mode != AUDCLNT_SHAREMODE_SHARED)
1263 outpwfx = NULL;
1266 if(This->dataflow == eRender)
1267 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1268 else if(This->dataflow == eCapture)
1269 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1271 if(fd < 0){
1272 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1273 strerror(errno));
1274 return AUDCLNT_E_DEVICE_INVALIDATED;
1277 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1279 close(fd);
1281 return ret;
1284 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1285 WAVEFORMATEX **pwfx)
1287 ACImpl *This = impl_from_IAudioClient(iface);
1288 WAVEFORMATEXTENSIBLE *fmt;
1289 int formats;
1291 TRACE("(%p)->(%p)\n", This, pwfx);
1293 if(!pwfx)
1294 return E_POINTER;
1295 *pwfx = NULL;
1297 if(This->dataflow == eRender)
1298 formats = This->ai.oformats;
1299 else if(This->dataflow == eCapture)
1300 formats = This->ai.iformats;
1301 else
1302 return E_UNEXPECTED;
1304 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1305 if(!fmt)
1306 return E_OUTOFMEMORY;
1308 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1309 if(formats & AFMT_S16_LE){
1310 fmt->Format.wBitsPerSample = 16;
1311 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1312 #ifdef AFMT_FLOAT
1313 }else if(formats & AFMT_FLOAT){
1314 fmt->Format.wBitsPerSample = 32;
1315 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1316 #endif
1317 }else if(formats & AFMT_U8){
1318 fmt->Format.wBitsPerSample = 8;
1319 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1320 }else if(formats & AFMT_S32_LE){
1321 fmt->Format.wBitsPerSample = 32;
1322 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1323 }else if(formats & AFMT_S24_LE){
1324 fmt->Format.wBitsPerSample = 24;
1325 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1326 }else{
1327 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1328 CoTaskMemFree(fmt);
1329 return E_FAIL;
1332 /* some OSS drivers are buggy, so set reasonable defaults if
1333 * the reported values seem wacky */
1334 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1335 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1336 fmt->Format.nChannels = 2;
1338 if(This->ai.max_rate == 0)
1339 fmt->Format.nSamplesPerSec = 44100;
1340 else
1341 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1342 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1343 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1345 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1347 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1348 fmt->Format.nChannels) / 8;
1349 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1350 fmt->Format.nBlockAlign;
1352 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1353 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1355 *pwfx = (WAVEFORMATEX*)fmt;
1356 dump_fmt(*pwfx);
1358 return S_OK;
1361 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1362 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1364 ACImpl *This = impl_from_IAudioClient(iface);
1366 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1368 if(!defperiod && !minperiod)
1369 return E_POINTER;
1371 if(defperiod)
1372 *defperiod = DefaultPeriod;
1373 if(minperiod)
1374 *minperiod = MinimumPeriod;
1376 return S_OK;
1379 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1381 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1382 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1383 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1384 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1385 This->fmt->wBitsPerSample == 8)
1386 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1387 else
1388 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1391 static void oss_write_data(ACImpl *This)
1393 ssize_t written_bytes;
1394 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1395 SIZE_T to_write_frames, to_write_bytes, advanced;
1396 audio_buf_info bi;
1397 BYTE *buf;
1399 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1400 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1401 return;
1404 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1406 if(bi.bytes > This->oss_bufsize_bytes){
1407 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1408 bi.bytes, This->oss_bufsize_bytes);
1409 This->oss_bufsize_bytes = bi.bytes;
1410 in_oss_frames = 0;
1411 }else
1412 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1414 if(in_oss_frames > This->in_oss_frames){
1415 TRACE("Capping reported frames from %u to %u\n",
1416 in_oss_frames, This->in_oss_frames);
1417 in_oss_frames = This->in_oss_frames;
1420 write_limit = 0;
1421 while(write_limit + in_oss_frames < max_period * 3)
1422 write_limit += max_period;
1423 if(write_limit == 0)
1424 return;
1426 /* vvvvvv - in_oss_frames
1427 * [--xxxxxxxxxx]
1428 * [xxxxxxxxxx--]
1429 * ^^^^^^^^^^ - held_frames
1430 * ^ - lcl_offs_frames
1432 advanced = This->in_oss_frames - in_oss_frames;
1433 if(advanced > This->held_frames)
1434 advanced = This->held_frames;
1435 This->lcl_offs_frames += advanced;
1436 This->lcl_offs_frames %= This->bufsize_frames;
1437 This->held_frames -= advanced;
1438 This->in_oss_frames = in_oss_frames;
1439 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
1440 advanced, This->lcl_offs_frames, This->held_frames, This->in_oss_frames);
1443 if(This->held_frames == This->in_oss_frames)
1444 return;
1446 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1447 new_frames = This->held_frames - This->in_oss_frames;
1449 if(write_offs_frames + new_frames > This->bufsize_frames)
1450 to_write_frames = This->bufsize_frames - write_offs_frames;
1451 else
1452 to_write_frames = new_frames;
1454 to_write_frames = min(to_write_frames, write_limit);
1455 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1456 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
1457 write_offs_frames, to_write_frames + write_offs_frames,
1458 This->bufsize_frames);
1460 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1462 if(This->session->mute)
1463 silence_buffer(This, buf, to_write_frames);
1465 written_bytes = write(This->fd, buf, to_write_bytes);
1466 if(written_bytes < 0){
1467 /* EAGAIN is OSS buffer full, log that too */
1468 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1469 return;
1471 written_frames = written_bytes / This->fmt->nBlockAlign;
1473 This->in_oss_frames += written_frames;
1475 if(written_frames < to_write_frames){
1476 /* OSS buffer probably full */
1477 return;
1480 if(new_frames > written_frames && written_frames < write_limit){
1481 /* wrapped and have some data back at the start to write */
1483 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1484 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1486 if(This->session->mute)
1487 silence_buffer(This, This->local_buffer, to_write_frames);
1489 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
1491 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1492 if(written_bytes < 0){
1493 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1494 return;
1496 written_frames = written_bytes / This->fmt->nBlockAlign;
1497 This->in_oss_frames += written_frames;
1501 static void oss_read_data(ACImpl *This)
1503 UINT64 pos, readable;
1504 ssize_t nread;
1506 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1507 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1509 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1510 readable);
1511 if(nread < 0){
1512 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1513 return;
1516 This->held_frames += nread / This->fmt->nBlockAlign;
1518 if(This->held_frames > This->bufsize_frames){
1519 WARN("Overflow of unread data\n");
1520 This->lcl_offs_frames += This->held_frames;
1521 This->lcl_offs_frames %= This->bufsize_frames;
1522 This->held_frames = This->bufsize_frames;
1526 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1528 ACImpl *This = user;
1530 EnterCriticalSection(&This->lock);
1532 if(This->playing){
1533 if(This->dataflow == eRender && This->held_frames)
1534 oss_write_data(This);
1535 else if(This->dataflow == eCapture)
1536 oss_read_data(This);
1539 LeaveCriticalSection(&This->lock);
1541 if(This->event)
1542 SetEvent(This->event);
1545 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1547 ACImpl *This = impl_from_IAudioClient(iface);
1549 TRACE("(%p)\n", This);
1551 EnterCriticalSection(&This->lock);
1553 if(!This->initted){
1554 LeaveCriticalSection(&This->lock);
1555 return AUDCLNT_E_NOT_INITIALIZED;
1558 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1559 LeaveCriticalSection(&This->lock);
1560 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1563 if(This->playing){
1564 LeaveCriticalSection(&This->lock);
1565 return AUDCLNT_E_NOT_STOPPED;
1568 if(!This->timer){
1569 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1570 oss_period_callback, This, 0, This->period_us / 1000,
1571 WT_EXECUTEINTIMERTHREAD))
1572 ERR("Unable to create period timer: %u\n", GetLastError());
1575 This->playing = TRUE;
1577 LeaveCriticalSection(&This->lock);
1579 return S_OK;
1582 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1584 ACImpl *This = impl_from_IAudioClient(iface);
1586 TRACE("(%p)\n", This);
1588 EnterCriticalSection(&This->lock);
1590 if(!This->initted){
1591 LeaveCriticalSection(&This->lock);
1592 return AUDCLNT_E_NOT_INITIALIZED;
1595 if(!This->playing){
1596 LeaveCriticalSection(&This->lock);
1597 return S_FALSE;
1600 This->playing = FALSE;
1601 This->in_oss_frames = 0;
1603 LeaveCriticalSection(&This->lock);
1605 return S_OK;
1608 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1610 ACImpl *This = impl_from_IAudioClient(iface);
1612 TRACE("(%p)\n", This);
1614 EnterCriticalSection(&This->lock);
1616 if(!This->initted){
1617 LeaveCriticalSection(&This->lock);
1618 return AUDCLNT_E_NOT_INITIALIZED;
1621 if(This->playing){
1622 LeaveCriticalSection(&This->lock);
1623 return AUDCLNT_E_NOT_STOPPED;
1626 if(This->getbuf_last){
1627 LeaveCriticalSection(&This->lock);
1628 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1631 if(This->dataflow == eRender){
1632 This->written_frames = 0;
1633 This->last_pos_frames = 0;
1634 }else{
1635 This->written_frames += This->held_frames;
1637 This->held_frames = 0;
1638 This->lcl_offs_frames = 0;
1639 This->in_oss_frames = 0;
1641 LeaveCriticalSection(&This->lock);
1643 return S_OK;
1646 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1647 HANDLE event)
1649 ACImpl *This = impl_from_IAudioClient(iface);
1651 TRACE("(%p)->(%p)\n", This, event);
1653 if(!event)
1654 return E_INVALIDARG;
1656 EnterCriticalSection(&This->lock);
1658 if(!This->initted){
1659 LeaveCriticalSection(&This->lock);
1660 return AUDCLNT_E_NOT_INITIALIZED;
1663 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1664 LeaveCriticalSection(&This->lock);
1665 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1668 if (This->event){
1669 LeaveCriticalSection(&This->lock);
1670 FIXME("called twice\n");
1671 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1674 This->event = event;
1676 LeaveCriticalSection(&This->lock);
1678 return S_OK;
1681 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1682 void **ppv)
1684 ACImpl *This = impl_from_IAudioClient(iface);
1686 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1688 if(!ppv)
1689 return E_POINTER;
1690 *ppv = NULL;
1692 EnterCriticalSection(&This->lock);
1694 if(!This->initted){
1695 LeaveCriticalSection(&This->lock);
1696 return AUDCLNT_E_NOT_INITIALIZED;
1699 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1700 if(This->dataflow != eRender){
1701 LeaveCriticalSection(&This->lock);
1702 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1704 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1705 *ppv = &This->IAudioRenderClient_iface;
1706 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1707 if(This->dataflow != eCapture){
1708 LeaveCriticalSection(&This->lock);
1709 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1711 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1712 *ppv = &This->IAudioCaptureClient_iface;
1713 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1714 IAudioClock_AddRef(&This->IAudioClock_iface);
1715 *ppv = &This->IAudioClock_iface;
1716 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1717 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1718 *ppv = &This->IAudioStreamVolume_iface;
1719 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1720 if(!This->session_wrapper){
1721 This->session_wrapper = AudioSessionWrapper_Create(This);
1722 if(!This->session_wrapper){
1723 LeaveCriticalSection(&This->lock);
1724 return E_OUTOFMEMORY;
1726 }else
1727 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1729 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1730 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1731 if(!This->session_wrapper){
1732 This->session_wrapper = AudioSessionWrapper_Create(This);
1733 if(!This->session_wrapper){
1734 LeaveCriticalSection(&This->lock);
1735 return E_OUTOFMEMORY;
1737 }else
1738 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1740 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1741 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1742 if(!This->session_wrapper){
1743 This->session_wrapper = AudioSessionWrapper_Create(This);
1744 if(!This->session_wrapper){
1745 LeaveCriticalSection(&This->lock);
1746 return E_OUTOFMEMORY;
1748 }else
1749 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1751 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1754 if(*ppv){
1755 LeaveCriticalSection(&This->lock);
1756 return S_OK;
1759 LeaveCriticalSection(&This->lock);
1761 FIXME("stub %s\n", debugstr_guid(riid));
1762 return E_NOINTERFACE;
1765 static const IAudioClientVtbl AudioClient_Vtbl =
1767 AudioClient_QueryInterface,
1768 AudioClient_AddRef,
1769 AudioClient_Release,
1770 AudioClient_Initialize,
1771 AudioClient_GetBufferSize,
1772 AudioClient_GetStreamLatency,
1773 AudioClient_GetCurrentPadding,
1774 AudioClient_IsFormatSupported,
1775 AudioClient_GetMixFormat,
1776 AudioClient_GetDevicePeriod,
1777 AudioClient_Start,
1778 AudioClient_Stop,
1779 AudioClient_Reset,
1780 AudioClient_SetEventHandle,
1781 AudioClient_GetService
1784 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1785 IAudioRenderClient *iface, REFIID riid, void **ppv)
1787 ACImpl *This = impl_from_IAudioRenderClient(iface);
1788 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1790 if(!ppv)
1791 return E_POINTER;
1792 *ppv = NULL;
1794 if(IsEqualIID(riid, &IID_IUnknown) ||
1795 IsEqualIID(riid, &IID_IAudioRenderClient))
1796 *ppv = iface;
1797 else if(IsEqualIID(riid, &IID_IMarshal))
1798 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1799 if(*ppv){
1800 IUnknown_AddRef((IUnknown*)*ppv);
1801 return S_OK;
1804 WARN("Unknown interface %s\n", debugstr_guid(riid));
1805 return E_NOINTERFACE;
1808 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1810 ACImpl *This = impl_from_IAudioRenderClient(iface);
1811 return AudioClient_AddRef(&This->IAudioClient_iface);
1814 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1816 ACImpl *This = impl_from_IAudioRenderClient(iface);
1817 return AudioClient_Release(&This->IAudioClient_iface);
1820 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1821 UINT32 frames, BYTE **data)
1823 ACImpl *This = impl_from_IAudioRenderClient(iface);
1824 UINT32 write_pos;
1826 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1828 if(!data)
1829 return E_POINTER;
1831 *data = NULL;
1833 EnterCriticalSection(&This->lock);
1835 if(This->getbuf_last){
1836 LeaveCriticalSection(&This->lock);
1837 return AUDCLNT_E_OUT_OF_ORDER;
1840 if(!frames){
1841 LeaveCriticalSection(&This->lock);
1842 return S_OK;
1845 if(This->held_frames + frames > This->bufsize_frames){
1846 LeaveCriticalSection(&This->lock);
1847 return AUDCLNT_E_BUFFER_TOO_LARGE;
1850 write_pos =
1851 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1852 if(write_pos + frames > This->bufsize_frames){
1853 if(This->tmp_buffer_frames < frames){
1854 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1855 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1856 frames * This->fmt->nBlockAlign);
1857 if(!This->tmp_buffer){
1858 LeaveCriticalSection(&This->lock);
1859 return E_OUTOFMEMORY;
1861 This->tmp_buffer_frames = frames;
1863 *data = This->tmp_buffer;
1864 This->getbuf_last = -frames;
1865 }else{
1866 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1867 This->getbuf_last = frames;
1870 silence_buffer(This, *data, frames);
1872 LeaveCriticalSection(&This->lock);
1874 return S_OK;
1877 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1879 UINT32 write_offs_frames =
1880 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1881 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1882 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1883 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1884 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1886 if(written_bytes <= chunk_bytes){
1887 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1888 }else{
1889 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1890 memcpy(This->local_buffer, buffer + chunk_bytes,
1891 written_bytes - chunk_bytes);
1895 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1896 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1898 ACImpl *This = impl_from_IAudioRenderClient(iface);
1899 BYTE *buffer;
1901 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1903 EnterCriticalSection(&This->lock);
1905 if(!written_frames){
1906 This->getbuf_last = 0;
1907 LeaveCriticalSection(&This->lock);
1908 return S_OK;
1911 if(!This->getbuf_last){
1912 LeaveCriticalSection(&This->lock);
1913 return AUDCLNT_E_OUT_OF_ORDER;
1916 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1917 LeaveCriticalSection(&This->lock);
1918 return AUDCLNT_E_INVALID_SIZE;
1921 if(This->getbuf_last >= 0)
1922 buffer = This->local_buffer + This->fmt->nBlockAlign *
1923 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1924 else
1925 buffer = This->tmp_buffer;
1927 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1928 silence_buffer(This, buffer, written_frames);
1930 if(This->getbuf_last < 0)
1931 oss_wrap_buffer(This, buffer, written_frames);
1933 This->held_frames += written_frames;
1934 This->written_frames += written_frames;
1935 This->getbuf_last = 0;
1937 LeaveCriticalSection(&This->lock);
1939 return S_OK;
1942 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1943 AudioRenderClient_QueryInterface,
1944 AudioRenderClient_AddRef,
1945 AudioRenderClient_Release,
1946 AudioRenderClient_GetBuffer,
1947 AudioRenderClient_ReleaseBuffer
1950 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1951 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1953 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1954 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1956 if(!ppv)
1957 return E_POINTER;
1958 *ppv = NULL;
1960 if(IsEqualIID(riid, &IID_IUnknown) ||
1961 IsEqualIID(riid, &IID_IAudioCaptureClient))
1962 *ppv = iface;
1963 else if(IsEqualIID(riid, &IID_IMarshal))
1964 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1965 if(*ppv){
1966 IUnknown_AddRef((IUnknown*)*ppv);
1967 return S_OK;
1970 WARN("Unknown interface %s\n", debugstr_guid(riid));
1971 return E_NOINTERFACE;
1974 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1976 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1977 return IAudioClient_AddRef(&This->IAudioClient_iface);
1980 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1982 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1983 return IAudioClient_Release(&This->IAudioClient_iface);
1986 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1987 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1988 UINT64 *qpcpos)
1990 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1992 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1993 devpos, qpcpos);
1995 if(!data || !frames || !flags)
1996 return E_POINTER;
1998 EnterCriticalSection(&This->lock);
2000 if(This->getbuf_last){
2001 LeaveCriticalSection(&This->lock);
2002 return AUDCLNT_E_OUT_OF_ORDER;
2005 if(This->held_frames < This->period_frames){
2006 *frames = 0;
2007 LeaveCriticalSection(&This->lock);
2008 return AUDCLNT_S_BUFFER_EMPTY;
2011 *flags = 0;
2013 *frames = This->period_frames;
2015 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2016 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2017 if(This->tmp_buffer_frames < *frames){
2018 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2019 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2020 *frames * This->fmt->nBlockAlign);
2021 if(!This->tmp_buffer){
2022 LeaveCriticalSection(&This->lock);
2023 return E_OUTOFMEMORY;
2025 This->tmp_buffer_frames = *frames;
2028 *data = This->tmp_buffer;
2029 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2030 This->fmt->nBlockAlign;
2031 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2032 frames_bytes = *frames * This->fmt->nBlockAlign;
2033 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2034 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2035 frames_bytes - chunk_bytes);
2036 }else
2037 *data = This->local_buffer +
2038 This->lcl_offs_frames * This->fmt->nBlockAlign;
2040 This->getbuf_last = *frames;
2042 if(devpos)
2043 *devpos = This->written_frames;
2044 if(qpcpos){
2045 LARGE_INTEGER stamp, freq;
2046 QueryPerformanceCounter(&stamp);
2047 QueryPerformanceFrequency(&freq);
2048 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2051 LeaveCriticalSection(&This->lock);
2053 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2056 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2057 IAudioCaptureClient *iface, UINT32 done)
2059 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2061 TRACE("(%p)->(%u)\n", This, done);
2063 EnterCriticalSection(&This->lock);
2065 if(!done){
2066 This->getbuf_last = 0;
2067 LeaveCriticalSection(&This->lock);
2068 return S_OK;
2071 if(!This->getbuf_last){
2072 LeaveCriticalSection(&This->lock);
2073 return AUDCLNT_E_OUT_OF_ORDER;
2076 if(This->getbuf_last != done){
2077 LeaveCriticalSection(&This->lock);
2078 return AUDCLNT_E_INVALID_SIZE;
2081 This->written_frames += done;
2082 This->held_frames -= done;
2083 This->lcl_offs_frames += done;
2084 This->lcl_offs_frames %= This->bufsize_frames;
2085 This->getbuf_last = 0;
2087 LeaveCriticalSection(&This->lock);
2089 return S_OK;
2092 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2093 IAudioCaptureClient *iface, UINT32 *frames)
2095 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2097 TRACE("(%p)->(%p)\n", This, frames);
2099 if(!frames)
2100 return E_POINTER;
2102 EnterCriticalSection(&This->lock);
2104 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2106 LeaveCriticalSection(&This->lock);
2108 return S_OK;
2111 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2113 AudioCaptureClient_QueryInterface,
2114 AudioCaptureClient_AddRef,
2115 AudioCaptureClient_Release,
2116 AudioCaptureClient_GetBuffer,
2117 AudioCaptureClient_ReleaseBuffer,
2118 AudioCaptureClient_GetNextPacketSize
2121 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2122 REFIID riid, void **ppv)
2124 ACImpl *This = impl_from_IAudioClock(iface);
2126 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2128 if(!ppv)
2129 return E_POINTER;
2130 *ppv = NULL;
2132 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2133 *ppv = iface;
2134 else if(IsEqualIID(riid, &IID_IAudioClock2))
2135 *ppv = &This->IAudioClock2_iface;
2136 if(*ppv){
2137 IUnknown_AddRef((IUnknown*)*ppv);
2138 return S_OK;
2141 WARN("Unknown interface %s\n", debugstr_guid(riid));
2142 return E_NOINTERFACE;
2145 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2147 ACImpl *This = impl_from_IAudioClock(iface);
2148 return IAudioClient_AddRef(&This->IAudioClient_iface);
2151 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2153 ACImpl *This = impl_from_IAudioClock(iface);
2154 return IAudioClient_Release(&This->IAudioClient_iface);
2157 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2159 ACImpl *This = impl_from_IAudioClock(iface);
2161 TRACE("(%p)->(%p)\n", This, freq);
2163 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2164 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2165 else
2166 *freq = This->fmt->nSamplesPerSec;
2168 return S_OK;
2171 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2172 UINT64 *qpctime)
2174 ACImpl *This = impl_from_IAudioClock(iface);
2176 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2178 if(!pos)
2179 return E_POINTER;
2181 EnterCriticalSection(&This->lock);
2183 if(This->dataflow == eRender){
2184 *pos = This->written_frames - This->held_frames;
2185 if(*pos < This->last_pos_frames)
2186 *pos = This->last_pos_frames;
2187 }else if(This->dataflow == eCapture){
2188 audio_buf_info bi;
2189 UINT32 held;
2191 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2192 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2193 held = 0;
2194 }else{
2195 if(bi.bytes <= bi.fragsize)
2196 held = 0;
2197 else
2198 held = bi.bytes / This->fmt->nBlockAlign;
2201 *pos = This->written_frames + held;
2204 This->last_pos_frames = *pos;
2206 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2207 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2208 *pos *= This->fmt->nBlockAlign;
2210 LeaveCriticalSection(&This->lock);
2212 if(qpctime){
2213 LARGE_INTEGER stamp, freq;
2214 QueryPerformanceCounter(&stamp);
2215 QueryPerformanceFrequency(&freq);
2216 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2219 return S_OK;
2222 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2223 DWORD *chars)
2225 ACImpl *This = impl_from_IAudioClock(iface);
2227 TRACE("(%p)->(%p)\n", This, chars);
2229 if(!chars)
2230 return E_POINTER;
2232 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2234 return S_OK;
2237 static const IAudioClockVtbl AudioClock_Vtbl =
2239 AudioClock_QueryInterface,
2240 AudioClock_AddRef,
2241 AudioClock_Release,
2242 AudioClock_GetFrequency,
2243 AudioClock_GetPosition,
2244 AudioClock_GetCharacteristics
2247 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2248 REFIID riid, void **ppv)
2250 ACImpl *This = impl_from_IAudioClock2(iface);
2251 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2254 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2256 ACImpl *This = impl_from_IAudioClock2(iface);
2257 return IAudioClient_AddRef(&This->IAudioClient_iface);
2260 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2262 ACImpl *This = impl_from_IAudioClock2(iface);
2263 return IAudioClient_Release(&This->IAudioClient_iface);
2266 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2267 UINT64 *pos, UINT64 *qpctime)
2269 ACImpl *This = impl_from_IAudioClock2(iface);
2271 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2273 return E_NOTIMPL;
2276 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2278 AudioClock2_QueryInterface,
2279 AudioClock2_AddRef,
2280 AudioClock2_Release,
2281 AudioClock2_GetDevicePosition
2284 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2286 AudioSessionWrapper *ret;
2288 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2289 sizeof(AudioSessionWrapper));
2290 if(!ret)
2291 return NULL;
2293 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2294 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2295 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2297 ret->ref = 1;
2299 ret->client = client;
2300 if(client){
2301 ret->session = client->session;
2302 AudioClient_AddRef(&client->IAudioClient_iface);
2305 return ret;
2308 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2309 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2311 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2313 if(!ppv)
2314 return E_POINTER;
2315 *ppv = NULL;
2317 if(IsEqualIID(riid, &IID_IUnknown) ||
2318 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2319 IsEqualIID(riid, &IID_IAudioSessionControl2))
2320 *ppv = iface;
2321 if(*ppv){
2322 IUnknown_AddRef((IUnknown*)*ppv);
2323 return S_OK;
2326 WARN("Unknown interface %s\n", debugstr_guid(riid));
2327 return E_NOINTERFACE;
2330 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2332 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2333 ULONG ref;
2334 ref = InterlockedIncrement(&This->ref);
2335 TRACE("(%p) Refcount now %u\n", This, ref);
2336 return ref;
2339 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2341 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2342 ULONG ref;
2343 ref = InterlockedDecrement(&This->ref);
2344 TRACE("(%p) Refcount now %u\n", This, ref);
2345 if(!ref){
2346 if(This->client){
2347 EnterCriticalSection(&This->client->lock);
2348 This->client->session_wrapper = NULL;
2349 LeaveCriticalSection(&This->client->lock);
2350 AudioClient_Release(&This->client->IAudioClient_iface);
2352 HeapFree(GetProcessHeap(), 0, This);
2354 return ref;
2357 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2358 AudioSessionState *state)
2360 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2361 ACImpl *client;
2363 TRACE("(%p)->(%p)\n", This, state);
2365 if(!state)
2366 return NULL_PTR_ERR;
2368 EnterCriticalSection(&g_sessions_lock);
2370 if(list_empty(&This->session->clients)){
2371 *state = AudioSessionStateExpired;
2372 LeaveCriticalSection(&g_sessions_lock);
2373 return S_OK;
2376 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2377 EnterCriticalSection(&client->lock);
2378 if(client->playing){
2379 *state = AudioSessionStateActive;
2380 LeaveCriticalSection(&client->lock);
2381 LeaveCriticalSection(&g_sessions_lock);
2382 return S_OK;
2384 LeaveCriticalSection(&client->lock);
2387 LeaveCriticalSection(&g_sessions_lock);
2389 *state = AudioSessionStateInactive;
2391 return S_OK;
2394 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2395 IAudioSessionControl2 *iface, WCHAR **name)
2397 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2399 FIXME("(%p)->(%p) - stub\n", This, name);
2401 return E_NOTIMPL;
2404 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2405 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2407 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2409 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2411 return E_NOTIMPL;
2414 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2415 IAudioSessionControl2 *iface, WCHAR **path)
2417 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2419 FIXME("(%p)->(%p) - stub\n", This, path);
2421 return E_NOTIMPL;
2424 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2425 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2427 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2429 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2431 return E_NOTIMPL;
2434 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2435 IAudioSessionControl2 *iface, GUID *group)
2437 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2439 FIXME("(%p)->(%p) - stub\n", This, group);
2441 return E_NOTIMPL;
2444 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2445 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2447 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2449 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2450 debugstr_guid(session));
2452 return E_NOTIMPL;
2455 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2456 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2458 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2460 FIXME("(%p)->(%p) - stub\n", This, events);
2462 return S_OK;
2465 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2466 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2468 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2470 FIXME("(%p)->(%p) - stub\n", This, events);
2472 return S_OK;
2475 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2476 IAudioSessionControl2 *iface, WCHAR **id)
2478 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2480 FIXME("(%p)->(%p) - stub\n", This, id);
2482 return E_NOTIMPL;
2485 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2486 IAudioSessionControl2 *iface, WCHAR **id)
2488 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2490 FIXME("(%p)->(%p) - stub\n", This, id);
2492 return E_NOTIMPL;
2495 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2496 IAudioSessionControl2 *iface, DWORD *pid)
2498 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2500 TRACE("(%p)->(%p)\n", This, pid);
2502 if(!pid)
2503 return E_POINTER;
2505 *pid = GetCurrentProcessId();
2507 return S_OK;
2510 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2511 IAudioSessionControl2 *iface)
2513 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2515 TRACE("(%p)\n", This);
2517 return S_FALSE;
2520 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2521 IAudioSessionControl2 *iface, BOOL optout)
2523 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2525 TRACE("(%p)->(%d)\n", This, optout);
2527 return S_OK;
2530 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2532 AudioSessionControl_QueryInterface,
2533 AudioSessionControl_AddRef,
2534 AudioSessionControl_Release,
2535 AudioSessionControl_GetState,
2536 AudioSessionControl_GetDisplayName,
2537 AudioSessionControl_SetDisplayName,
2538 AudioSessionControl_GetIconPath,
2539 AudioSessionControl_SetIconPath,
2540 AudioSessionControl_GetGroupingParam,
2541 AudioSessionControl_SetGroupingParam,
2542 AudioSessionControl_RegisterAudioSessionNotification,
2543 AudioSessionControl_UnregisterAudioSessionNotification,
2544 AudioSessionControl_GetSessionIdentifier,
2545 AudioSessionControl_GetSessionInstanceIdentifier,
2546 AudioSessionControl_GetProcessId,
2547 AudioSessionControl_IsSystemSoundsSession,
2548 AudioSessionControl_SetDuckingPreference
2551 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2552 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2554 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2556 if(!ppv)
2557 return E_POINTER;
2558 *ppv = NULL;
2560 if(IsEqualIID(riid, &IID_IUnknown) ||
2561 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2562 *ppv = iface;
2563 if(*ppv){
2564 IUnknown_AddRef((IUnknown*)*ppv);
2565 return S_OK;
2568 WARN("Unknown interface %s\n", debugstr_guid(riid));
2569 return E_NOINTERFACE;
2572 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2574 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2575 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2578 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2580 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2581 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2584 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2585 ISimpleAudioVolume *iface, float level, const GUID *context)
2587 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2588 AudioSession *session = This->session;
2590 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2592 if(level < 0.f || level > 1.f)
2593 return E_INVALIDARG;
2595 if(context)
2596 FIXME("Notifications not supported yet\n");
2598 EnterCriticalSection(&session->lock);
2600 session->master_vol = level;
2602 TRACE("OSS doesn't support setting volume\n");
2604 LeaveCriticalSection(&session->lock);
2606 return S_OK;
2609 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2610 ISimpleAudioVolume *iface, float *level)
2612 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2613 AudioSession *session = This->session;
2615 TRACE("(%p)->(%p)\n", session, level);
2617 if(!level)
2618 return NULL_PTR_ERR;
2620 *level = session->master_vol;
2622 return S_OK;
2625 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2626 BOOL mute, const GUID *context)
2628 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2629 AudioSession *session = This->session;
2631 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2633 EnterCriticalSection(&session->lock);
2635 session->mute = mute;
2637 LeaveCriticalSection(&session->lock);
2639 return S_OK;
2642 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2643 BOOL *mute)
2645 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2646 AudioSession *session = This->session;
2648 TRACE("(%p)->(%p)\n", session, mute);
2650 if(!mute)
2651 return NULL_PTR_ERR;
2653 *mute = This->session->mute;
2655 return S_OK;
2658 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2660 SimpleAudioVolume_QueryInterface,
2661 SimpleAudioVolume_AddRef,
2662 SimpleAudioVolume_Release,
2663 SimpleAudioVolume_SetMasterVolume,
2664 SimpleAudioVolume_GetMasterVolume,
2665 SimpleAudioVolume_SetMute,
2666 SimpleAudioVolume_GetMute
2669 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2670 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2672 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2674 if(!ppv)
2675 return E_POINTER;
2676 *ppv = NULL;
2678 if(IsEqualIID(riid, &IID_IUnknown) ||
2679 IsEqualIID(riid, &IID_IAudioStreamVolume))
2680 *ppv = iface;
2681 if(*ppv){
2682 IUnknown_AddRef((IUnknown*)*ppv);
2683 return S_OK;
2686 WARN("Unknown interface %s\n", debugstr_guid(riid));
2687 return E_NOINTERFACE;
2690 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2692 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2693 return IAudioClient_AddRef(&This->IAudioClient_iface);
2696 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2698 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2699 return IAudioClient_Release(&This->IAudioClient_iface);
2702 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2703 IAudioStreamVolume *iface, UINT32 *out)
2705 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2707 TRACE("(%p)->(%p)\n", This, out);
2709 if(!out)
2710 return E_POINTER;
2712 *out = This->fmt->nChannels;
2714 return S_OK;
2717 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2718 IAudioStreamVolume *iface, UINT32 index, float level)
2720 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2722 TRACE("(%p)->(%d, %f)\n", This, index, level);
2724 if(level < 0.f || level > 1.f)
2725 return E_INVALIDARG;
2727 if(index >= This->fmt->nChannels)
2728 return E_INVALIDARG;
2730 EnterCriticalSection(&This->lock);
2732 This->vols[index] = level;
2734 TRACE("OSS doesn't support setting volume\n");
2736 LeaveCriticalSection(&This->lock);
2738 return S_OK;
2741 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2742 IAudioStreamVolume *iface, UINT32 index, float *level)
2744 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2746 TRACE("(%p)->(%d, %p)\n", This, index, level);
2748 if(!level)
2749 return E_POINTER;
2751 if(index >= This->fmt->nChannels)
2752 return E_INVALIDARG;
2754 *level = This->vols[index];
2756 return S_OK;
2759 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2760 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2762 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2763 int i;
2765 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2767 if(!levels)
2768 return E_POINTER;
2770 if(count != This->fmt->nChannels)
2771 return E_INVALIDARG;
2773 EnterCriticalSection(&This->lock);
2775 for(i = 0; i < count; ++i)
2776 This->vols[i] = levels[i];
2778 TRACE("OSS doesn't support setting volume\n");
2780 LeaveCriticalSection(&This->lock);
2782 return S_OK;
2785 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2786 IAudioStreamVolume *iface, UINT32 count, float *levels)
2788 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2789 int i;
2791 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2793 if(!levels)
2794 return E_POINTER;
2796 if(count != This->fmt->nChannels)
2797 return E_INVALIDARG;
2799 EnterCriticalSection(&This->lock);
2801 for(i = 0; i < count; ++i)
2802 levels[i] = This->vols[i];
2804 LeaveCriticalSection(&This->lock);
2806 return S_OK;
2809 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2811 AudioStreamVolume_QueryInterface,
2812 AudioStreamVolume_AddRef,
2813 AudioStreamVolume_Release,
2814 AudioStreamVolume_GetChannelCount,
2815 AudioStreamVolume_SetChannelVolume,
2816 AudioStreamVolume_GetChannelVolume,
2817 AudioStreamVolume_SetAllVolumes,
2818 AudioStreamVolume_GetAllVolumes
2821 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2822 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2824 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2826 if(!ppv)
2827 return E_POINTER;
2828 *ppv = NULL;
2830 if(IsEqualIID(riid, &IID_IUnknown) ||
2831 IsEqualIID(riid, &IID_IChannelAudioVolume))
2832 *ppv = iface;
2833 if(*ppv){
2834 IUnknown_AddRef((IUnknown*)*ppv);
2835 return S_OK;
2838 WARN("Unknown interface %s\n", debugstr_guid(riid));
2839 return E_NOINTERFACE;
2842 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2844 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2845 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2848 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2850 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2851 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2854 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2855 IChannelAudioVolume *iface, UINT32 *out)
2857 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2858 AudioSession *session = This->session;
2860 TRACE("(%p)->(%p)\n", session, out);
2862 if(!out)
2863 return NULL_PTR_ERR;
2865 *out = session->channel_count;
2867 return S_OK;
2870 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2871 IChannelAudioVolume *iface, UINT32 index, float level,
2872 const GUID *context)
2874 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2875 AudioSession *session = This->session;
2877 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2878 wine_dbgstr_guid(context));
2880 if(level < 0.f || level > 1.f)
2881 return E_INVALIDARG;
2883 if(index >= session->channel_count)
2884 return E_INVALIDARG;
2886 if(context)
2887 FIXME("Notifications not supported yet\n");
2889 EnterCriticalSection(&session->lock);
2891 session->channel_vols[index] = level;
2893 TRACE("OSS doesn't support setting volume\n");
2895 LeaveCriticalSection(&session->lock);
2897 return S_OK;
2900 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2901 IChannelAudioVolume *iface, UINT32 index, float *level)
2903 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2904 AudioSession *session = This->session;
2906 TRACE("(%p)->(%d, %p)\n", session, index, level);
2908 if(!level)
2909 return NULL_PTR_ERR;
2911 if(index >= session->channel_count)
2912 return E_INVALIDARG;
2914 *level = session->channel_vols[index];
2916 return S_OK;
2919 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2920 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2921 const GUID *context)
2923 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2924 AudioSession *session = This->session;
2925 int i;
2927 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2928 wine_dbgstr_guid(context));
2930 if(!levels)
2931 return NULL_PTR_ERR;
2933 if(count != session->channel_count)
2934 return E_INVALIDARG;
2936 if(context)
2937 FIXME("Notifications not supported yet\n");
2939 EnterCriticalSection(&session->lock);
2941 for(i = 0; i < count; ++i)
2942 session->channel_vols[i] = levels[i];
2944 TRACE("OSS doesn't support setting volume\n");
2946 LeaveCriticalSection(&session->lock);
2948 return S_OK;
2951 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2952 IChannelAudioVolume *iface, UINT32 count, float *levels)
2954 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2955 AudioSession *session = This->session;
2956 int i;
2958 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2960 if(!levels)
2961 return NULL_PTR_ERR;
2963 if(count != session->channel_count)
2964 return E_INVALIDARG;
2966 for(i = 0; i < count; ++i)
2967 levels[i] = session->channel_vols[i];
2969 return S_OK;
2972 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2974 ChannelAudioVolume_QueryInterface,
2975 ChannelAudioVolume_AddRef,
2976 ChannelAudioVolume_Release,
2977 ChannelAudioVolume_GetChannelCount,
2978 ChannelAudioVolume_SetChannelVolume,
2979 ChannelAudioVolume_GetChannelVolume,
2980 ChannelAudioVolume_SetAllVolumes,
2981 ChannelAudioVolume_GetAllVolumes
2984 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2985 REFIID riid, void **ppv)
2987 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2989 if(!ppv)
2990 return E_POINTER;
2991 *ppv = NULL;
2993 if(IsEqualIID(riid, &IID_IUnknown) ||
2994 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2995 IsEqualIID(riid, &IID_IAudioSessionManager2))
2996 *ppv = iface;
2997 if(*ppv){
2998 IUnknown_AddRef((IUnknown*)*ppv);
2999 return S_OK;
3002 WARN("Unknown interface %s\n", debugstr_guid(riid));
3003 return E_NOINTERFACE;
3006 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3008 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3009 ULONG ref;
3010 ref = InterlockedIncrement(&This->ref);
3011 TRACE("(%p) Refcount now %u\n", This, ref);
3012 return ref;
3015 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3017 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3018 ULONG ref;
3019 ref = InterlockedDecrement(&This->ref);
3020 TRACE("(%p) Refcount now %u\n", This, ref);
3021 if(!ref)
3022 HeapFree(GetProcessHeap(), 0, This);
3023 return ref;
3026 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3027 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3028 IAudioSessionControl **out)
3030 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3031 AudioSession *session;
3032 AudioSessionWrapper *wrapper;
3033 HRESULT hr;
3035 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3036 flags, out);
3038 hr = get_audio_session(session_guid, This->device, 0, &session);
3039 if(FAILED(hr))
3040 return hr;
3042 wrapper = AudioSessionWrapper_Create(NULL);
3043 if(!wrapper)
3044 return E_OUTOFMEMORY;
3046 wrapper->session = session;
3048 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3050 return S_OK;
3053 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3054 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3055 ISimpleAudioVolume **out)
3057 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3058 AudioSession *session;
3059 AudioSessionWrapper *wrapper;
3060 HRESULT hr;
3062 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3063 flags, out);
3065 hr = get_audio_session(session_guid, This->device, 0, &session);
3066 if(FAILED(hr))
3067 return hr;
3069 wrapper = AudioSessionWrapper_Create(NULL);
3070 if(!wrapper)
3071 return E_OUTOFMEMORY;
3073 wrapper->session = session;
3075 *out = &wrapper->ISimpleAudioVolume_iface;
3077 return S_OK;
3080 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3081 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3083 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3084 FIXME("(%p)->(%p) - stub\n", This, out);
3085 return E_NOTIMPL;
3088 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3089 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3091 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3092 FIXME("(%p)->(%p) - stub\n", This, notification);
3093 return E_NOTIMPL;
3096 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3097 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3099 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3100 FIXME("(%p)->(%p) - stub\n", This, notification);
3101 return E_NOTIMPL;
3104 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3105 IAudioSessionManager2 *iface, const WCHAR *session_id,
3106 IAudioVolumeDuckNotification *notification)
3108 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3109 FIXME("(%p)->(%p) - stub\n", This, notification);
3110 return E_NOTIMPL;
3113 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3114 IAudioSessionManager2 *iface,
3115 IAudioVolumeDuckNotification *notification)
3117 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3118 FIXME("(%p)->(%p) - stub\n", This, notification);
3119 return E_NOTIMPL;
3122 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3124 AudioSessionManager_QueryInterface,
3125 AudioSessionManager_AddRef,
3126 AudioSessionManager_Release,
3127 AudioSessionManager_GetAudioSessionControl,
3128 AudioSessionManager_GetSimpleAudioVolume,
3129 AudioSessionManager_GetSessionEnumerator,
3130 AudioSessionManager_RegisterSessionNotification,
3131 AudioSessionManager_UnregisterSessionNotification,
3132 AudioSessionManager_RegisterDuckNotification,
3133 AudioSessionManager_UnregisterDuckNotification
3136 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3137 IAudioSessionManager2 **out)
3139 SessionMgr *This;
3141 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3142 if(!This)
3143 return E_OUTOFMEMORY;
3145 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3146 This->device = device;
3147 This->ref = 1;
3149 *out = &This->IAudioSessionManager2_iface;
3151 return S_OK;