mmdevapi: More accurately track device position.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobb111338a051e0a67d385b15ec680953be6f925c8
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <sys/soundcard.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
45 #include "ole2.h"
46 #include "mmdeviceapi.h"
47 #include "devpkey.h"
48 #include "dshow.h"
49 #include "dsound.h"
51 #include "initguid.h"
52 #include "endpointvolume.h"
53 #include "audiopolicy.h"
54 #include "audioclient.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(oss);
58 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
60 static const REFERENCE_TIME DefaultPeriod = 100000;
61 static const REFERENCE_TIME MinimumPeriod = 50000;
63 struct ACImpl;
64 typedef struct ACImpl ACImpl;
66 typedef struct _AudioSession {
67 GUID guid;
68 struct list clients;
70 IMMDevice *device;
72 float master_vol;
73 UINT32 channel_count;
74 float *channel_vols;
75 BOOL mute;
77 CRITICAL_SECTION lock;
79 struct list entry;
80 } AudioSession;
82 typedef struct _AudioSessionWrapper {
83 IAudioSessionControl2 IAudioSessionControl2_iface;
84 IChannelAudioVolume IChannelAudioVolume_iface;
85 ISimpleAudioVolume ISimpleAudioVolume_iface;
87 LONG ref;
89 ACImpl *client;
90 AudioSession *session;
91 } AudioSessionWrapper;
93 struct ACImpl {
94 IAudioClient IAudioClient_iface;
95 IAudioRenderClient IAudioRenderClient_iface;
96 IAudioCaptureClient IAudioCaptureClient_iface;
97 IAudioClock IAudioClock_iface;
98 IAudioClock2 IAudioClock2_iface;
99 IAudioStreamVolume IAudioStreamVolume_iface;
101 LONG ref;
103 IMMDevice *parent;
104 IUnknown *pUnkFTMarshal;
106 WAVEFORMATEX *fmt;
108 EDataFlow dataflow;
109 DWORD flags;
110 AUDCLNT_SHAREMODE share;
111 HANDLE event;
112 float *vols;
114 int fd;
115 oss_audioinfo ai;
116 char devnode[OSS_DEVNODE_SIZE];
118 BOOL initted, playing;
119 UINT64 written_frames, last_pos_frames;
120 UINT32 period_us, period_frames, bufsize_frames, held_frames, tmp_buffer_frames, in_oss_frames;
121 UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
123 BYTE *local_buffer, *tmp_buffer;
124 LONG32 getbuf_last; /* <0 when using tmp_buffer */
125 HANDLE timer;
127 CRITICAL_SECTION lock;
129 AudioSession *session;
130 AudioSessionWrapper *session_wrapper;
132 struct list entry;
135 typedef struct _SessionMgr {
136 IAudioSessionManager2 IAudioSessionManager2_iface;
138 LONG ref;
140 IMMDevice *device;
141 } SessionMgr;
143 typedef struct _OSSDevice {
144 EDataFlow flow;
145 char devnode[OSS_DEVNODE_SIZE];
146 GUID guid;
148 struct list entry;
149 } OSSDevice;
151 static struct list g_devices = LIST_INIT(g_devices);
153 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
154 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
155 'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
156 static const WCHAR guidW[] = {'g','u','i','d',0};
158 static HANDLE g_timer_q;
160 static CRITICAL_SECTION g_sessions_lock;
161 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
163 0, 0, &g_sessions_lock,
164 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
165 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
167 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
168 static struct list g_sessions = LIST_INIT(g_sessions);
170 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
172 static const IAudioClientVtbl AudioClient_Vtbl;
173 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
174 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
175 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
176 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
177 static const IAudioClockVtbl AudioClock_Vtbl;
178 static const IAudioClock2Vtbl AudioClock2_Vtbl;
179 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
180 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
181 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
183 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
185 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
188 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
190 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
193 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
195 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
198 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
200 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
203 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
205 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
208 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
210 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
213 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
215 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
218 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
220 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
223 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
225 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
228 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
230 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
233 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
235 switch (reason)
237 case DLL_PROCESS_ATTACH:
238 g_timer_q = CreateTimerQueue();
239 if(!g_timer_q)
240 return FALSE;
241 break;
243 case DLL_PROCESS_DETACH:
244 if (!reserved)
246 OSSDevice *iter, *iter2;
248 DeleteCriticalSection(&g_sessions_lock);
250 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
251 HeapFree(GetProcessHeap(), 0, iter);
254 break;
256 return TRUE;
259 /* From <dlls/mmdevapi/mmdevapi.h> */
260 enum DriverPriority {
261 Priority_Unavailable = 0,
262 Priority_Low,
263 Priority_Neutral,
264 Priority_Preferred
267 int WINAPI AUDDRV_GetPriority(void)
269 int mixer_fd;
270 oss_sysinfo sysinfo;
272 /* Attempt to determine if we are running on OSS or ALSA's OSS
273 * compatibility layer. There is no official way to do that, so just check
274 * for validity as best as possible, without rejecting valid OSS
275 * implementations. */
277 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
278 if(mixer_fd < 0){
279 TRACE("Priority_Unavailable: open failed\n");
280 return Priority_Unavailable;
283 sysinfo.version[0] = 0xFF;
284 sysinfo.versionnum = ~0;
285 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
286 TRACE("Priority_Unavailable: ioctl failed\n");
287 close(mixer_fd);
288 return Priority_Unavailable;
291 close(mixer_fd);
293 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
294 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
295 return Priority_Low;
297 if(sysinfo.versionnum & 0x80000000){
298 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
299 return Priority_Low;
302 TRACE("Priority_Preferred: Seems like valid OSS!\n");
304 return Priority_Preferred;
307 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
308 GUID *guid)
310 HKEY key;
311 BOOL opened = FALSE;
312 LONG lr;
314 if(!drv_key){
315 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
316 NULL, &drv_key, NULL);
317 if(lr != ERROR_SUCCESS){
318 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
319 return;
321 opened = TRUE;
324 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
325 NULL, &key, NULL);
326 if(lr != ERROR_SUCCESS){
327 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
328 goto exit;
331 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
332 sizeof(GUID));
333 if(lr != ERROR_SUCCESS)
334 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
336 RegCloseKey(key);
337 exit:
338 if(opened)
339 RegCloseKey(drv_key);
342 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
344 HKEY key = NULL, dev_key;
345 DWORD type, size = sizeof(*guid);
346 WCHAR key_name[256];
348 if(flow == eCapture)
349 key_name[0] = '1';
350 else
351 key_name[0] = '0';
352 key_name[1] = ',';
353 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
354 (sizeof(key_name) / sizeof(*key_name)) - 2);
356 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
357 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
358 if(RegQueryValueExW(dev_key, guidW, 0, &type,
359 (BYTE*)guid, &size) == ERROR_SUCCESS){
360 if(type == REG_BINARY){
361 RegCloseKey(dev_key);
362 RegCloseKey(key);
363 return;
365 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
366 wine_dbgstr_w(key_name), type);
368 RegCloseKey(dev_key);
372 CoCreateGuid(guid);
374 set_device_guid(flow, key, key_name, guid);
376 if(key)
377 RegCloseKey(key);
380 static const char *oss_clean_devnode(const char *devnode)
382 static char ret[OSS_DEVNODE_SIZE];
384 const char *dot, *slash;
385 size_t len;
387 dot = strrchr(devnode, '.');
388 if(!dot)
389 return devnode;
391 slash = strrchr(devnode, '/');
392 if(slash && dot < slash)
393 return devnode;
395 len = dot - devnode;
397 memcpy(ret, devnode, len);
398 ret[len] = '\0';
400 return ret;
403 static UINT get_default_index(EDataFlow flow)
405 int fd = -1, err;
406 UINT i;
407 oss_audioinfo ai;
408 const char *devnode;
409 OSSDevice *dev_item;
411 if(flow == eRender)
412 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
413 else
414 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
416 if(fd < 0){
417 WARN("Couldn't open default device!\n");
418 return 0;
421 ai.dev = -1;
422 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
423 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
424 close(fd);
425 return 0;
428 close(fd);
430 TRACE("Default devnode: %s\n", ai.devnode);
431 devnode = oss_clean_devnode(ai.devnode);
432 i = 0;
433 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
434 if(dev_item->flow == flow){
435 if(!strcmp(devnode, dev_item->devnode))
436 return i;
437 ++i;
441 WARN("Couldn't find default device! Choosing first.\n");
442 return 0;
445 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
446 UINT *num, UINT *def_index)
448 int i, mixer_fd;
449 oss_sysinfo sysinfo;
450 static int print_once = 0;
452 static const WCHAR outW[] = {'O','u','t',':',' ',0};
453 static const WCHAR inW[] = {'I','n',':',' ',0};
455 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
457 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
458 if(mixer_fd < 0){
459 ERR("OSS /dev/mixer doesn't seem to exist\n");
460 return AUDCLNT_E_SERVICE_NOT_RUNNING;
463 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
464 close(mixer_fd);
466 if(errno == EINVAL){
467 ERR("OSS version too old, need at least OSSv4\n");
468 return AUDCLNT_E_SERVICE_NOT_RUNNING;
471 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
472 return E_FAIL;
475 if(!print_once){
476 TRACE("OSS sysinfo:\n");
477 TRACE("product: %s\n", sysinfo.product);
478 TRACE("version: %s\n", sysinfo.version);
479 TRACE("versionnum: %x\n", sysinfo.versionnum);
480 TRACE("numaudios: %d\n", sysinfo.numaudios);
481 TRACE("nummixers: %d\n", sysinfo.nummixers);
482 TRACE("numcards: %d\n", sysinfo.numcards);
483 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
484 print_once = 1;
487 if(sysinfo.numaudios <= 0){
488 WARN("No audio devices!\n");
489 close(mixer_fd);
490 return AUDCLNT_E_SERVICE_NOT_RUNNING;
493 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
494 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
496 *num = 0;
497 for(i = 0; i < sysinfo.numaudios; ++i){
498 oss_audioinfo ai = {0};
499 const char *devnode;
500 OSSDevice *dev_item;
501 int fd;
503 ai.dev = i;
504 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
505 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
506 strerror(errno));
507 continue;
510 devnode = oss_clean_devnode(ai.devnode);
512 /* check for duplicates */
513 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
514 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
515 break;
517 if(&dev_item->entry != &g_devices)
518 continue;
520 if(flow == eRender)
521 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
522 else
523 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
524 if(fd < 0){
525 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
526 devnode, errno, strerror(errno));
527 continue;
529 close(fd);
531 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
532 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
533 size_t len, prefix_len;
534 const WCHAR *prefix;
536 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
538 dev_item->flow = flow;
539 get_device_guid(flow, devnode, &dev_item->guid);
540 strcpy(dev_item->devnode, devnode);
542 (*guids)[*num] = dev_item->guid;
544 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
545 if(flow == eRender){
546 prefix = outW;
547 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
548 len += prefix_len;
549 }else{
550 prefix = inW;
551 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
552 len += prefix_len;
554 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
555 len * sizeof(WCHAR));
556 if(!(*ids)[*num]){
557 for(i = 0; i < *num; ++i)
558 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
559 HeapFree(GetProcessHeap(), 0, *ids);
560 HeapFree(GetProcessHeap(), 0, *guids);
561 HeapFree(GetProcessHeap(), 0, dev_item);
562 close(mixer_fd);
563 return E_OUTOFMEMORY;
565 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
566 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
567 (*ids)[*num] + prefix_len, len - prefix_len);
569 list_add_tail(&g_devices, &dev_item->entry);
571 (*num)++;
575 close(mixer_fd);
577 *def_index = get_default_index(flow);
579 return S_OK;
582 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
584 OSSDevice *dev_item;
585 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
586 if(IsEqualGUID(guid, &dev_item->guid))
587 return dev_item;
588 return NULL;
591 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
592 IAudioClient **out)
594 ACImpl *This;
595 const OSSDevice *oss_dev;
596 HRESULT hr;
598 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
600 oss_dev = get_ossdevice_from_guid(guid);
601 if(!oss_dev){
602 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
603 return AUDCLNT_E_DEVICE_INVALIDATED;
606 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
607 if(!This)
608 return E_OUTOFMEMORY;
610 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
611 (IUnknown **)&This->pUnkFTMarshal);
612 if (FAILED(hr)) {
613 HeapFree(GetProcessHeap(), 0, This);
614 return hr;
617 if(oss_dev->flow == eRender)
618 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
619 else if(oss_dev->flow == eCapture)
620 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
621 else{
622 HeapFree(GetProcessHeap(), 0, This);
623 return E_INVALIDARG;
625 if(This->fd < 0){
626 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
627 strerror(errno));
628 HeapFree(GetProcessHeap(), 0, This);
629 return AUDCLNT_E_DEVICE_INVALIDATED;
632 This->dataflow = oss_dev->flow;
634 This->ai.dev = -1;
635 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
636 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
637 errno, strerror(errno));
638 close(This->fd);
639 HeapFree(GetProcessHeap(), 0, This);
640 return E_FAIL;
643 strcpy(This->devnode, oss_dev->devnode);
645 TRACE("OSS audioinfo:\n");
646 TRACE("devnode: %s\n", This->ai.devnode);
647 TRACE("name: %s\n", This->ai.name);
648 TRACE("busy: %x\n", This->ai.busy);
649 TRACE("caps: %x\n", This->ai.caps);
650 TRACE("iformats: %x\n", This->ai.iformats);
651 TRACE("oformats: %x\n", This->ai.oformats);
652 TRACE("enabled: %d\n", This->ai.enabled);
653 TRACE("min_rate: %d\n", This->ai.min_rate);
654 TRACE("max_rate: %d\n", This->ai.max_rate);
655 TRACE("min_channels: %d\n", This->ai.min_channels);
656 TRACE("max_channels: %d\n", This->ai.max_channels);
658 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
659 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
660 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
661 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
662 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
663 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
665 InitializeCriticalSection(&This->lock);
666 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
668 This->parent = dev;
669 IMMDevice_AddRef(This->parent);
671 IAudioClient_AddRef(&This->IAudioClient_iface);
673 *out = &This->IAudioClient_iface;
675 return S_OK;
678 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
679 REFIID riid, void **ppv)
681 ACImpl *This = impl_from_IAudioClient(iface);
682 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
684 if(!ppv)
685 return E_POINTER;
686 *ppv = NULL;
687 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
688 *ppv = iface;
689 else if(IsEqualIID(riid, &IID_IMarshal))
690 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
691 if(*ppv){
692 IUnknown_AddRef((IUnknown*)*ppv);
693 return S_OK;
695 WARN("Unknown interface %s\n", debugstr_guid(riid));
696 return E_NOINTERFACE;
699 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
701 ACImpl *This = impl_from_IAudioClient(iface);
702 ULONG ref;
703 ref = InterlockedIncrement(&This->ref);
704 TRACE("(%p) Refcount now %u\n", This, ref);
705 return ref;
708 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
710 ACImpl *This = impl_from_IAudioClient(iface);
711 ULONG ref;
713 ref = InterlockedDecrement(&This->ref);
714 TRACE("(%p) Refcount now %u\n", This, ref);
715 if(!ref){
716 if(This->timer){
717 HANDLE event;
718 DWORD wait;
719 event = CreateEventW(NULL, TRUE, FALSE, NULL);
720 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
721 wait = wait && GetLastError() == ERROR_IO_PENDING;
722 if(event && wait)
723 WaitForSingleObject(event, INFINITE);
724 CloseHandle(event);
727 IAudioClient_Stop(iface);
728 IMMDevice_Release(This->parent);
729 IUnknown_Release(This->pUnkFTMarshal);
730 This->lock.DebugInfo->Spare[0] = 0;
731 DeleteCriticalSection(&This->lock);
732 close(This->fd);
733 if(This->initted){
734 EnterCriticalSection(&g_sessions_lock);
735 list_remove(&This->entry);
736 LeaveCriticalSection(&g_sessions_lock);
738 HeapFree(GetProcessHeap(), 0, This->vols);
739 HeapFree(GetProcessHeap(), 0, This->local_buffer);
740 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
741 CoTaskMemFree(This->fmt);
742 HeapFree(GetProcessHeap(), 0, This);
744 return ref;
747 static void dump_fmt(const WAVEFORMATEX *fmt)
749 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
750 switch(fmt->wFormatTag){
751 case WAVE_FORMAT_PCM:
752 TRACE("WAVE_FORMAT_PCM");
753 break;
754 case WAVE_FORMAT_IEEE_FLOAT:
755 TRACE("WAVE_FORMAT_IEEE_FLOAT");
756 break;
757 case WAVE_FORMAT_EXTENSIBLE:
758 TRACE("WAVE_FORMAT_EXTENSIBLE");
759 break;
760 default:
761 TRACE("Unknown");
762 break;
764 TRACE(")\n");
766 TRACE("nChannels: %u\n", fmt->nChannels);
767 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
768 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
769 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
770 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
771 TRACE("cbSize: %u\n", fmt->cbSize);
773 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
774 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
775 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
776 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
777 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
781 static DWORD get_channel_mask(unsigned int channels)
783 switch(channels){
784 case 0:
785 return 0;
786 case 1:
787 return KSAUDIO_SPEAKER_MONO;
788 case 2:
789 return KSAUDIO_SPEAKER_STEREO;
790 case 3:
791 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
792 case 4:
793 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
794 case 5:
795 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
796 case 6:
797 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
798 case 7:
799 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
800 case 8:
801 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
803 FIXME("Unknown speaker configuration: %u\n", channels);
804 return 0;
807 static int get_oss_format(const WAVEFORMATEX *fmt)
809 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
811 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
812 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
813 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
814 switch(fmt->wBitsPerSample){
815 case 8:
816 return AFMT_U8;
817 case 16:
818 return AFMT_S16_LE;
819 case 24:
820 return AFMT_S24_LE;
821 case 32:
822 return AFMT_S32_LE;
824 return -1;
827 #ifdef AFMT_FLOAT
828 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
829 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
830 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
831 if(fmt->wBitsPerSample != 32)
832 return -1;
834 return AFMT_FLOAT;
836 #endif
838 return -1;
841 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
843 WAVEFORMATEX *ret;
844 size_t size;
846 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
847 size = sizeof(WAVEFORMATEXTENSIBLE);
848 else
849 size = sizeof(WAVEFORMATEX);
851 ret = CoTaskMemAlloc(size);
852 if(!ret)
853 return NULL;
855 memcpy(ret, fmt, size);
857 ret->cbSize = size - sizeof(WAVEFORMATEX);
859 return ret;
862 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
863 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
865 int tmp, oss_format;
866 double tenth;
867 HRESULT ret = S_OK;
868 WAVEFORMATEX *closest = NULL;
870 tmp = oss_format = get_oss_format(fmt);
871 if(oss_format < 0)
872 return AUDCLNT_E_UNSUPPORTED_FORMAT;
873 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
874 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
875 return E_FAIL;
877 if(tmp != oss_format){
878 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
879 return AUDCLNT_E_UNSUPPORTED_FORMAT;
882 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
883 (fmt->nAvgBytesPerSec == 0 ||
884 fmt->nBlockAlign == 0 ||
885 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
886 return E_INVALIDARG;
888 if(fmt->nChannels == 0)
889 return AUDCLNT_E_UNSUPPORTED_FORMAT;
891 closest = clone_format(fmt);
892 if(!closest)
893 return E_OUTOFMEMORY;
895 tmp = fmt->nSamplesPerSec;
896 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
897 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
898 CoTaskMemFree(closest);
899 return E_FAIL;
901 tenth = fmt->nSamplesPerSec * 0.1;
902 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
903 ret = S_FALSE;
904 closest->nSamplesPerSec = tmp;
907 tmp = fmt->nChannels;
908 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
909 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
910 CoTaskMemFree(closest);
911 return E_FAIL;
913 if(tmp != fmt->nChannels){
914 ret = S_FALSE;
915 closest->nChannels = tmp;
918 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
919 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
921 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
922 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
923 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
924 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
925 ret = S_FALSE;
927 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
928 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
929 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
930 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
931 ret = S_FALSE;
934 if(ret == S_FALSE && !out)
935 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
937 if(ret == S_FALSE && out){
938 closest->nBlockAlign =
939 closest->nChannels * closest->wBitsPerSample / 8;
940 closest->nAvgBytesPerSec =
941 closest->nBlockAlign * closest->nSamplesPerSec;
942 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
943 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
944 *out = closest;
945 } else
946 CoTaskMemFree(closest);
948 TRACE("returning: %08x\n", ret);
949 return ret;
952 static void session_init_vols(AudioSession *session, UINT channels)
954 if(session->channel_count < channels){
955 UINT i;
957 if(session->channel_vols)
958 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
959 session->channel_vols, sizeof(float) * channels);
960 else
961 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
962 sizeof(float) * channels);
963 if(!session->channel_vols)
964 return;
966 for(i = session->channel_count; i < channels; ++i)
967 session->channel_vols[i] = 1.f;
969 session->channel_count = channels;
973 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
974 UINT num_channels)
976 AudioSession *ret;
978 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
979 if(!ret)
980 return NULL;
982 memcpy(&ret->guid, guid, sizeof(GUID));
984 ret->device = device;
986 list_init(&ret->clients);
988 list_add_head(&g_sessions, &ret->entry);
990 InitializeCriticalSection(&ret->lock);
991 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
993 session_init_vols(ret, num_channels);
995 ret->master_vol = 1.f;
997 return ret;
1000 /* if channels == 0, then this will return or create a session with
1001 * matching dataflow and GUID. otherwise, channels must also match */
1002 static HRESULT get_audio_session(const GUID *sessionguid,
1003 IMMDevice *device, UINT channels, AudioSession **out)
1005 AudioSession *session;
1007 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1008 *out = create_session(&GUID_NULL, device, channels);
1009 if(!*out)
1010 return E_OUTOFMEMORY;
1012 return S_OK;
1015 *out = NULL;
1016 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1017 if(session->device == device &&
1018 IsEqualGUID(sessionguid, &session->guid)){
1019 session_init_vols(session, channels);
1020 *out = session;
1021 break;
1025 if(!*out){
1026 *out = create_session(sessionguid, device, channels);
1027 if(!*out)
1028 return E_OUTOFMEMORY;
1031 return S_OK;
1034 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1035 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1036 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1037 const GUID *sessionguid)
1039 ACImpl *This = impl_from_IAudioClient(iface);
1040 int i;
1041 HRESULT hr;
1043 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1044 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1046 if(!fmt)
1047 return E_POINTER;
1049 dump_fmt(fmt);
1051 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1052 return AUDCLNT_E_NOT_INITIALIZED;
1054 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1055 AUDCLNT_STREAMFLAGS_LOOPBACK |
1056 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1057 AUDCLNT_STREAMFLAGS_NOPERSIST |
1058 AUDCLNT_STREAMFLAGS_RATEADJUST |
1059 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1060 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1061 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1062 TRACE("Unknown flags: %08x\n", flags);
1063 return E_INVALIDARG;
1066 if(mode == AUDCLNT_SHAREMODE_SHARED){
1067 period = DefaultPeriod;
1068 if( duration < 3 * period)
1069 duration = 3 * period;
1070 }else{
1071 if(!period)
1072 period = DefaultPeriod; /* not minimum */
1073 if(period < MinimumPeriod || period > 5000000)
1074 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1075 if(duration > 20000000) /* the smaller the period, the lower this limit */
1076 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1077 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1078 if(duration != period)
1079 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1080 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1081 return AUDCLNT_E_DEVICE_IN_USE;
1082 }else{
1083 if( duration < 8 * period)
1084 duration = 8 * period; /* may grow above 2s */
1088 EnterCriticalSection(&This->lock);
1090 if(This->initted){
1091 LeaveCriticalSection(&This->lock);
1092 return AUDCLNT_E_ALREADY_INITIALIZED;
1095 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1096 if(FAILED(hr)){
1097 LeaveCriticalSection(&This->lock);
1098 return hr;
1101 This->fmt = clone_format(fmt);
1102 if(!This->fmt){
1103 LeaveCriticalSection(&This->lock);
1104 return E_OUTOFMEMORY;
1107 This->period_us = period / 10;
1108 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1110 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1111 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1112 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1113 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1114 This->bufsize_frames * fmt->nBlockAlign);
1115 if(!This->local_buffer){
1116 CoTaskMemFree(This->fmt);
1117 This->fmt = NULL;
1118 LeaveCriticalSection(&This->lock);
1119 return E_OUTOFMEMORY;
1122 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1123 if(!This->vols){
1124 CoTaskMemFree(This->fmt);
1125 This->fmt = NULL;
1126 LeaveCriticalSection(&This->lock);
1127 return E_OUTOFMEMORY;
1130 for(i = 0; i < fmt->nChannels; ++i)
1131 This->vols[i] = 1.f;
1133 This->share = mode;
1134 This->flags = flags;
1135 This->oss_bufsize_bytes = 0;
1137 EnterCriticalSection(&g_sessions_lock);
1139 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1140 &This->session);
1141 if(FAILED(hr)){
1142 LeaveCriticalSection(&g_sessions_lock);
1143 HeapFree(GetProcessHeap(), 0, This->vols);
1144 This->vols = NULL;
1145 CoTaskMemFree(This->fmt);
1146 This->fmt = NULL;
1147 LeaveCriticalSection(&This->lock);
1148 return hr;
1151 list_add_tail(&This->session->clients, &This->entry);
1153 LeaveCriticalSection(&g_sessions_lock);
1155 This->initted = TRUE;
1157 LeaveCriticalSection(&This->lock);
1159 return S_OK;
1162 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1163 UINT32 *frames)
1165 ACImpl *This = impl_from_IAudioClient(iface);
1167 TRACE("(%p)->(%p)\n", This, frames);
1169 if(!frames)
1170 return E_POINTER;
1172 EnterCriticalSection(&This->lock);
1174 if(!This->initted){
1175 LeaveCriticalSection(&This->lock);
1176 return AUDCLNT_E_NOT_INITIALIZED;
1179 *frames = This->bufsize_frames;
1181 TRACE("buffer size: %u\n", *frames);
1183 LeaveCriticalSection(&This->lock);
1185 return S_OK;
1188 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1189 REFERENCE_TIME *latency)
1191 ACImpl *This = impl_from_IAudioClient(iface);
1193 TRACE("(%p)->(%p)\n", This, latency);
1195 if(!latency)
1196 return E_POINTER;
1198 EnterCriticalSection(&This->lock);
1200 if(!This->initted){
1201 LeaveCriticalSection(&This->lock);
1202 return AUDCLNT_E_NOT_INITIALIZED;
1205 /* pretend we process audio in Period chunks, so max latency includes
1206 * the period time. Some native machines add .6666ms in shared mode. */
1207 *latency = This->period_us * 10 + 6666;
1209 LeaveCriticalSection(&This->lock);
1211 return S_OK;
1214 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1215 UINT32 *numpad)
1217 ACImpl *This = impl_from_IAudioClient(iface);
1219 TRACE("(%p)->(%p)\n", This, numpad);
1221 if(!numpad)
1222 return E_POINTER;
1224 EnterCriticalSection(&This->lock);
1226 if(!This->initted){
1227 LeaveCriticalSection(&This->lock);
1228 return AUDCLNT_E_NOT_INITIALIZED;
1231 *numpad = This->held_frames;
1233 TRACE("padding: %u\n", *numpad);
1235 LeaveCriticalSection(&This->lock);
1237 return S_OK;
1240 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1241 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1242 WAVEFORMATEX **outpwfx)
1244 ACImpl *This = impl_from_IAudioClient(iface);
1245 int fd = -1;
1246 HRESULT ret;
1248 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1250 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1251 return E_POINTER;
1253 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1254 return E_INVALIDARG;
1256 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1257 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1258 return E_INVALIDARG;
1260 dump_fmt(pwfx);
1262 if(outpwfx){
1263 *outpwfx = NULL;
1264 if(mode != AUDCLNT_SHAREMODE_SHARED)
1265 outpwfx = NULL;
1268 if(This->dataflow == eRender)
1269 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1270 else if(This->dataflow == eCapture)
1271 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1273 if(fd < 0){
1274 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1275 strerror(errno));
1276 return AUDCLNT_E_DEVICE_INVALIDATED;
1279 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1281 close(fd);
1283 return ret;
1286 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1287 WAVEFORMATEX **pwfx)
1289 ACImpl *This = impl_from_IAudioClient(iface);
1290 WAVEFORMATEXTENSIBLE *fmt;
1291 int formats;
1293 TRACE("(%p)->(%p)\n", This, pwfx);
1295 if(!pwfx)
1296 return E_POINTER;
1297 *pwfx = NULL;
1299 if(This->dataflow == eRender)
1300 formats = This->ai.oformats;
1301 else if(This->dataflow == eCapture)
1302 formats = This->ai.iformats;
1303 else
1304 return E_UNEXPECTED;
1306 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1307 if(!fmt)
1308 return E_OUTOFMEMORY;
1310 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1311 if(formats & AFMT_S16_LE){
1312 fmt->Format.wBitsPerSample = 16;
1313 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1314 #ifdef AFMT_FLOAT
1315 }else if(formats & AFMT_FLOAT){
1316 fmt->Format.wBitsPerSample = 32;
1317 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1318 #endif
1319 }else if(formats & AFMT_U8){
1320 fmt->Format.wBitsPerSample = 8;
1321 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1322 }else if(formats & AFMT_S32_LE){
1323 fmt->Format.wBitsPerSample = 32;
1324 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1325 }else if(formats & AFMT_S24_LE){
1326 fmt->Format.wBitsPerSample = 24;
1327 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1328 }else{
1329 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1330 CoTaskMemFree(fmt);
1331 return E_FAIL;
1334 /* some OSS drivers are buggy, so set reasonable defaults if
1335 * the reported values seem wacky */
1336 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1337 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1338 fmt->Format.nChannels = 2;
1340 if(This->ai.max_rate == 0)
1341 fmt->Format.nSamplesPerSec = 44100;
1342 else
1343 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1344 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1345 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1347 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1349 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1350 fmt->Format.nChannels) / 8;
1351 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1352 fmt->Format.nBlockAlign;
1354 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1355 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1357 *pwfx = (WAVEFORMATEX*)fmt;
1358 dump_fmt(*pwfx);
1360 return S_OK;
1363 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1364 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1366 ACImpl *This = impl_from_IAudioClient(iface);
1368 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1370 if(!defperiod && !minperiod)
1371 return E_POINTER;
1373 if(defperiod)
1374 *defperiod = DefaultPeriod;
1375 if(minperiod)
1376 *minperiod = MinimumPeriod;
1378 return S_OK;
1381 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1383 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1384 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1385 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1386 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1387 This->fmt->wBitsPerSample == 8)
1388 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1389 else
1390 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1393 static void oss_write_data(ACImpl *This)
1395 ssize_t written_bytes;
1396 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1397 size_t to_write_frames, to_write_bytes, advanced;
1398 audio_buf_info bi;
1399 BYTE *buf;
1401 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1402 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1403 return;
1406 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1408 if(bi.bytes > This->oss_bufsize_bytes){
1409 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1410 bi.bytes, This->oss_bufsize_bytes);
1411 This->oss_bufsize_bytes = bi.bytes;
1412 in_oss_frames = 0;
1413 }else if(This->oss_bufsize_bytes - bi.bytes <= bi.fragsize)
1414 in_oss_frames = 0;
1415 else
1416 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1418 write_limit = 0;
1419 while(write_limit + in_oss_frames < max_period * 3)
1420 write_limit += max_period;
1421 if(write_limit == 0)
1422 return;
1424 /* vvvvvv - in_oss_frames
1425 * [--xxxxxxxxxx]
1426 * [xxxxxxxxxx--]
1427 * ^^^^^^^^^^ - held_frames
1428 * ^ - lcl_offs_frames
1430 advanced = This->in_oss_frames - in_oss_frames;
1431 if(advanced > This->held_frames)
1432 advanced = This->held_frames;
1433 This->lcl_offs_frames += advanced;
1434 This->lcl_offs_frames %= This->bufsize_frames;
1435 This->held_frames -= advanced;
1436 This->in_oss_frames = in_oss_frames;
1439 if(This->held_frames == This->in_oss_frames)
1440 return;
1442 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1443 new_frames = This->held_frames - This->in_oss_frames;
1445 if(write_offs_frames + new_frames > This->bufsize_frames)
1446 to_write_frames = This->bufsize_frames - write_offs_frames;
1447 else
1448 to_write_frames = new_frames;
1450 to_write_frames = min(to_write_frames, write_limit);
1451 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1454 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1456 if(This->session->mute)
1457 silence_buffer(This, buf, to_write_frames);
1459 written_bytes = write(This->fd, buf, to_write_bytes);
1460 if(written_bytes < 0){
1461 /* EAGAIN is OSS buffer full, log that too */
1462 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1463 return;
1465 written_frames = written_bytes / This->fmt->nBlockAlign;
1467 This->in_oss_frames += written_frames;
1469 if(written_frames < to_write_frames){
1470 /* OSS buffer probably full */
1471 return;
1474 if(new_frames > written_frames && written_frames < write_limit){
1475 /* wrapped and have some data back at the start to write */
1477 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1478 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1480 if(This->session->mute)
1481 silence_buffer(This, This->local_buffer, to_write_frames);
1483 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1484 if(written_bytes < 0){
1485 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1486 return;
1488 written_frames = written_bytes / This->fmt->nBlockAlign;
1489 This->in_oss_frames += written_frames;
1493 static void oss_read_data(ACImpl *This)
1495 UINT64 pos, readable;
1496 audio_buf_info bi;
1497 ssize_t nread;
1499 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1500 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1501 return;
1504 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1505 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1507 if(bi.bytes < readable)
1508 readable = bi.bytes;
1510 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1511 readable);
1512 if(nread < 0){
1513 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1514 return;
1517 This->held_frames += nread / This->fmt->nBlockAlign;
1519 if(This->held_frames > This->bufsize_frames){
1520 WARN("Overflow of unread data\n");
1521 This->lcl_offs_frames += This->held_frames;
1522 This->lcl_offs_frames %= This->bufsize_frames;
1523 This->held_frames = This->bufsize_frames;
1527 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1529 ACImpl *This = user;
1531 EnterCriticalSection(&This->lock);
1533 if(This->playing){
1534 if(This->dataflow == eRender && This->held_frames)
1535 oss_write_data(This);
1536 else if(This->dataflow == eCapture)
1537 oss_read_data(This);
1540 LeaveCriticalSection(&This->lock);
1542 if(This->event)
1543 SetEvent(This->event);
1546 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1548 ACImpl *This = impl_from_IAudioClient(iface);
1550 TRACE("(%p)\n", This);
1552 EnterCriticalSection(&This->lock);
1554 if(!This->initted){
1555 LeaveCriticalSection(&This->lock);
1556 return AUDCLNT_E_NOT_INITIALIZED;
1559 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1560 LeaveCriticalSection(&This->lock);
1561 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1564 if(This->playing){
1565 LeaveCriticalSection(&This->lock);
1566 return AUDCLNT_E_NOT_STOPPED;
1569 if(!This->timer){
1570 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1571 oss_period_callback, This, 0, This->period_us / 1000,
1572 WT_EXECUTEINTIMERTHREAD))
1573 ERR("Unable to create period timer: %u\n", GetLastError());
1576 This->playing = TRUE;
1578 LeaveCriticalSection(&This->lock);
1580 return S_OK;
1583 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1585 ACImpl *This = impl_from_IAudioClient(iface);
1587 TRACE("(%p)\n", This);
1589 EnterCriticalSection(&This->lock);
1591 if(!This->initted){
1592 LeaveCriticalSection(&This->lock);
1593 return AUDCLNT_E_NOT_INITIALIZED;
1596 if(!This->playing){
1597 LeaveCriticalSection(&This->lock);
1598 return S_FALSE;
1601 This->playing = FALSE;
1602 This->in_oss_frames = 0;
1604 LeaveCriticalSection(&This->lock);
1606 return S_OK;
1609 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1611 ACImpl *This = impl_from_IAudioClient(iface);
1613 TRACE("(%p)\n", This);
1615 EnterCriticalSection(&This->lock);
1617 if(!This->initted){
1618 LeaveCriticalSection(&This->lock);
1619 return AUDCLNT_E_NOT_INITIALIZED;
1622 if(This->playing){
1623 LeaveCriticalSection(&This->lock);
1624 return AUDCLNT_E_NOT_STOPPED;
1627 if(This->getbuf_last){
1628 LeaveCriticalSection(&This->lock);
1629 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1632 if(This->dataflow == eRender){
1633 This->written_frames = 0;
1634 This->last_pos_frames = 0;
1635 }else{
1636 This->written_frames += This->held_frames;
1638 This->held_frames = 0;
1639 This->lcl_offs_frames = 0;
1640 This->in_oss_frames = 0;
1642 LeaveCriticalSection(&This->lock);
1644 return S_OK;
1647 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1648 HANDLE event)
1650 ACImpl *This = impl_from_IAudioClient(iface);
1652 TRACE("(%p)->(%p)\n", This, event);
1654 if(!event)
1655 return E_INVALIDARG;
1657 EnterCriticalSection(&This->lock);
1659 if(!This->initted){
1660 LeaveCriticalSection(&This->lock);
1661 return AUDCLNT_E_NOT_INITIALIZED;
1664 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1665 LeaveCriticalSection(&This->lock);
1666 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1669 if (This->event){
1670 LeaveCriticalSection(&This->lock);
1671 FIXME("called twice\n");
1672 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1675 This->event = event;
1677 LeaveCriticalSection(&This->lock);
1679 return S_OK;
1682 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1683 void **ppv)
1685 ACImpl *This = impl_from_IAudioClient(iface);
1687 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1689 if(!ppv)
1690 return E_POINTER;
1691 *ppv = NULL;
1693 EnterCriticalSection(&This->lock);
1695 if(!This->initted){
1696 LeaveCriticalSection(&This->lock);
1697 return AUDCLNT_E_NOT_INITIALIZED;
1700 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1701 if(This->dataflow != eRender){
1702 LeaveCriticalSection(&This->lock);
1703 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1705 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1706 *ppv = &This->IAudioRenderClient_iface;
1707 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1708 if(This->dataflow != eCapture){
1709 LeaveCriticalSection(&This->lock);
1710 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1712 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1713 *ppv = &This->IAudioCaptureClient_iface;
1714 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1715 IAudioClock_AddRef(&This->IAudioClock_iface);
1716 *ppv = &This->IAudioClock_iface;
1717 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1718 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1719 *ppv = &This->IAudioStreamVolume_iface;
1720 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1721 if(!This->session_wrapper){
1722 This->session_wrapper = AudioSessionWrapper_Create(This);
1723 if(!This->session_wrapper){
1724 LeaveCriticalSection(&This->lock);
1725 return E_OUTOFMEMORY;
1727 }else
1728 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1730 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1731 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1732 if(!This->session_wrapper){
1733 This->session_wrapper = AudioSessionWrapper_Create(This);
1734 if(!This->session_wrapper){
1735 LeaveCriticalSection(&This->lock);
1736 return E_OUTOFMEMORY;
1738 }else
1739 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1741 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1742 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1743 if(!This->session_wrapper){
1744 This->session_wrapper = AudioSessionWrapper_Create(This);
1745 if(!This->session_wrapper){
1746 LeaveCriticalSection(&This->lock);
1747 return E_OUTOFMEMORY;
1749 }else
1750 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1752 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1755 if(*ppv){
1756 LeaveCriticalSection(&This->lock);
1757 return S_OK;
1760 LeaveCriticalSection(&This->lock);
1762 FIXME("stub %s\n", debugstr_guid(riid));
1763 return E_NOINTERFACE;
1766 static const IAudioClientVtbl AudioClient_Vtbl =
1768 AudioClient_QueryInterface,
1769 AudioClient_AddRef,
1770 AudioClient_Release,
1771 AudioClient_Initialize,
1772 AudioClient_GetBufferSize,
1773 AudioClient_GetStreamLatency,
1774 AudioClient_GetCurrentPadding,
1775 AudioClient_IsFormatSupported,
1776 AudioClient_GetMixFormat,
1777 AudioClient_GetDevicePeriod,
1778 AudioClient_Start,
1779 AudioClient_Stop,
1780 AudioClient_Reset,
1781 AudioClient_SetEventHandle,
1782 AudioClient_GetService
1785 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1786 IAudioRenderClient *iface, REFIID riid, void **ppv)
1788 ACImpl *This = impl_from_IAudioRenderClient(iface);
1789 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1791 if(!ppv)
1792 return E_POINTER;
1793 *ppv = NULL;
1795 if(IsEqualIID(riid, &IID_IUnknown) ||
1796 IsEqualIID(riid, &IID_IAudioRenderClient))
1797 *ppv = iface;
1798 else if(IsEqualIID(riid, &IID_IMarshal))
1799 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1800 if(*ppv){
1801 IUnknown_AddRef((IUnknown*)*ppv);
1802 return S_OK;
1805 WARN("Unknown interface %s\n", debugstr_guid(riid));
1806 return E_NOINTERFACE;
1809 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1811 ACImpl *This = impl_from_IAudioRenderClient(iface);
1812 return AudioClient_AddRef(&This->IAudioClient_iface);
1815 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1817 ACImpl *This = impl_from_IAudioRenderClient(iface);
1818 return AudioClient_Release(&This->IAudioClient_iface);
1821 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1822 UINT32 frames, BYTE **data)
1824 ACImpl *This = impl_from_IAudioRenderClient(iface);
1825 UINT32 write_pos;
1827 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1829 if(!data)
1830 return E_POINTER;
1832 *data = NULL;
1834 EnterCriticalSection(&This->lock);
1836 if(This->getbuf_last){
1837 LeaveCriticalSection(&This->lock);
1838 return AUDCLNT_E_OUT_OF_ORDER;
1841 if(!frames){
1842 LeaveCriticalSection(&This->lock);
1843 return S_OK;
1846 if(This->held_frames + frames > This->bufsize_frames){
1847 LeaveCriticalSection(&This->lock);
1848 return AUDCLNT_E_BUFFER_TOO_LARGE;
1851 write_pos =
1852 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1853 if(write_pos + frames > This->bufsize_frames){
1854 if(This->tmp_buffer_frames < frames){
1855 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1856 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1857 frames * This->fmt->nBlockAlign);
1858 if(!This->tmp_buffer){
1859 LeaveCriticalSection(&This->lock);
1860 return E_OUTOFMEMORY;
1862 This->tmp_buffer_frames = frames;
1864 *data = This->tmp_buffer;
1865 This->getbuf_last = -frames;
1866 }else{
1867 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1868 This->getbuf_last = frames;
1871 silence_buffer(This, *data, frames);
1873 LeaveCriticalSection(&This->lock);
1875 return S_OK;
1878 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1880 UINT32 write_offs_frames =
1881 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1882 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1883 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1884 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1885 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1887 if(written_bytes <= chunk_bytes){
1888 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1889 }else{
1890 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1891 memcpy(This->local_buffer, buffer + chunk_bytes,
1892 written_bytes - chunk_bytes);
1896 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1897 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1899 ACImpl *This = impl_from_IAudioRenderClient(iface);
1900 BYTE *buffer;
1902 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1904 EnterCriticalSection(&This->lock);
1906 if(!written_frames){
1907 This->getbuf_last = 0;
1908 LeaveCriticalSection(&This->lock);
1909 return S_OK;
1912 if(!This->getbuf_last){
1913 LeaveCriticalSection(&This->lock);
1914 return AUDCLNT_E_OUT_OF_ORDER;
1917 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1918 LeaveCriticalSection(&This->lock);
1919 return AUDCLNT_E_INVALID_SIZE;
1922 if(This->getbuf_last >= 0)
1923 buffer = This->local_buffer + This->fmt->nBlockAlign *
1924 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1925 else
1926 buffer = This->tmp_buffer;
1928 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1929 silence_buffer(This, buffer, written_frames);
1931 if(This->getbuf_last < 0)
1932 oss_wrap_buffer(This, buffer, written_frames);
1934 This->held_frames += written_frames;
1935 This->written_frames += written_frames;
1936 This->getbuf_last = 0;
1938 LeaveCriticalSection(&This->lock);
1940 return S_OK;
1943 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1944 AudioRenderClient_QueryInterface,
1945 AudioRenderClient_AddRef,
1946 AudioRenderClient_Release,
1947 AudioRenderClient_GetBuffer,
1948 AudioRenderClient_ReleaseBuffer
1951 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1952 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1954 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1955 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1957 if(!ppv)
1958 return E_POINTER;
1959 *ppv = NULL;
1961 if(IsEqualIID(riid, &IID_IUnknown) ||
1962 IsEqualIID(riid, &IID_IAudioCaptureClient))
1963 *ppv = iface;
1964 else if(IsEqualIID(riid, &IID_IMarshal))
1965 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1966 if(*ppv){
1967 IUnknown_AddRef((IUnknown*)*ppv);
1968 return S_OK;
1971 WARN("Unknown interface %s\n", debugstr_guid(riid));
1972 return E_NOINTERFACE;
1975 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1977 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1978 return IAudioClient_AddRef(&This->IAudioClient_iface);
1981 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1983 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1984 return IAudioClient_Release(&This->IAudioClient_iface);
1987 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1988 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1989 UINT64 *qpcpos)
1991 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1993 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1994 devpos, qpcpos);
1996 if(!data || !frames || !flags)
1997 return E_POINTER;
1999 EnterCriticalSection(&This->lock);
2001 if(This->getbuf_last){
2002 LeaveCriticalSection(&This->lock);
2003 return AUDCLNT_E_OUT_OF_ORDER;
2006 if(This->held_frames < This->period_frames){
2007 *frames = 0;
2008 LeaveCriticalSection(&This->lock);
2009 return AUDCLNT_S_BUFFER_EMPTY;
2012 *flags = 0;
2014 *frames = This->period_frames;
2016 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2017 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2018 if(This->tmp_buffer_frames < *frames){
2019 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2020 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2021 *frames * This->fmt->nBlockAlign);
2022 if(!This->tmp_buffer){
2023 LeaveCriticalSection(&This->lock);
2024 return E_OUTOFMEMORY;
2026 This->tmp_buffer_frames = *frames;
2029 *data = This->tmp_buffer;
2030 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2031 This->fmt->nBlockAlign;
2032 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2033 frames_bytes = *frames * This->fmt->nBlockAlign;
2034 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2035 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2036 frames_bytes - chunk_bytes);
2037 }else
2038 *data = This->local_buffer +
2039 This->lcl_offs_frames * This->fmt->nBlockAlign;
2041 This->getbuf_last = *frames;
2043 if(devpos)
2044 *devpos = This->written_frames;
2045 if(qpcpos){
2046 LARGE_INTEGER stamp, freq;
2047 QueryPerformanceCounter(&stamp);
2048 QueryPerformanceFrequency(&freq);
2049 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2052 LeaveCriticalSection(&This->lock);
2054 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2057 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2058 IAudioCaptureClient *iface, UINT32 done)
2060 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2062 TRACE("(%p)->(%u)\n", This, done);
2064 EnterCriticalSection(&This->lock);
2066 if(!done){
2067 This->getbuf_last = 0;
2068 LeaveCriticalSection(&This->lock);
2069 return S_OK;
2072 if(!This->getbuf_last){
2073 LeaveCriticalSection(&This->lock);
2074 return AUDCLNT_E_OUT_OF_ORDER;
2077 if(This->getbuf_last != done){
2078 LeaveCriticalSection(&This->lock);
2079 return AUDCLNT_E_INVALID_SIZE;
2082 This->written_frames += done;
2083 This->held_frames -= done;
2084 This->lcl_offs_frames += done;
2085 This->lcl_offs_frames %= This->bufsize_frames;
2086 This->getbuf_last = 0;
2088 LeaveCriticalSection(&This->lock);
2090 return S_OK;
2093 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2094 IAudioCaptureClient *iface, UINT32 *frames)
2096 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2098 TRACE("(%p)->(%p)\n", This, frames);
2100 if(!frames)
2101 return E_POINTER;
2103 EnterCriticalSection(&This->lock);
2105 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2107 LeaveCriticalSection(&This->lock);
2109 return S_OK;
2112 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2114 AudioCaptureClient_QueryInterface,
2115 AudioCaptureClient_AddRef,
2116 AudioCaptureClient_Release,
2117 AudioCaptureClient_GetBuffer,
2118 AudioCaptureClient_ReleaseBuffer,
2119 AudioCaptureClient_GetNextPacketSize
2122 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2123 REFIID riid, void **ppv)
2125 ACImpl *This = impl_from_IAudioClock(iface);
2127 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2129 if(!ppv)
2130 return E_POINTER;
2131 *ppv = NULL;
2133 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2134 *ppv = iface;
2135 else if(IsEqualIID(riid, &IID_IAudioClock2))
2136 *ppv = &This->IAudioClock2_iface;
2137 if(*ppv){
2138 IUnknown_AddRef((IUnknown*)*ppv);
2139 return S_OK;
2142 WARN("Unknown interface %s\n", debugstr_guid(riid));
2143 return E_NOINTERFACE;
2146 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2148 ACImpl *This = impl_from_IAudioClock(iface);
2149 return IAudioClient_AddRef(&This->IAudioClient_iface);
2152 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2154 ACImpl *This = impl_from_IAudioClock(iface);
2155 return IAudioClient_Release(&This->IAudioClient_iface);
2158 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2160 ACImpl *This = impl_from_IAudioClock(iface);
2162 TRACE("(%p)->(%p)\n", This, freq);
2164 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2165 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2166 else
2167 *freq = This->fmt->nSamplesPerSec;
2169 return S_OK;
2172 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2173 UINT64 *qpctime)
2175 ACImpl *This = impl_from_IAudioClock(iface);
2177 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2179 if(!pos)
2180 return E_POINTER;
2182 EnterCriticalSection(&This->lock);
2184 if(This->dataflow == eRender){
2185 *pos = This->written_frames - This->held_frames;
2186 if(*pos < This->last_pos_frames)
2187 *pos = This->last_pos_frames;
2188 }else if(This->dataflow == eCapture){
2189 audio_buf_info bi;
2190 UINT32 held;
2192 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2193 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2194 held = 0;
2195 }else{
2196 if(bi.bytes <= bi.fragsize)
2197 held = 0;
2198 else
2199 held = bi.bytes / This->fmt->nBlockAlign;
2202 *pos = This->written_frames + held;
2205 This->last_pos_frames = *pos;
2207 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2208 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2209 *pos *= This->fmt->nBlockAlign;
2211 LeaveCriticalSection(&This->lock);
2213 if(qpctime){
2214 LARGE_INTEGER stamp, freq;
2215 QueryPerformanceCounter(&stamp);
2216 QueryPerformanceFrequency(&freq);
2217 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2220 return S_OK;
2223 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2224 DWORD *chars)
2226 ACImpl *This = impl_from_IAudioClock(iface);
2228 TRACE("(%p)->(%p)\n", This, chars);
2230 if(!chars)
2231 return E_POINTER;
2233 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2235 return S_OK;
2238 static const IAudioClockVtbl AudioClock_Vtbl =
2240 AudioClock_QueryInterface,
2241 AudioClock_AddRef,
2242 AudioClock_Release,
2243 AudioClock_GetFrequency,
2244 AudioClock_GetPosition,
2245 AudioClock_GetCharacteristics
2248 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2249 REFIID riid, void **ppv)
2251 ACImpl *This = impl_from_IAudioClock2(iface);
2252 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2255 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2257 ACImpl *This = impl_from_IAudioClock2(iface);
2258 return IAudioClient_AddRef(&This->IAudioClient_iface);
2261 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2263 ACImpl *This = impl_from_IAudioClock2(iface);
2264 return IAudioClient_Release(&This->IAudioClient_iface);
2267 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2268 UINT64 *pos, UINT64 *qpctime)
2270 ACImpl *This = impl_from_IAudioClock2(iface);
2272 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2274 return E_NOTIMPL;
2277 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2279 AudioClock2_QueryInterface,
2280 AudioClock2_AddRef,
2281 AudioClock2_Release,
2282 AudioClock2_GetDevicePosition
2285 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2287 AudioSessionWrapper *ret;
2289 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2290 sizeof(AudioSessionWrapper));
2291 if(!ret)
2292 return NULL;
2294 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2295 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2296 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2298 ret->ref = 1;
2300 ret->client = client;
2301 if(client){
2302 ret->session = client->session;
2303 AudioClient_AddRef(&client->IAudioClient_iface);
2306 return ret;
2309 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2310 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2312 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2314 if(!ppv)
2315 return E_POINTER;
2316 *ppv = NULL;
2318 if(IsEqualIID(riid, &IID_IUnknown) ||
2319 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2320 IsEqualIID(riid, &IID_IAudioSessionControl2))
2321 *ppv = iface;
2322 if(*ppv){
2323 IUnknown_AddRef((IUnknown*)*ppv);
2324 return S_OK;
2327 WARN("Unknown interface %s\n", debugstr_guid(riid));
2328 return E_NOINTERFACE;
2331 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2333 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2334 ULONG ref;
2335 ref = InterlockedIncrement(&This->ref);
2336 TRACE("(%p) Refcount now %u\n", This, ref);
2337 return ref;
2340 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2342 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2343 ULONG ref;
2344 ref = InterlockedDecrement(&This->ref);
2345 TRACE("(%p) Refcount now %u\n", This, ref);
2346 if(!ref){
2347 if(This->client){
2348 EnterCriticalSection(&This->client->lock);
2349 This->client->session_wrapper = NULL;
2350 LeaveCriticalSection(&This->client->lock);
2351 AudioClient_Release(&This->client->IAudioClient_iface);
2353 HeapFree(GetProcessHeap(), 0, This);
2355 return ref;
2358 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2359 AudioSessionState *state)
2361 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2362 ACImpl *client;
2364 TRACE("(%p)->(%p)\n", This, state);
2366 if(!state)
2367 return NULL_PTR_ERR;
2369 EnterCriticalSection(&g_sessions_lock);
2371 if(list_empty(&This->session->clients)){
2372 *state = AudioSessionStateExpired;
2373 LeaveCriticalSection(&g_sessions_lock);
2374 return S_OK;
2377 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2378 EnterCriticalSection(&client->lock);
2379 if(client->playing){
2380 *state = AudioSessionStateActive;
2381 LeaveCriticalSection(&client->lock);
2382 LeaveCriticalSection(&g_sessions_lock);
2383 return S_OK;
2385 LeaveCriticalSection(&client->lock);
2388 LeaveCriticalSection(&g_sessions_lock);
2390 *state = AudioSessionStateInactive;
2392 return S_OK;
2395 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2396 IAudioSessionControl2 *iface, WCHAR **name)
2398 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2400 FIXME("(%p)->(%p) - stub\n", This, name);
2402 return E_NOTIMPL;
2405 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2406 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2408 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2410 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2412 return E_NOTIMPL;
2415 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2416 IAudioSessionControl2 *iface, WCHAR **path)
2418 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2420 FIXME("(%p)->(%p) - stub\n", This, path);
2422 return E_NOTIMPL;
2425 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2426 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2428 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2430 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2432 return E_NOTIMPL;
2435 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2436 IAudioSessionControl2 *iface, GUID *group)
2438 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2440 FIXME("(%p)->(%p) - stub\n", This, group);
2442 return E_NOTIMPL;
2445 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2446 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2448 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2450 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2451 debugstr_guid(session));
2453 return E_NOTIMPL;
2456 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2457 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2459 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2461 FIXME("(%p)->(%p) - stub\n", This, events);
2463 return S_OK;
2466 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2467 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2469 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2471 FIXME("(%p)->(%p) - stub\n", This, events);
2473 return S_OK;
2476 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2477 IAudioSessionControl2 *iface, WCHAR **id)
2479 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2481 FIXME("(%p)->(%p) - stub\n", This, id);
2483 return E_NOTIMPL;
2486 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2487 IAudioSessionControl2 *iface, WCHAR **id)
2489 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2491 FIXME("(%p)->(%p) - stub\n", This, id);
2493 return E_NOTIMPL;
2496 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2497 IAudioSessionControl2 *iface, DWORD *pid)
2499 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2501 TRACE("(%p)->(%p)\n", This, pid);
2503 if(!pid)
2504 return E_POINTER;
2506 *pid = GetCurrentProcessId();
2508 return S_OK;
2511 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2512 IAudioSessionControl2 *iface)
2514 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2516 TRACE("(%p)\n", This);
2518 return S_FALSE;
2521 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2522 IAudioSessionControl2 *iface, BOOL optout)
2524 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2526 TRACE("(%p)->(%d)\n", This, optout);
2528 return S_OK;
2531 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2533 AudioSessionControl_QueryInterface,
2534 AudioSessionControl_AddRef,
2535 AudioSessionControl_Release,
2536 AudioSessionControl_GetState,
2537 AudioSessionControl_GetDisplayName,
2538 AudioSessionControl_SetDisplayName,
2539 AudioSessionControl_GetIconPath,
2540 AudioSessionControl_SetIconPath,
2541 AudioSessionControl_GetGroupingParam,
2542 AudioSessionControl_SetGroupingParam,
2543 AudioSessionControl_RegisterAudioSessionNotification,
2544 AudioSessionControl_UnregisterAudioSessionNotification,
2545 AudioSessionControl_GetSessionIdentifier,
2546 AudioSessionControl_GetSessionInstanceIdentifier,
2547 AudioSessionControl_GetProcessId,
2548 AudioSessionControl_IsSystemSoundsSession,
2549 AudioSessionControl_SetDuckingPreference
2552 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2553 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2555 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2557 if(!ppv)
2558 return E_POINTER;
2559 *ppv = NULL;
2561 if(IsEqualIID(riid, &IID_IUnknown) ||
2562 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2563 *ppv = iface;
2564 if(*ppv){
2565 IUnknown_AddRef((IUnknown*)*ppv);
2566 return S_OK;
2569 WARN("Unknown interface %s\n", debugstr_guid(riid));
2570 return E_NOINTERFACE;
2573 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2575 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2576 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2579 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2581 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2582 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2585 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2586 ISimpleAudioVolume *iface, float level, const GUID *context)
2588 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2589 AudioSession *session = This->session;
2591 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2593 if(level < 0.f || level > 1.f)
2594 return E_INVALIDARG;
2596 if(context)
2597 FIXME("Notifications not supported yet\n");
2599 EnterCriticalSection(&session->lock);
2601 session->master_vol = level;
2603 TRACE("OSS doesn't support setting volume\n");
2605 LeaveCriticalSection(&session->lock);
2607 return S_OK;
2610 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2611 ISimpleAudioVolume *iface, float *level)
2613 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2614 AudioSession *session = This->session;
2616 TRACE("(%p)->(%p)\n", session, level);
2618 if(!level)
2619 return NULL_PTR_ERR;
2621 *level = session->master_vol;
2623 return S_OK;
2626 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2627 BOOL mute, const GUID *context)
2629 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2630 AudioSession *session = This->session;
2632 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2634 EnterCriticalSection(&session->lock);
2636 session->mute = mute;
2638 LeaveCriticalSection(&session->lock);
2640 return S_OK;
2643 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2644 BOOL *mute)
2646 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2647 AudioSession *session = This->session;
2649 TRACE("(%p)->(%p)\n", session, mute);
2651 if(!mute)
2652 return NULL_PTR_ERR;
2654 *mute = This->session->mute;
2656 return S_OK;
2659 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2661 SimpleAudioVolume_QueryInterface,
2662 SimpleAudioVolume_AddRef,
2663 SimpleAudioVolume_Release,
2664 SimpleAudioVolume_SetMasterVolume,
2665 SimpleAudioVolume_GetMasterVolume,
2666 SimpleAudioVolume_SetMute,
2667 SimpleAudioVolume_GetMute
2670 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2671 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2673 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2675 if(!ppv)
2676 return E_POINTER;
2677 *ppv = NULL;
2679 if(IsEqualIID(riid, &IID_IUnknown) ||
2680 IsEqualIID(riid, &IID_IAudioStreamVolume))
2681 *ppv = iface;
2682 if(*ppv){
2683 IUnknown_AddRef((IUnknown*)*ppv);
2684 return S_OK;
2687 WARN("Unknown interface %s\n", debugstr_guid(riid));
2688 return E_NOINTERFACE;
2691 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2693 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2694 return IAudioClient_AddRef(&This->IAudioClient_iface);
2697 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2699 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2700 return IAudioClient_Release(&This->IAudioClient_iface);
2703 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2704 IAudioStreamVolume *iface, UINT32 *out)
2706 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2708 TRACE("(%p)->(%p)\n", This, out);
2710 if(!out)
2711 return E_POINTER;
2713 *out = This->fmt->nChannels;
2715 return S_OK;
2718 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2719 IAudioStreamVolume *iface, UINT32 index, float level)
2721 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2723 TRACE("(%p)->(%d, %f)\n", This, index, level);
2725 if(level < 0.f || level > 1.f)
2726 return E_INVALIDARG;
2728 if(index >= This->fmt->nChannels)
2729 return E_INVALIDARG;
2731 EnterCriticalSection(&This->lock);
2733 This->vols[index] = level;
2735 TRACE("OSS doesn't support setting volume\n");
2737 LeaveCriticalSection(&This->lock);
2739 return S_OK;
2742 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2743 IAudioStreamVolume *iface, UINT32 index, float *level)
2745 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2747 TRACE("(%p)->(%d, %p)\n", This, index, level);
2749 if(!level)
2750 return E_POINTER;
2752 if(index >= This->fmt->nChannels)
2753 return E_INVALIDARG;
2755 *level = This->vols[index];
2757 return S_OK;
2760 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2761 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2763 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2764 int i;
2766 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2768 if(!levels)
2769 return E_POINTER;
2771 if(count != This->fmt->nChannels)
2772 return E_INVALIDARG;
2774 EnterCriticalSection(&This->lock);
2776 for(i = 0; i < count; ++i)
2777 This->vols[i] = levels[i];
2779 TRACE("OSS doesn't support setting volume\n");
2781 LeaveCriticalSection(&This->lock);
2783 return S_OK;
2786 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2787 IAudioStreamVolume *iface, UINT32 count, float *levels)
2789 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2790 int i;
2792 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2794 if(!levels)
2795 return E_POINTER;
2797 if(count != This->fmt->nChannels)
2798 return E_INVALIDARG;
2800 EnterCriticalSection(&This->lock);
2802 for(i = 0; i < count; ++i)
2803 levels[i] = This->vols[i];
2805 LeaveCriticalSection(&This->lock);
2807 return S_OK;
2810 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2812 AudioStreamVolume_QueryInterface,
2813 AudioStreamVolume_AddRef,
2814 AudioStreamVolume_Release,
2815 AudioStreamVolume_GetChannelCount,
2816 AudioStreamVolume_SetChannelVolume,
2817 AudioStreamVolume_GetChannelVolume,
2818 AudioStreamVolume_SetAllVolumes,
2819 AudioStreamVolume_GetAllVolumes
2822 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2823 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2825 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2827 if(!ppv)
2828 return E_POINTER;
2829 *ppv = NULL;
2831 if(IsEqualIID(riid, &IID_IUnknown) ||
2832 IsEqualIID(riid, &IID_IChannelAudioVolume))
2833 *ppv = iface;
2834 if(*ppv){
2835 IUnknown_AddRef((IUnknown*)*ppv);
2836 return S_OK;
2839 WARN("Unknown interface %s\n", debugstr_guid(riid));
2840 return E_NOINTERFACE;
2843 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2845 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2846 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2849 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2851 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2852 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2855 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2856 IChannelAudioVolume *iface, UINT32 *out)
2858 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2859 AudioSession *session = This->session;
2861 TRACE("(%p)->(%p)\n", session, out);
2863 if(!out)
2864 return NULL_PTR_ERR;
2866 *out = session->channel_count;
2868 return S_OK;
2871 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2872 IChannelAudioVolume *iface, UINT32 index, float level,
2873 const GUID *context)
2875 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2876 AudioSession *session = This->session;
2878 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2879 wine_dbgstr_guid(context));
2881 if(level < 0.f || level > 1.f)
2882 return E_INVALIDARG;
2884 if(index >= session->channel_count)
2885 return E_INVALIDARG;
2887 if(context)
2888 FIXME("Notifications not supported yet\n");
2890 EnterCriticalSection(&session->lock);
2892 session->channel_vols[index] = level;
2894 TRACE("OSS doesn't support setting volume\n");
2896 LeaveCriticalSection(&session->lock);
2898 return S_OK;
2901 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2902 IChannelAudioVolume *iface, UINT32 index, float *level)
2904 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2905 AudioSession *session = This->session;
2907 TRACE("(%p)->(%d, %p)\n", session, index, level);
2909 if(!level)
2910 return NULL_PTR_ERR;
2912 if(index >= session->channel_count)
2913 return E_INVALIDARG;
2915 *level = session->channel_vols[index];
2917 return S_OK;
2920 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2921 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2922 const GUID *context)
2924 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2925 AudioSession *session = This->session;
2926 int i;
2928 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2929 wine_dbgstr_guid(context));
2931 if(!levels)
2932 return NULL_PTR_ERR;
2934 if(count != session->channel_count)
2935 return E_INVALIDARG;
2937 if(context)
2938 FIXME("Notifications not supported yet\n");
2940 EnterCriticalSection(&session->lock);
2942 for(i = 0; i < count; ++i)
2943 session->channel_vols[i] = levels[i];
2945 TRACE("OSS doesn't support setting volume\n");
2947 LeaveCriticalSection(&session->lock);
2949 return S_OK;
2952 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2953 IChannelAudioVolume *iface, UINT32 count, float *levels)
2955 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2956 AudioSession *session = This->session;
2957 int i;
2959 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2961 if(!levels)
2962 return NULL_PTR_ERR;
2964 if(count != session->channel_count)
2965 return E_INVALIDARG;
2967 for(i = 0; i < count; ++i)
2968 levels[i] = session->channel_vols[i];
2970 return S_OK;
2973 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2975 ChannelAudioVolume_QueryInterface,
2976 ChannelAudioVolume_AddRef,
2977 ChannelAudioVolume_Release,
2978 ChannelAudioVolume_GetChannelCount,
2979 ChannelAudioVolume_SetChannelVolume,
2980 ChannelAudioVolume_GetChannelVolume,
2981 ChannelAudioVolume_SetAllVolumes,
2982 ChannelAudioVolume_GetAllVolumes
2985 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2986 REFIID riid, void **ppv)
2988 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2990 if(!ppv)
2991 return E_POINTER;
2992 *ppv = NULL;
2994 if(IsEqualIID(riid, &IID_IUnknown) ||
2995 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2996 IsEqualIID(riid, &IID_IAudioSessionManager2))
2997 *ppv = iface;
2998 if(*ppv){
2999 IUnknown_AddRef((IUnknown*)*ppv);
3000 return S_OK;
3003 WARN("Unknown interface %s\n", debugstr_guid(riid));
3004 return E_NOINTERFACE;
3007 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3009 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3010 ULONG ref;
3011 ref = InterlockedIncrement(&This->ref);
3012 TRACE("(%p) Refcount now %u\n", This, ref);
3013 return ref;
3016 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3018 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3019 ULONG ref;
3020 ref = InterlockedDecrement(&This->ref);
3021 TRACE("(%p) Refcount now %u\n", This, ref);
3022 if(!ref)
3023 HeapFree(GetProcessHeap(), 0, This);
3024 return ref;
3027 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3028 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3029 IAudioSessionControl **out)
3031 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3032 AudioSession *session;
3033 AudioSessionWrapper *wrapper;
3034 HRESULT hr;
3036 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3037 flags, out);
3039 hr = get_audio_session(session_guid, This->device, 0, &session);
3040 if(FAILED(hr))
3041 return hr;
3043 wrapper = AudioSessionWrapper_Create(NULL);
3044 if(!wrapper)
3045 return E_OUTOFMEMORY;
3047 wrapper->session = session;
3049 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3051 return S_OK;
3054 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3055 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3056 ISimpleAudioVolume **out)
3058 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3059 AudioSession *session;
3060 AudioSessionWrapper *wrapper;
3061 HRESULT hr;
3063 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3064 flags, out);
3066 hr = get_audio_session(session_guid, This->device, 0, &session);
3067 if(FAILED(hr))
3068 return hr;
3070 wrapper = AudioSessionWrapper_Create(NULL);
3071 if(!wrapper)
3072 return E_OUTOFMEMORY;
3074 wrapper->session = session;
3076 *out = &wrapper->ISimpleAudioVolume_iface;
3078 return S_OK;
3081 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3082 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3084 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3085 FIXME("(%p)->(%p) - stub\n", This, out);
3086 return E_NOTIMPL;
3089 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3090 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3092 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3093 FIXME("(%p)->(%p) - stub\n", This, notification);
3094 return E_NOTIMPL;
3097 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3098 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3100 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3101 FIXME("(%p)->(%p) - stub\n", This, notification);
3102 return E_NOTIMPL;
3105 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3106 IAudioSessionManager2 *iface, const WCHAR *session_id,
3107 IAudioVolumeDuckNotification *notification)
3109 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3110 FIXME("(%p)->(%p) - stub\n", This, notification);
3111 return E_NOTIMPL;
3114 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3115 IAudioSessionManager2 *iface,
3116 IAudioVolumeDuckNotification *notification)
3118 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3119 FIXME("(%p)->(%p) - stub\n", This, notification);
3120 return E_NOTIMPL;
3123 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3125 AudioSessionManager_QueryInterface,
3126 AudioSessionManager_AddRef,
3127 AudioSessionManager_Release,
3128 AudioSessionManager_GetAudioSessionControl,
3129 AudioSessionManager_GetSimpleAudioVolume,
3130 AudioSessionManager_GetSessionEnumerator,
3131 AudioSessionManager_RegisterSessionNotification,
3132 AudioSessionManager_UnregisterSessionNotification,
3133 AudioSessionManager_RegisterDuckNotification,
3134 AudioSessionManager_UnregisterDuckNotification
3137 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3138 IAudioSessionManager2 **out)
3140 SessionMgr *This;
3142 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3143 if(!This)
3144 return E_OUTOFMEMORY;
3146 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3147 This->device = device;
3148 This->ref = 1;
3150 *out = &This->IAudioSessionManager2_iface;
3152 return S_OK;