kernel32/tests: Added more FoldString tests.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob9b0f5f8bd0d0b05fe61b218ccbff027ff124e0b6
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 = 200000;
61 static const REFERENCE_TIME MinimumPeriod = 100000;
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;
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;
120 UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
122 BYTE *local_buffer, *tmp_buffer;
123 int buf_state;
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 enum BufferStates {
136 NOT_LOCKED = 0,
137 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
138 LOCKED_WRAPPED /* public buffer piece is in tmp_buffer */
141 typedef struct _SessionMgr {
142 IAudioSessionManager2 IAudioSessionManager2_iface;
144 LONG ref;
146 IMMDevice *device;
147 } SessionMgr;
149 typedef struct _OSSDevice {
150 EDataFlow flow;
151 char devnode[OSS_DEVNODE_SIZE];
152 GUID guid;
154 struct list entry;
155 } OSSDevice;
157 static struct list g_devices = LIST_INIT(g_devices);
159 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
160 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
161 'w','i','n','e','o','s','s','.','d','r','v',0};
162 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
163 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
164 'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
165 static const WCHAR guidW[] = {'g','u','i','d',0};
167 static HANDLE g_timer_q;
169 static CRITICAL_SECTION g_sessions_lock;
170 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
172 0, 0, &g_sessions_lock,
173 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
174 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
176 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
177 static struct list g_sessions = LIST_INIT(g_sessions);
179 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
181 static const IAudioClientVtbl AudioClient_Vtbl;
182 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
183 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
184 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
185 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
186 static const IAudioClockVtbl AudioClock_Vtbl;
187 static const IAudioClock2Vtbl AudioClock2_Vtbl;
188 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
189 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
190 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
192 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
194 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
197 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
199 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
202 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
204 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
207 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
209 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
212 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
214 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
217 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
219 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
222 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
224 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
227 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
229 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
232 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
234 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
237 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
239 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
242 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
244 switch (reason)
246 case DLL_PROCESS_ATTACH:
247 g_timer_q = CreateTimerQueue();
248 if(!g_timer_q)
249 return FALSE;
250 break;
252 case DLL_PROCESS_DETACH:
254 OSSDevice *iter, *iter2;
256 DeleteCriticalSection(&g_sessions_lock);
258 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
259 HeapFree(GetProcessHeap(), 0, iter);
262 break;
265 return TRUE;
268 /* From <dlls/mmdevapi/mmdevapi.h> */
269 enum DriverPriority {
270 Priority_Unavailable = 0,
271 Priority_Low,
272 Priority_Neutral,
273 Priority_Preferred
276 int WINAPI AUDDRV_GetPriority(void)
278 int mixer_fd;
279 oss_sysinfo sysinfo;
281 /* Attempt to determine if we are running on OSS or ALSA's OSS
282 * compatibility layer. There is no official way to do that, so just check
283 * for validity as best as possible, without rejecting valid OSS
284 * implementations. */
286 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
287 if(mixer_fd < 0){
288 TRACE("Priority_Unavailable: open failed\n");
289 return Priority_Unavailable;
292 sysinfo.version[0] = 0xFF;
293 sysinfo.versionnum = ~0;
294 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
295 TRACE("Priority_Unavailable: ioctl failed\n");
296 close(mixer_fd);
297 return Priority_Unavailable;
300 close(mixer_fd);
302 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
303 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
304 return Priority_Low;
306 if(sysinfo.versionnum & 0x80000000){
307 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
308 return Priority_Low;
311 TRACE("Priority_Preferred: Seems like valid OSS!\n");
313 return Priority_Preferred;
316 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
317 GUID *guid)
319 HKEY key;
320 BOOL opened = FALSE;
321 LONG lr;
323 if(!drv_key){
324 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
325 NULL, &drv_key, NULL);
326 if(lr != ERROR_SUCCESS){
327 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
328 return;
330 opened = TRUE;
333 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
334 NULL, &key, NULL);
335 if(lr != ERROR_SUCCESS){
336 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
337 goto exit;
340 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
341 sizeof(GUID));
342 if(lr != ERROR_SUCCESS)
343 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
345 RegCloseKey(key);
346 exit:
347 if(opened)
348 RegCloseKey(drv_key);
351 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
353 HKEY key = NULL, dev_key;
354 DWORD type, size = sizeof(*guid);
355 WCHAR key_name[256];
357 if(flow == eCapture)
358 key_name[0] = '1';
359 else
360 key_name[0] = '0';
361 key_name[1] = ',';
362 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
363 (sizeof(key_name) / sizeof(*key_name)) - 2);
365 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
366 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
367 if(RegQueryValueExW(dev_key, guidW, 0, &type,
368 (BYTE*)guid, &size) == ERROR_SUCCESS){
369 if(type == REG_BINARY){
370 RegCloseKey(dev_key);
371 RegCloseKey(key);
372 return;
374 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
375 wine_dbgstr_w(key_name), type);
377 RegCloseKey(dev_key);
381 CoCreateGuid(guid);
383 set_device_guid(flow, key, key_name, guid);
385 if(key)
386 RegCloseKey(key);
389 static const char *oss_clean_devnode(const char *devnode)
391 static char ret[OSS_DEVNODE_SIZE];
393 const char *dot, *slash;
394 size_t len;
396 dot = strrchr(devnode, '.');
397 if(!dot)
398 return devnode;
400 slash = strrchr(devnode, '/');
401 if(slash && dot < slash)
402 return devnode;
404 len = dot - devnode;
406 memcpy(ret, devnode, len);
407 ret[len] = '\0';
409 return ret;
412 static UINT get_default_index(EDataFlow flow)
414 int fd = -1, err;
415 UINT i;
416 oss_audioinfo ai;
417 const char *devnode;
418 OSSDevice *dev_item;
420 if(flow == eRender)
421 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
422 else
423 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
425 if(fd < 0){
426 WARN("Couldn't open default device!\n");
427 return 0;
430 ai.dev = -1;
431 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
432 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
433 close(fd);
434 return 0;
437 close(fd);
439 TRACE("Default devnode: %s\n", ai.devnode);
440 devnode = oss_clean_devnode(ai.devnode);
441 i = 0;
442 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
443 if(dev_item->flow == flow){
444 if(!strcmp(devnode, dev_item->devnode))
445 return i;
446 ++i;
450 WARN("Couldn't find default device! Choosing first.\n");
451 return 0;
454 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
455 UINT *num, UINT *def_index)
457 int i, mixer_fd;
458 oss_sysinfo sysinfo;
459 static int print_once = 0;
461 static const WCHAR outW[] = {'O','u','t',':',' ',0};
462 static const WCHAR inW[] = {'I','n',':',' ',0};
464 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
466 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
467 if(mixer_fd < 0){
468 ERR("OSS /dev/mixer doesn't seem to exist\n");
469 return AUDCLNT_E_SERVICE_NOT_RUNNING;
472 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
473 close(mixer_fd);
475 if(errno == EINVAL){
476 ERR("OSS version too old, need at least OSSv4\n");
477 return AUDCLNT_E_SERVICE_NOT_RUNNING;
480 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
481 return E_FAIL;
484 if(!print_once){
485 TRACE("OSS sysinfo:\n");
486 TRACE("product: %s\n", sysinfo.product);
487 TRACE("version: %s\n", sysinfo.version);
488 TRACE("versionnum: %x\n", sysinfo.versionnum);
489 TRACE("numaudios: %d\n", sysinfo.numaudios);
490 TRACE("nummixers: %d\n", sysinfo.nummixers);
491 TRACE("numcards: %d\n", sysinfo.numcards);
492 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
493 print_once = 1;
496 if(sysinfo.numaudios <= 0){
497 WARN("No audio devices!\n");
498 close(mixer_fd);
499 return AUDCLNT_E_SERVICE_NOT_RUNNING;
502 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
503 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
505 *num = 0;
506 for(i = 0; i < sysinfo.numaudios; ++i){
507 oss_audioinfo ai = {0};
508 const char *devnode;
509 OSSDevice *dev_item;
510 int fd;
512 ai.dev = i;
513 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
514 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
515 strerror(errno));
516 continue;
519 devnode = oss_clean_devnode(ai.devnode);
521 /* check for duplicates */
522 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
523 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
524 break;
526 if(&dev_item->entry != &g_devices)
527 continue;
529 if(flow == eRender)
530 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
531 else
532 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
533 if(fd < 0){
534 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
535 devnode, errno, strerror(errno));
536 continue;
538 close(fd);
540 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
541 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
542 size_t len, prefix_len;
543 const WCHAR *prefix;
545 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
547 dev_item->flow = flow;
548 get_device_guid(flow, devnode, &dev_item->guid);
549 strcpy(dev_item->devnode, devnode);
551 (*guids)[*num] = dev_item->guid;
553 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
554 if(flow == eRender){
555 prefix = outW;
556 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
557 len += prefix_len;
558 }else{
559 prefix = inW;
560 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
561 len += prefix_len;
563 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
564 len * sizeof(WCHAR));
565 if(!(*ids)[*num]){
566 for(i = 0; i < *num; ++i)
567 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
568 HeapFree(GetProcessHeap(), 0, *ids);
569 HeapFree(GetProcessHeap(), 0, *guids);
570 HeapFree(GetProcessHeap(), 0, dev_item);
571 close(mixer_fd);
572 return E_OUTOFMEMORY;
574 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
575 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
576 (*ids)[*num] + prefix_len, len - prefix_len);
578 list_add_tail(&g_devices, &dev_item->entry);
580 (*num)++;
584 close(mixer_fd);
586 *def_index = get_default_index(flow);
588 return S_OK;
591 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
593 OSSDevice *dev_item;
594 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
595 if(IsEqualGUID(guid, &dev_item->guid))
596 return dev_item;
597 return NULL;
600 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
601 IAudioClient **out)
603 ACImpl *This;
604 const OSSDevice *oss_dev;
606 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
608 oss_dev = get_ossdevice_from_guid(guid);
609 if(!oss_dev){
610 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
611 return AUDCLNT_E_DEVICE_INVALIDATED;
614 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
615 if(!This)
616 return E_OUTOFMEMORY;
618 if(oss_dev->flow == eRender)
619 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
620 else if(oss_dev->flow == eCapture)
621 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
622 else{
623 HeapFree(GetProcessHeap(), 0, This);
624 return E_INVALIDARG;
626 if(This->fd < 0){
627 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
628 strerror(errno));
629 HeapFree(GetProcessHeap(), 0, This);
630 return AUDCLNT_E_DEVICE_INVALIDATED;
633 This->dataflow = oss_dev->flow;
635 This->ai.dev = -1;
636 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
637 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
638 errno, strerror(errno));
639 close(This->fd);
640 HeapFree(GetProcessHeap(), 0, This);
641 return E_FAIL;
644 strcpy(This->devnode, oss_dev->devnode);
646 TRACE("OSS audioinfo:\n");
647 TRACE("devnode: %s\n", This->ai.devnode);
648 TRACE("name: %s\n", This->ai.name);
649 TRACE("busy: %x\n", This->ai.busy);
650 TRACE("caps: %x\n", This->ai.caps);
651 TRACE("iformats: %x\n", This->ai.iformats);
652 TRACE("oformats: %x\n", This->ai.oformats);
653 TRACE("enabled: %d\n", This->ai.enabled);
654 TRACE("min_rate: %d\n", This->ai.min_rate);
655 TRACE("max_rate: %d\n", This->ai.max_rate);
656 TRACE("min_channels: %d\n", This->ai.min_channels);
657 TRACE("max_channels: %d\n", This->ai.max_channels);
659 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
660 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
661 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
662 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
663 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
664 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
666 InitializeCriticalSection(&This->lock);
667 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
669 This->parent = dev;
670 IMMDevice_AddRef(This->parent);
672 IAudioClient_AddRef(&This->IAudioClient_iface);
674 *out = &This->IAudioClient_iface;
676 return S_OK;
679 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
680 REFIID riid, void **ppv)
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 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;
710 ref = InterlockedDecrement(&This->ref);
711 TRACE("(%p) Refcount now %u\n", This, ref);
712 if(!ref){
713 IAudioClient_Stop(iface);
714 IMMDevice_Release(This->parent);
715 This->lock.DebugInfo->Spare[0] = 0;
716 DeleteCriticalSection(&This->lock);
717 close(This->fd);
718 if(This->initted){
719 EnterCriticalSection(&g_sessions_lock);
720 list_remove(&This->entry);
721 LeaveCriticalSection(&g_sessions_lock);
723 HeapFree(GetProcessHeap(), 0, This->vols);
724 HeapFree(GetProcessHeap(), 0, This->local_buffer);
725 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
726 CoTaskMemFree(This->fmt);
727 HeapFree(GetProcessHeap(), 0, This);
729 return ref;
732 static void dump_fmt(const WAVEFORMATEX *fmt)
734 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
735 switch(fmt->wFormatTag){
736 case WAVE_FORMAT_PCM:
737 TRACE("WAVE_FORMAT_PCM");
738 break;
739 case WAVE_FORMAT_IEEE_FLOAT:
740 TRACE("WAVE_FORMAT_IEEE_FLOAT");
741 break;
742 case WAVE_FORMAT_EXTENSIBLE:
743 TRACE("WAVE_FORMAT_EXTENSIBLE");
744 break;
745 default:
746 TRACE("Unknown");
747 break;
749 TRACE(")\n");
751 TRACE("nChannels: %u\n", fmt->nChannels);
752 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
753 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
754 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
755 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
756 TRACE("cbSize: %u\n", fmt->cbSize);
758 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
759 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
760 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
761 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
762 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
766 static DWORD get_channel_mask(unsigned int channels)
768 switch(channels){
769 case 0:
770 return 0;
771 case 1:
772 return KSAUDIO_SPEAKER_MONO;
773 case 2:
774 return KSAUDIO_SPEAKER_STEREO;
775 case 3:
776 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
777 case 4:
778 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
779 case 5:
780 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
781 case 6:
782 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
783 case 7:
784 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
785 case 8:
786 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
788 FIXME("Unknown speaker configuration: %u\n", channels);
789 return 0;
792 static int get_oss_format(const WAVEFORMATEX *fmt)
794 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
796 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
797 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
798 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
799 switch(fmt->wBitsPerSample){
800 case 8:
801 return AFMT_U8;
802 case 16:
803 return AFMT_S16_LE;
804 case 24:
805 return AFMT_S24_LE;
806 case 32:
807 return AFMT_S32_LE;
809 return -1;
812 #ifdef AFMT_FLOAT
813 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
814 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
815 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
816 if(fmt->wBitsPerSample != 32)
817 return -1;
819 return AFMT_FLOAT;
821 #endif
823 return -1;
826 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
828 WAVEFORMATEX *ret;
829 size_t size;
831 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
832 size = sizeof(WAVEFORMATEXTENSIBLE);
833 else
834 size = sizeof(WAVEFORMATEX);
836 ret = CoTaskMemAlloc(size);
837 if(!ret)
838 return NULL;
840 memcpy(ret, fmt, size);
842 ret->cbSize = size - sizeof(WAVEFORMATEX);
844 return ret;
847 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
848 WAVEFORMATEX **out, BOOL query)
850 int tmp, oss_format;
851 double tenth;
852 HRESULT ret = S_OK;
853 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
854 WAVEFORMATEX *closest = NULL;
856 tmp = oss_format = get_oss_format(fmt);
857 if(oss_format < 0)
858 return AUDCLNT_E_UNSUPPORTED_FORMAT;
859 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
860 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
861 return E_FAIL;
863 if(tmp != oss_format){
864 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
865 return AUDCLNT_E_UNSUPPORTED_FORMAT;
868 closest = clone_format(fmt);
869 if(!closest)
870 return E_OUTOFMEMORY;
872 tmp = fmt->nSamplesPerSec;
873 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
874 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
875 CoTaskMemFree(closest);
876 return E_FAIL;
878 tenth = fmt->nSamplesPerSec * 0.1;
879 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
880 ret = S_FALSE;
881 closest->nSamplesPerSec = tmp;
884 tmp = fmt->nChannels;
885 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
886 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
887 CoTaskMemFree(closest);
888 return E_FAIL;
890 if(tmp != fmt->nChannels){
891 ret = S_FALSE;
892 closest->nChannels = tmp;
895 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
896 DWORD mask = get_channel_mask(closest->nChannels);
898 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
900 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
901 fmtex->dwChannelMask != 0 &&
902 fmtex->dwChannelMask != mask)
903 ret = S_FALSE;
906 if(ret == S_FALSE && !out)
907 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
909 if(ret == S_FALSE && out){
910 closest->nBlockAlign =
911 closest->nChannels * closest->wBitsPerSample / 8;
912 closest->nAvgBytesPerSec =
913 closest->nBlockAlign * closest->nSamplesPerSec;
914 *out = closest;
915 } else
916 CoTaskMemFree(closest);
918 TRACE("returning: %08x\n", ret);
919 return ret;
922 static void session_init_vols(AudioSession *session, UINT channels)
924 if(session->channel_count < channels){
925 UINT i;
927 if(session->channel_vols)
928 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
929 session->channel_vols, sizeof(float) * channels);
930 else
931 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
932 sizeof(float) * channels);
933 if(!session->channel_vols)
934 return;
936 for(i = session->channel_count; i < channels; ++i)
937 session->channel_vols[i] = 1.f;
939 session->channel_count = channels;
943 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
944 UINT num_channels)
946 AudioSession *ret;
948 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
949 if(!ret)
950 return NULL;
952 memcpy(&ret->guid, guid, sizeof(GUID));
954 ret->device = device;
956 list_init(&ret->clients);
958 list_add_head(&g_sessions, &ret->entry);
960 InitializeCriticalSection(&ret->lock);
961 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
963 session_init_vols(ret, num_channels);
965 ret->master_vol = 1.f;
967 return ret;
970 /* if channels == 0, then this will return or create a session with
971 * matching dataflow and GUID. otherwise, channels must also match */
972 static HRESULT get_audio_session(const GUID *sessionguid,
973 IMMDevice *device, UINT channels, AudioSession **out)
975 AudioSession *session;
977 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
978 *out = create_session(&GUID_NULL, device, channels);
979 if(!*out)
980 return E_OUTOFMEMORY;
982 return S_OK;
985 *out = NULL;
986 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
987 if(session->device == device &&
988 IsEqualGUID(sessionguid, &session->guid)){
989 session_init_vols(session, channels);
990 *out = session;
991 break;
995 if(!*out){
996 *out = create_session(sessionguid, device, channels);
997 if(!*out)
998 return E_OUTOFMEMORY;
1001 return S_OK;
1004 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1005 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1006 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1007 const GUID *sessionguid)
1009 ACImpl *This = impl_from_IAudioClient(iface);
1010 int i;
1011 HRESULT hr;
1013 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1014 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1016 if(!fmt)
1017 return E_POINTER;
1019 dump_fmt(fmt);
1021 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1022 return AUDCLNT_E_NOT_INITIALIZED;
1024 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1025 AUDCLNT_STREAMFLAGS_LOOPBACK |
1026 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1027 AUDCLNT_STREAMFLAGS_NOPERSIST |
1028 AUDCLNT_STREAMFLAGS_RATEADJUST |
1029 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1030 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1031 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1032 TRACE("Unknown flags: %08x\n", flags);
1033 return E_INVALIDARG;
1036 if(mode == AUDCLNT_SHAREMODE_SHARED){
1037 period = DefaultPeriod;
1038 if( duration < 3 * period)
1039 duration = 3 * period;
1040 }else{
1041 if(!period)
1042 period = DefaultPeriod; /* not minimum */
1043 if(period < MinimumPeriod || period > 5000000)
1044 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1045 if(duration > 20000000) /* the smaller the period, the lower this limit */
1046 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1047 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1048 if(duration != period)
1049 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1050 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1051 return AUDCLNT_E_DEVICE_IN_USE;
1052 }else{
1053 if( duration < 8 * period)
1054 duration = 8 * period; /* may grow above 2s */
1058 EnterCriticalSection(&This->lock);
1060 if(This->initted){
1061 LeaveCriticalSection(&This->lock);
1062 return AUDCLNT_E_ALREADY_INITIALIZED;
1065 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
1066 if(FAILED(hr)){
1067 LeaveCriticalSection(&This->lock);
1068 return hr;
1071 This->fmt = clone_format(fmt);
1072 if(!This->fmt){
1073 LeaveCriticalSection(&This->lock);
1074 return E_OUTOFMEMORY;
1077 This->period_us = period / 10;
1078 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1080 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1081 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1082 This->bufsize_frames * fmt->nBlockAlign);
1083 if(!This->local_buffer){
1084 CoTaskMemFree(This->fmt);
1085 This->fmt = NULL;
1086 LeaveCriticalSection(&This->lock);
1087 return E_OUTOFMEMORY;
1090 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1091 if(!This->vols){
1092 CoTaskMemFree(This->fmt);
1093 This->fmt = NULL;
1094 LeaveCriticalSection(&This->lock);
1095 return E_OUTOFMEMORY;
1098 for(i = 0; i < fmt->nChannels; ++i)
1099 This->vols[i] = 1.f;
1101 This->share = mode;
1102 This->flags = flags;
1103 This->oss_bufsize_bytes = 0;
1105 EnterCriticalSection(&g_sessions_lock);
1107 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1108 &This->session);
1109 if(FAILED(hr)){
1110 LeaveCriticalSection(&g_sessions_lock);
1111 HeapFree(GetProcessHeap(), 0, This->vols);
1112 This->vols = NULL;
1113 CoTaskMemFree(This->fmt);
1114 This->fmt = NULL;
1115 LeaveCriticalSection(&This->lock);
1116 return hr;
1119 list_add_tail(&This->session->clients, &This->entry);
1121 LeaveCriticalSection(&g_sessions_lock);
1123 This->initted = TRUE;
1125 LeaveCriticalSection(&This->lock);
1127 return S_OK;
1130 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1131 UINT32 *frames)
1133 ACImpl *This = impl_from_IAudioClient(iface);
1135 TRACE("(%p)->(%p)\n", This, frames);
1137 if(!frames)
1138 return E_POINTER;
1140 EnterCriticalSection(&This->lock);
1142 if(!This->initted){
1143 LeaveCriticalSection(&This->lock);
1144 return AUDCLNT_E_NOT_INITIALIZED;
1147 *frames = This->bufsize_frames;
1149 TRACE("buffer size: %u\n", *frames);
1151 LeaveCriticalSection(&This->lock);
1153 return S_OK;
1156 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1157 REFERENCE_TIME *latency)
1159 ACImpl *This = impl_from_IAudioClient(iface);
1161 TRACE("(%p)->(%p)\n", This, latency);
1163 if(!latency)
1164 return E_POINTER;
1166 EnterCriticalSection(&This->lock);
1168 if(!This->initted){
1169 LeaveCriticalSection(&This->lock);
1170 return AUDCLNT_E_NOT_INITIALIZED;
1173 /* pretend we process audio in Period chunks, so max latency includes
1174 * the period time. Some native machines add .6666ms in shared mode. */
1175 *latency = This->period_us * 10 + 6666;
1177 LeaveCriticalSection(&This->lock);
1179 return S_OK;
1182 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1183 UINT32 *numpad)
1185 ACImpl *This = impl_from_IAudioClient(iface);
1187 TRACE("(%p)->(%p)\n", This, numpad);
1189 if(!numpad)
1190 return E_POINTER;
1192 EnterCriticalSection(&This->lock);
1194 if(!This->initted){
1195 LeaveCriticalSection(&This->lock);
1196 return AUDCLNT_E_NOT_INITIALIZED;
1199 *numpad = This->held_frames;
1201 TRACE("padding: %u\n", *numpad);
1203 LeaveCriticalSection(&This->lock);
1205 return S_OK;
1208 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1209 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1210 WAVEFORMATEX **outpwfx)
1212 ACImpl *This = impl_from_IAudioClient(iface);
1213 int fd = -1;
1214 HRESULT ret;
1216 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1218 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1219 return E_POINTER;
1221 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1222 return E_INVALIDARG;
1224 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1225 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1226 return E_INVALIDARG;
1228 dump_fmt(pwfx);
1230 if(outpwfx){
1231 *outpwfx = NULL;
1232 if(mode != AUDCLNT_SHAREMODE_SHARED)
1233 outpwfx = NULL;
1236 if(This->dataflow == eRender)
1237 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1238 else if(This->dataflow == eCapture)
1239 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1241 if(fd < 0){
1242 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1243 strerror(errno));
1244 return AUDCLNT_E_DEVICE_INVALIDATED;
1247 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1249 close(fd);
1251 return ret;
1254 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1255 WAVEFORMATEX **pwfx)
1257 ACImpl *This = impl_from_IAudioClient(iface);
1258 WAVEFORMATEXTENSIBLE *fmt;
1259 int formats;
1261 TRACE("(%p)->(%p)\n", This, pwfx);
1263 if(!pwfx)
1264 return E_POINTER;
1265 *pwfx = NULL;
1267 if(This->dataflow == eRender)
1268 formats = This->ai.oformats;
1269 else if(This->dataflow == eCapture)
1270 formats = This->ai.iformats;
1271 else
1272 return E_UNEXPECTED;
1274 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1275 if(!fmt)
1276 return E_OUTOFMEMORY;
1278 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1279 if(formats & AFMT_S16_LE){
1280 fmt->Format.wBitsPerSample = 16;
1281 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1282 #ifdef AFMT_FLOAT
1283 }else if(formats & AFMT_FLOAT){
1284 fmt->Format.wBitsPerSample = 32;
1285 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1286 #endif
1287 }else if(formats & AFMT_U8){
1288 fmt->Format.wBitsPerSample = 8;
1289 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1290 }else if(formats & AFMT_S32_LE){
1291 fmt->Format.wBitsPerSample = 32;
1292 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1293 }else if(formats & AFMT_S24_LE){
1294 fmt->Format.wBitsPerSample = 24;
1295 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1296 }else{
1297 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1298 CoTaskMemFree(fmt);
1299 return E_FAIL;
1302 fmt->Format.nChannels = This->ai.max_channels;
1303 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1304 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1306 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1307 fmt->Format.nChannels) / 8;
1308 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1309 fmt->Format.nBlockAlign;
1311 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1312 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1314 *pwfx = (WAVEFORMATEX*)fmt;
1315 dump_fmt(*pwfx);
1317 return S_OK;
1320 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1321 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1323 ACImpl *This = impl_from_IAudioClient(iface);
1325 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1327 if(!defperiod && !minperiod)
1328 return E_POINTER;
1330 if(defperiod)
1331 *defperiod = DefaultPeriod;
1332 if(minperiod)
1333 *minperiod = MinimumPeriod;
1335 return S_OK;
1338 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1340 if(This->fmt->wBitsPerSample == 8)
1341 memset(buf, 128, frames * This->fmt->nBlockAlign);
1342 else
1343 memset(buf, 0, frames * This->fmt->nBlockAlign);
1346 static void oss_write_data(ACImpl *This)
1348 ssize_t written_bytes;
1349 UINT32 written_frames, in_oss_frames, write_limit, max_period;
1350 size_t to_write_frames, to_write_bytes;
1351 audio_buf_info bi;
1352 BYTE *buf =
1353 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1355 if(This->held_frames == 0)
1356 return;
1358 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1359 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1360 else
1361 to_write_frames = This->held_frames;
1363 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1364 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1365 return;
1368 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1370 if(bi.bytes > This->oss_bufsize_bytes){
1371 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1372 bi.bytes, This->oss_bufsize_bytes);
1373 This->oss_bufsize_bytes = bi.bytes;
1374 in_oss_frames = 0;
1375 }else if(This->oss_bufsize_bytes - bi.bytes <= bi.fragsize)
1376 in_oss_frames = 0;
1377 else
1378 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1380 write_limit = 0;
1381 while(write_limit + in_oss_frames < max_period * 3)
1382 write_limit += max_period;
1383 if(write_limit == 0)
1384 return;
1386 to_write_frames = min(to_write_frames, write_limit);
1387 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1389 if(This->session->mute)
1390 oss_silence_buffer(This, buf, to_write_frames);
1392 written_bytes = write(This->fd, buf, to_write_bytes);
1393 if(written_bytes < 0){
1394 /* EAGAIN is OSS buffer full, log that too */
1395 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1396 return;
1398 written_frames = written_bytes / This->fmt->nBlockAlign;
1400 This->lcl_offs_frames += written_frames;
1401 This->lcl_offs_frames %= This->bufsize_frames;
1402 This->held_frames -= written_frames;
1404 if(written_frames < to_write_frames){
1405 /* OSS buffer probably full */
1406 return;
1409 if(This->held_frames && written_frames < write_limit){
1410 /* wrapped and have some data back at the start to write */
1412 to_write_frames = min(write_limit - written_frames, This->held_frames);
1413 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1415 if(This->session->mute)
1416 oss_silence_buffer(This, This->local_buffer, to_write_frames);
1418 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1419 if(written_bytes < 0){
1420 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1421 return;
1423 written_frames = written_bytes / This->fmt->nBlockAlign;
1425 This->lcl_offs_frames += written_frames;
1426 This->lcl_offs_frames %= This->bufsize_frames;
1427 This->held_frames -= written_frames;
1431 static void oss_read_data(ACImpl *This)
1433 UINT64 pos, readable;
1434 audio_buf_info bi;
1435 ssize_t nread;
1437 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1438 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1439 return;
1442 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1443 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1445 if(bi.bytes < readable)
1446 readable = bi.bytes;
1448 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1449 readable);
1450 if(nread < 0){
1451 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1452 return;
1455 This->held_frames += nread / This->fmt->nBlockAlign;
1457 if(This->held_frames > This->bufsize_frames){
1458 WARN("Overflow of unread data\n");
1459 This->lcl_offs_frames += This->held_frames;
1460 This->lcl_offs_frames %= This->bufsize_frames;
1461 This->held_frames = This->bufsize_frames;
1465 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1467 ACImpl *This = user;
1469 EnterCriticalSection(&This->lock);
1471 if(This->playing){
1472 if(This->dataflow == eRender && This->held_frames)
1473 oss_write_data(This);
1474 else if(This->dataflow == eCapture)
1475 oss_read_data(This);
1477 if(This->event)
1478 SetEvent(This->event);
1481 LeaveCriticalSection(&This->lock);
1484 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1486 ACImpl *This = impl_from_IAudioClient(iface);
1488 TRACE("(%p)\n", This);
1490 EnterCriticalSection(&This->lock);
1492 if(!This->initted){
1493 LeaveCriticalSection(&This->lock);
1494 return AUDCLNT_E_NOT_INITIALIZED;
1497 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1498 LeaveCriticalSection(&This->lock);
1499 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1502 if(This->playing){
1503 LeaveCriticalSection(&This->lock);
1504 return AUDCLNT_E_NOT_STOPPED;
1507 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1508 oss_period_callback, This, 0, This->period_us / 1000,
1509 WT_EXECUTEINTIMERTHREAD))
1510 ERR("Unable to create period timer: %u\n", GetLastError());
1512 This->playing = TRUE;
1514 LeaveCriticalSection(&This->lock);
1516 return S_OK;
1519 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1521 ACImpl *This = impl_from_IAudioClient(iface);
1522 HANDLE event;
1523 DWORD wait;
1525 TRACE("(%p)\n", This);
1527 EnterCriticalSection(&This->lock);
1529 if(!This->initted){
1530 LeaveCriticalSection(&This->lock);
1531 return AUDCLNT_E_NOT_INITIALIZED;
1534 if(!This->playing){
1535 LeaveCriticalSection(&This->lock);
1536 return S_FALSE;
1539 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1540 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1541 if(wait)
1542 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1543 wait = wait && GetLastError() == ERROR_IO_PENDING;
1545 This->playing = FALSE;
1547 LeaveCriticalSection(&This->lock);
1549 if(event && wait)
1550 WaitForSingleObject(event, INFINITE);
1551 CloseHandle(event);
1553 return S_OK;
1556 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1558 ACImpl *This = impl_from_IAudioClient(iface);
1560 TRACE("(%p)\n", This);
1562 EnterCriticalSection(&This->lock);
1564 if(!This->initted){
1565 LeaveCriticalSection(&This->lock);
1566 return AUDCLNT_E_NOT_INITIALIZED;
1569 if(This->playing){
1570 LeaveCriticalSection(&This->lock);
1571 return AUDCLNT_E_NOT_STOPPED;
1574 if(This->buf_state != NOT_LOCKED || This->getbuf_last){
1575 LeaveCriticalSection(&This->lock);
1576 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1579 if(This->dataflow == eRender){
1580 This->written_frames = 0;
1581 }else{
1582 This->written_frames += This->held_frames;
1584 This->lcl_offs_frames = 0;
1585 This->held_frames = 0;
1586 This->last_pos_frames = 0;
1588 LeaveCriticalSection(&This->lock);
1590 return S_OK;
1593 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1594 HANDLE event)
1596 ACImpl *This = impl_from_IAudioClient(iface);
1598 TRACE("(%p)->(%p)\n", This, event);
1600 if(!event)
1601 return E_INVALIDARG;
1603 EnterCriticalSection(&This->lock);
1605 if(!This->initted){
1606 LeaveCriticalSection(&This->lock);
1607 return AUDCLNT_E_NOT_INITIALIZED;
1610 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1611 LeaveCriticalSection(&This->lock);
1612 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1615 This->event = event;
1617 LeaveCriticalSection(&This->lock);
1619 return S_OK;
1622 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1623 void **ppv)
1625 ACImpl *This = impl_from_IAudioClient(iface);
1627 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1629 if(!ppv)
1630 return E_POINTER;
1631 *ppv = NULL;
1633 EnterCriticalSection(&This->lock);
1635 if(!This->initted){
1636 LeaveCriticalSection(&This->lock);
1637 return AUDCLNT_E_NOT_INITIALIZED;
1640 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1641 if(This->dataflow != eRender){
1642 LeaveCriticalSection(&This->lock);
1643 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1645 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1646 *ppv = &This->IAudioRenderClient_iface;
1647 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1648 if(This->dataflow != eCapture){
1649 LeaveCriticalSection(&This->lock);
1650 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1652 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1653 *ppv = &This->IAudioCaptureClient_iface;
1654 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1655 IAudioClock_AddRef(&This->IAudioClock_iface);
1656 *ppv = &This->IAudioClock_iface;
1657 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1658 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1659 *ppv = &This->IAudioStreamVolume_iface;
1660 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1661 if(!This->session_wrapper){
1662 This->session_wrapper = AudioSessionWrapper_Create(This);
1663 if(!This->session_wrapper){
1664 LeaveCriticalSection(&This->lock);
1665 return E_OUTOFMEMORY;
1667 }else
1668 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1670 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1671 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1672 if(!This->session_wrapper){
1673 This->session_wrapper = AudioSessionWrapper_Create(This);
1674 if(!This->session_wrapper){
1675 LeaveCriticalSection(&This->lock);
1676 return E_OUTOFMEMORY;
1678 }else
1679 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1681 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1682 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1683 if(!This->session_wrapper){
1684 This->session_wrapper = AudioSessionWrapper_Create(This);
1685 if(!This->session_wrapper){
1686 LeaveCriticalSection(&This->lock);
1687 return E_OUTOFMEMORY;
1689 }else
1690 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1692 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1695 if(*ppv){
1696 LeaveCriticalSection(&This->lock);
1697 return S_OK;
1700 LeaveCriticalSection(&This->lock);
1702 FIXME("stub %s\n", debugstr_guid(riid));
1703 return E_NOINTERFACE;
1706 static const IAudioClientVtbl AudioClient_Vtbl =
1708 AudioClient_QueryInterface,
1709 AudioClient_AddRef,
1710 AudioClient_Release,
1711 AudioClient_Initialize,
1712 AudioClient_GetBufferSize,
1713 AudioClient_GetStreamLatency,
1714 AudioClient_GetCurrentPadding,
1715 AudioClient_IsFormatSupported,
1716 AudioClient_GetMixFormat,
1717 AudioClient_GetDevicePeriod,
1718 AudioClient_Start,
1719 AudioClient_Stop,
1720 AudioClient_Reset,
1721 AudioClient_SetEventHandle,
1722 AudioClient_GetService
1725 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1726 IAudioRenderClient *iface, REFIID riid, void **ppv)
1728 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1730 if(!ppv)
1731 return E_POINTER;
1732 *ppv = NULL;
1734 if(IsEqualIID(riid, &IID_IUnknown) ||
1735 IsEqualIID(riid, &IID_IAudioRenderClient))
1736 *ppv = iface;
1737 if(*ppv){
1738 IUnknown_AddRef((IUnknown*)*ppv);
1739 return S_OK;
1742 WARN("Unknown interface %s\n", debugstr_guid(riid));
1743 return E_NOINTERFACE;
1746 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1748 ACImpl *This = impl_from_IAudioRenderClient(iface);
1749 return AudioClient_AddRef(&This->IAudioClient_iface);
1752 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1754 ACImpl *This = impl_from_IAudioRenderClient(iface);
1755 return AudioClient_Release(&This->IAudioClient_iface);
1758 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1759 UINT32 frames, BYTE **data)
1761 ACImpl *This = impl_from_IAudioRenderClient(iface);
1762 UINT32 pad, write_pos;
1763 HRESULT hr;
1765 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1767 if(!data)
1768 return E_POINTER;
1770 *data = NULL;
1772 EnterCriticalSection(&This->lock);
1774 if(This->getbuf_last){
1775 LeaveCriticalSection(&This->lock);
1776 return AUDCLNT_E_OUT_OF_ORDER;
1779 if(!frames){
1780 LeaveCriticalSection(&This->lock);
1781 return S_OK;
1784 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1785 if(FAILED(hr)){
1786 LeaveCriticalSection(&This->lock);
1787 return hr;
1790 if(pad + frames > This->bufsize_frames){
1791 LeaveCriticalSection(&This->lock);
1792 return AUDCLNT_E_BUFFER_TOO_LARGE;
1795 write_pos =
1796 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1797 if(write_pos + frames > This->bufsize_frames){
1798 if(This->tmp_buffer_frames < frames){
1799 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1800 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1801 frames * This->fmt->nBlockAlign);
1802 if(!This->tmp_buffer){
1803 LeaveCriticalSection(&This->lock);
1804 return E_OUTOFMEMORY;
1806 This->tmp_buffer_frames = frames;
1808 *data = This->tmp_buffer;
1809 This->getbuf_last = -frames;
1810 }else{
1811 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1812 This->getbuf_last = frames;
1815 LeaveCriticalSection(&This->lock);
1817 return S_OK;
1820 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1822 UINT32 write_offs_frames =
1823 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1824 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1825 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1826 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1827 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1829 if(written_bytes <= chunk_bytes){
1830 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1831 }else{
1832 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1833 memcpy(This->local_buffer, buffer + chunk_bytes,
1834 written_bytes - chunk_bytes);
1838 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1839 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1841 ACImpl *This = impl_from_IAudioRenderClient(iface);
1842 BYTE *buffer;
1844 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1846 EnterCriticalSection(&This->lock);
1848 if(!written_frames){
1849 This->getbuf_last = 0;
1850 LeaveCriticalSection(&This->lock);
1851 return S_OK;
1854 if(!This->getbuf_last){
1855 LeaveCriticalSection(&This->lock);
1856 return AUDCLNT_E_OUT_OF_ORDER;
1859 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1860 LeaveCriticalSection(&This->lock);
1861 return AUDCLNT_E_INVALID_SIZE;
1864 if(This->getbuf_last >= 0)
1865 buffer = This->local_buffer + This->fmt->nBlockAlign *
1866 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1867 else
1868 buffer = This->tmp_buffer;
1870 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1871 oss_silence_buffer(This, buffer, written_frames);
1873 if(This->getbuf_last < 0)
1874 oss_wrap_buffer(This, buffer, written_frames);
1876 This->held_frames += written_frames;
1877 This->written_frames += written_frames;
1878 This->getbuf_last = 0;
1880 LeaveCriticalSection(&This->lock);
1882 return S_OK;
1885 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1886 AudioRenderClient_QueryInterface,
1887 AudioRenderClient_AddRef,
1888 AudioRenderClient_Release,
1889 AudioRenderClient_GetBuffer,
1890 AudioRenderClient_ReleaseBuffer
1893 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1894 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1896 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1898 if(!ppv)
1899 return E_POINTER;
1900 *ppv = NULL;
1902 if(IsEqualIID(riid, &IID_IUnknown) ||
1903 IsEqualIID(riid, &IID_IAudioCaptureClient))
1904 *ppv = iface;
1905 if(*ppv){
1906 IUnknown_AddRef((IUnknown*)*ppv);
1907 return S_OK;
1910 WARN("Unknown interface %s\n", debugstr_guid(riid));
1911 return E_NOINTERFACE;
1914 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1916 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1917 return IAudioClient_AddRef(&This->IAudioClient_iface);
1920 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1922 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1923 return IAudioClient_Release(&This->IAudioClient_iface);
1926 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1927 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1928 UINT64 *qpcpos)
1930 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1931 HRESULT hr;
1933 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1934 devpos, qpcpos);
1936 if(!data || !frames || !flags)
1937 return E_POINTER;
1939 EnterCriticalSection(&This->lock);
1941 if(This->buf_state != NOT_LOCKED){
1942 LeaveCriticalSection(&This->lock);
1943 return AUDCLNT_E_OUT_OF_ORDER;
1946 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1947 if(FAILED(hr)){
1948 LeaveCriticalSection(&This->lock);
1949 return hr;
1952 *flags = 0;
1954 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1955 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1956 if(This->tmp_buffer_frames < *frames){
1957 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1958 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1959 *frames * This->fmt->nBlockAlign);
1960 if(!This->tmp_buffer){
1961 LeaveCriticalSection(&This->lock);
1962 return E_OUTOFMEMORY;
1964 This->tmp_buffer_frames = *frames;
1967 *data = This->tmp_buffer;
1968 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1969 This->fmt->nBlockAlign;
1970 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1971 frames_bytes = *frames * This->fmt->nBlockAlign;
1972 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1973 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
1974 frames_bytes - chunk_bytes);
1975 }else
1976 *data = This->local_buffer +
1977 This->lcl_offs_frames * This->fmt->nBlockAlign;
1979 This->buf_state = LOCKED_NORMAL;
1981 if(devpos || qpcpos)
1982 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1984 LeaveCriticalSection(&This->lock);
1986 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1989 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1990 IAudioCaptureClient *iface, UINT32 done)
1992 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1994 TRACE("(%p)->(%u)\n", This, done);
1996 EnterCriticalSection(&This->lock);
1998 if(This->buf_state == NOT_LOCKED){
1999 LeaveCriticalSection(&This->lock);
2000 return AUDCLNT_E_OUT_OF_ORDER;
2003 This->held_frames -= done;
2004 This->lcl_offs_frames += done;
2005 This->lcl_offs_frames %= This->bufsize_frames;
2007 This->buf_state = NOT_LOCKED;
2009 LeaveCriticalSection(&This->lock);
2011 return S_OK;
2014 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2015 IAudioCaptureClient *iface, UINT32 *frames)
2017 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2019 TRACE("(%p)->(%p)\n", This, frames);
2021 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
2024 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2026 AudioCaptureClient_QueryInterface,
2027 AudioCaptureClient_AddRef,
2028 AudioCaptureClient_Release,
2029 AudioCaptureClient_GetBuffer,
2030 AudioCaptureClient_ReleaseBuffer,
2031 AudioCaptureClient_GetNextPacketSize
2034 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2035 REFIID riid, void **ppv)
2037 ACImpl *This = impl_from_IAudioClock(iface);
2039 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2041 if(!ppv)
2042 return E_POINTER;
2043 *ppv = NULL;
2045 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2046 *ppv = iface;
2047 else if(IsEqualIID(riid, &IID_IAudioClock2))
2048 *ppv = &This->IAudioClock2_iface;
2049 if(*ppv){
2050 IUnknown_AddRef((IUnknown*)*ppv);
2051 return S_OK;
2054 WARN("Unknown interface %s\n", debugstr_guid(riid));
2055 return E_NOINTERFACE;
2058 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2060 ACImpl *This = impl_from_IAudioClock(iface);
2061 return IAudioClient_AddRef(&This->IAudioClient_iface);
2064 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2066 ACImpl *This = impl_from_IAudioClock(iface);
2067 return IAudioClient_Release(&This->IAudioClient_iface);
2070 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2072 ACImpl *This = impl_from_IAudioClock(iface);
2074 TRACE("(%p)->(%p)\n", This, freq);
2076 *freq = This->fmt->nSamplesPerSec;
2078 return S_OK;
2081 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2082 UINT64 *qpctime)
2084 ACImpl *This = impl_from_IAudioClock(iface);
2085 int delay;
2087 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2089 if(!pos)
2090 return E_POINTER;
2092 EnterCriticalSection(&This->lock);
2094 if(This->dataflow == eRender){
2095 if(!This->playing || !This->held_frames ||
2096 ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay) < 0)
2097 delay = 0;
2098 else
2099 delay /= This->fmt->nBlockAlign;
2100 if(This->held_frames + delay >= This->written_frames)
2101 *pos = This->last_pos_frames;
2102 else{
2103 *pos = This->written_frames - This->held_frames - delay;
2104 if(*pos < This->last_pos_frames)
2105 *pos = This->last_pos_frames;
2107 }else if(This->dataflow == eCapture){
2108 audio_buf_info bi;
2109 UINT32 held;
2111 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2112 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2113 held = 0;
2114 }else{
2115 if(bi.bytes <= bi.fragsize)
2116 held = 0;
2117 else
2118 held = bi.bytes / This->fmt->nBlockAlign;
2121 *pos = This->written_frames + held;
2124 This->last_pos_frames = *pos;
2126 LeaveCriticalSection(&This->lock);
2128 if(qpctime){
2129 LARGE_INTEGER stamp, freq;
2130 QueryPerformanceCounter(&stamp);
2131 QueryPerformanceFrequency(&freq);
2132 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2135 return S_OK;
2138 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2139 DWORD *chars)
2141 ACImpl *This = impl_from_IAudioClock(iface);
2143 TRACE("(%p)->(%p)\n", This, chars);
2145 if(!chars)
2146 return E_POINTER;
2148 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2150 return S_OK;
2153 static const IAudioClockVtbl AudioClock_Vtbl =
2155 AudioClock_QueryInterface,
2156 AudioClock_AddRef,
2157 AudioClock_Release,
2158 AudioClock_GetFrequency,
2159 AudioClock_GetPosition,
2160 AudioClock_GetCharacteristics
2163 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2164 REFIID riid, void **ppv)
2166 ACImpl *This = impl_from_IAudioClock2(iface);
2167 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2170 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2172 ACImpl *This = impl_from_IAudioClock2(iface);
2173 return IAudioClient_AddRef(&This->IAudioClient_iface);
2176 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2178 ACImpl *This = impl_from_IAudioClock2(iface);
2179 return IAudioClient_Release(&This->IAudioClient_iface);
2182 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2183 UINT64 *pos, UINT64 *qpctime)
2185 ACImpl *This = impl_from_IAudioClock2(iface);
2187 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2189 return E_NOTIMPL;
2192 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2194 AudioClock2_QueryInterface,
2195 AudioClock2_AddRef,
2196 AudioClock2_Release,
2197 AudioClock2_GetDevicePosition
2200 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2202 AudioSessionWrapper *ret;
2204 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2205 sizeof(AudioSessionWrapper));
2206 if(!ret)
2207 return NULL;
2209 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2210 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2211 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2213 ret->ref = 1;
2215 ret->client = client;
2216 if(client){
2217 ret->session = client->session;
2218 AudioClient_AddRef(&client->IAudioClient_iface);
2221 return ret;
2224 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2225 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2227 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2229 if(!ppv)
2230 return E_POINTER;
2231 *ppv = NULL;
2233 if(IsEqualIID(riid, &IID_IUnknown) ||
2234 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2235 IsEqualIID(riid, &IID_IAudioSessionControl2))
2236 *ppv = iface;
2237 if(*ppv){
2238 IUnknown_AddRef((IUnknown*)*ppv);
2239 return S_OK;
2242 WARN("Unknown interface %s\n", debugstr_guid(riid));
2243 return E_NOINTERFACE;
2246 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2248 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2249 ULONG ref;
2250 ref = InterlockedIncrement(&This->ref);
2251 TRACE("(%p) Refcount now %u\n", This, ref);
2252 return ref;
2255 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2257 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2258 ULONG ref;
2259 ref = InterlockedDecrement(&This->ref);
2260 TRACE("(%p) Refcount now %u\n", This, ref);
2261 if(!ref){
2262 if(This->client){
2263 EnterCriticalSection(&This->client->lock);
2264 This->client->session_wrapper = NULL;
2265 LeaveCriticalSection(&This->client->lock);
2266 AudioClient_Release(&This->client->IAudioClient_iface);
2268 HeapFree(GetProcessHeap(), 0, This);
2270 return ref;
2273 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2274 AudioSessionState *state)
2276 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2277 ACImpl *client;
2279 TRACE("(%p)->(%p)\n", This, state);
2281 if(!state)
2282 return NULL_PTR_ERR;
2284 EnterCriticalSection(&g_sessions_lock);
2286 if(list_empty(&This->session->clients)){
2287 *state = AudioSessionStateExpired;
2288 LeaveCriticalSection(&g_sessions_lock);
2289 return S_OK;
2292 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2293 EnterCriticalSection(&client->lock);
2294 if(client->playing){
2295 *state = AudioSessionStateActive;
2296 LeaveCriticalSection(&client->lock);
2297 LeaveCriticalSection(&g_sessions_lock);
2298 return S_OK;
2300 LeaveCriticalSection(&client->lock);
2303 LeaveCriticalSection(&g_sessions_lock);
2305 *state = AudioSessionStateInactive;
2307 return S_OK;
2310 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2311 IAudioSessionControl2 *iface, WCHAR **name)
2313 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2315 FIXME("(%p)->(%p) - stub\n", This, name);
2317 return E_NOTIMPL;
2320 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2321 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2323 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2325 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2327 return E_NOTIMPL;
2330 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2331 IAudioSessionControl2 *iface, WCHAR **path)
2333 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2335 FIXME("(%p)->(%p) - stub\n", This, path);
2337 return E_NOTIMPL;
2340 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2341 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2343 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2345 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2347 return E_NOTIMPL;
2350 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2351 IAudioSessionControl2 *iface, GUID *group)
2353 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2355 FIXME("(%p)->(%p) - stub\n", This, group);
2357 return E_NOTIMPL;
2360 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2361 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2363 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2365 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2366 debugstr_guid(session));
2368 return E_NOTIMPL;
2371 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2372 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2374 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2376 FIXME("(%p)->(%p) - stub\n", This, events);
2378 return S_OK;
2381 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2382 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2384 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2386 FIXME("(%p)->(%p) - stub\n", This, events);
2388 return S_OK;
2391 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2392 IAudioSessionControl2 *iface, WCHAR **id)
2394 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2396 FIXME("(%p)->(%p) - stub\n", This, id);
2398 return E_NOTIMPL;
2401 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2402 IAudioSessionControl2 *iface, WCHAR **id)
2404 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2406 FIXME("(%p)->(%p) - stub\n", This, id);
2408 return E_NOTIMPL;
2411 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2412 IAudioSessionControl2 *iface, DWORD *pid)
2414 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2416 TRACE("(%p)->(%p)\n", This, pid);
2418 if(!pid)
2419 return E_POINTER;
2421 *pid = GetCurrentProcessId();
2423 return S_OK;
2426 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2427 IAudioSessionControl2 *iface)
2429 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2431 TRACE("(%p)\n", This);
2433 return S_FALSE;
2436 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2437 IAudioSessionControl2 *iface, BOOL optout)
2439 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2441 TRACE("(%p)->(%d)\n", This, optout);
2443 return S_OK;
2446 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2448 AudioSessionControl_QueryInterface,
2449 AudioSessionControl_AddRef,
2450 AudioSessionControl_Release,
2451 AudioSessionControl_GetState,
2452 AudioSessionControl_GetDisplayName,
2453 AudioSessionControl_SetDisplayName,
2454 AudioSessionControl_GetIconPath,
2455 AudioSessionControl_SetIconPath,
2456 AudioSessionControl_GetGroupingParam,
2457 AudioSessionControl_SetGroupingParam,
2458 AudioSessionControl_RegisterAudioSessionNotification,
2459 AudioSessionControl_UnregisterAudioSessionNotification,
2460 AudioSessionControl_GetSessionIdentifier,
2461 AudioSessionControl_GetSessionInstanceIdentifier,
2462 AudioSessionControl_GetProcessId,
2463 AudioSessionControl_IsSystemSoundsSession,
2464 AudioSessionControl_SetDuckingPreference
2467 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2468 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2470 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2472 if(!ppv)
2473 return E_POINTER;
2474 *ppv = NULL;
2476 if(IsEqualIID(riid, &IID_IUnknown) ||
2477 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2478 *ppv = iface;
2479 if(*ppv){
2480 IUnknown_AddRef((IUnknown*)*ppv);
2481 return S_OK;
2484 WARN("Unknown interface %s\n", debugstr_guid(riid));
2485 return E_NOINTERFACE;
2488 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2490 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2491 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2494 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2496 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2497 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2500 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2501 ISimpleAudioVolume *iface, float level, const GUID *context)
2503 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2504 AudioSession *session = This->session;
2506 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2508 if(level < 0.f || level > 1.f)
2509 return E_INVALIDARG;
2511 if(context)
2512 FIXME("Notifications not supported yet\n");
2514 EnterCriticalSection(&session->lock);
2516 session->master_vol = level;
2518 TRACE("OSS doesn't support setting volume\n");
2520 LeaveCriticalSection(&session->lock);
2522 return S_OK;
2525 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2526 ISimpleAudioVolume *iface, float *level)
2528 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2529 AudioSession *session = This->session;
2531 TRACE("(%p)->(%p)\n", session, level);
2533 if(!level)
2534 return NULL_PTR_ERR;
2536 *level = session->master_vol;
2538 return S_OK;
2541 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2542 BOOL mute, const GUID *context)
2544 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2545 AudioSession *session = This->session;
2547 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2549 EnterCriticalSection(&session->lock);
2551 session->mute = mute;
2553 LeaveCriticalSection(&session->lock);
2555 return S_OK;
2558 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2559 BOOL *mute)
2561 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2562 AudioSession *session = This->session;
2564 TRACE("(%p)->(%p)\n", session, mute);
2566 if(!mute)
2567 return NULL_PTR_ERR;
2569 *mute = This->session->mute;
2571 return S_OK;
2574 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2576 SimpleAudioVolume_QueryInterface,
2577 SimpleAudioVolume_AddRef,
2578 SimpleAudioVolume_Release,
2579 SimpleAudioVolume_SetMasterVolume,
2580 SimpleAudioVolume_GetMasterVolume,
2581 SimpleAudioVolume_SetMute,
2582 SimpleAudioVolume_GetMute
2585 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2586 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2588 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2590 if(!ppv)
2591 return E_POINTER;
2592 *ppv = NULL;
2594 if(IsEqualIID(riid, &IID_IUnknown) ||
2595 IsEqualIID(riid, &IID_IAudioStreamVolume))
2596 *ppv = iface;
2597 if(*ppv){
2598 IUnknown_AddRef((IUnknown*)*ppv);
2599 return S_OK;
2602 WARN("Unknown interface %s\n", debugstr_guid(riid));
2603 return E_NOINTERFACE;
2606 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2608 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2609 return IAudioClient_AddRef(&This->IAudioClient_iface);
2612 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2614 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2615 return IAudioClient_Release(&This->IAudioClient_iface);
2618 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2619 IAudioStreamVolume *iface, UINT32 *out)
2621 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2623 TRACE("(%p)->(%p)\n", This, out);
2625 if(!out)
2626 return E_POINTER;
2628 *out = This->fmt->nChannels;
2630 return S_OK;
2633 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2634 IAudioStreamVolume *iface, UINT32 index, float level)
2636 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2638 TRACE("(%p)->(%d, %f)\n", This, index, level);
2640 if(level < 0.f || level > 1.f)
2641 return E_INVALIDARG;
2643 if(index >= This->fmt->nChannels)
2644 return E_INVALIDARG;
2646 EnterCriticalSection(&This->lock);
2648 This->vols[index] = level;
2650 TRACE("OSS doesn't support setting volume\n");
2652 LeaveCriticalSection(&This->lock);
2654 return S_OK;
2657 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2658 IAudioStreamVolume *iface, UINT32 index, float *level)
2660 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2662 TRACE("(%p)->(%d, %p)\n", This, index, level);
2664 if(!level)
2665 return E_POINTER;
2667 if(index >= This->fmt->nChannels)
2668 return E_INVALIDARG;
2670 *level = This->vols[index];
2672 return S_OK;
2675 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2676 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2678 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2679 int i;
2681 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2683 if(!levels)
2684 return E_POINTER;
2686 if(count != This->fmt->nChannels)
2687 return E_INVALIDARG;
2689 EnterCriticalSection(&This->lock);
2691 for(i = 0; i < count; ++i)
2692 This->vols[i] = levels[i];
2694 TRACE("OSS doesn't support setting volume\n");
2696 LeaveCriticalSection(&This->lock);
2698 return S_OK;
2701 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2702 IAudioStreamVolume *iface, UINT32 count, float *levels)
2704 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2705 int i;
2707 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2709 if(!levels)
2710 return E_POINTER;
2712 if(count != This->fmt->nChannels)
2713 return E_INVALIDARG;
2715 EnterCriticalSection(&This->lock);
2717 for(i = 0; i < count; ++i)
2718 levels[i] = This->vols[i];
2720 LeaveCriticalSection(&This->lock);
2722 return S_OK;
2725 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2727 AudioStreamVolume_QueryInterface,
2728 AudioStreamVolume_AddRef,
2729 AudioStreamVolume_Release,
2730 AudioStreamVolume_GetChannelCount,
2731 AudioStreamVolume_SetChannelVolume,
2732 AudioStreamVolume_GetChannelVolume,
2733 AudioStreamVolume_SetAllVolumes,
2734 AudioStreamVolume_GetAllVolumes
2737 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2738 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2740 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2742 if(!ppv)
2743 return E_POINTER;
2744 *ppv = NULL;
2746 if(IsEqualIID(riid, &IID_IUnknown) ||
2747 IsEqualIID(riid, &IID_IChannelAudioVolume))
2748 *ppv = iface;
2749 if(*ppv){
2750 IUnknown_AddRef((IUnknown*)*ppv);
2751 return S_OK;
2754 WARN("Unknown interface %s\n", debugstr_guid(riid));
2755 return E_NOINTERFACE;
2758 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2760 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2761 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2764 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2766 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2767 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2770 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2771 IChannelAudioVolume *iface, UINT32 *out)
2773 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2774 AudioSession *session = This->session;
2776 TRACE("(%p)->(%p)\n", session, out);
2778 if(!out)
2779 return NULL_PTR_ERR;
2781 *out = session->channel_count;
2783 return S_OK;
2786 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2787 IChannelAudioVolume *iface, UINT32 index, float level,
2788 const GUID *context)
2790 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2791 AudioSession *session = This->session;
2793 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2794 wine_dbgstr_guid(context));
2796 if(level < 0.f || level > 1.f)
2797 return E_INVALIDARG;
2799 if(index >= session->channel_count)
2800 return E_INVALIDARG;
2802 if(context)
2803 FIXME("Notifications not supported yet\n");
2805 EnterCriticalSection(&session->lock);
2807 session->channel_vols[index] = level;
2809 TRACE("OSS doesn't support setting volume\n");
2811 LeaveCriticalSection(&session->lock);
2813 return S_OK;
2816 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2817 IChannelAudioVolume *iface, UINT32 index, float *level)
2819 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2820 AudioSession *session = This->session;
2822 TRACE("(%p)->(%d, %p)\n", session, index, level);
2824 if(!level)
2825 return NULL_PTR_ERR;
2827 if(index >= session->channel_count)
2828 return E_INVALIDARG;
2830 *level = session->channel_vols[index];
2832 return S_OK;
2835 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2836 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2837 const GUID *context)
2839 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2840 AudioSession *session = This->session;
2841 int i;
2843 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2844 wine_dbgstr_guid(context));
2846 if(!levels)
2847 return NULL_PTR_ERR;
2849 if(count != session->channel_count)
2850 return E_INVALIDARG;
2852 if(context)
2853 FIXME("Notifications not supported yet\n");
2855 EnterCriticalSection(&session->lock);
2857 for(i = 0; i < count; ++i)
2858 session->channel_vols[i] = levels[i];
2860 TRACE("OSS doesn't support setting volume\n");
2862 LeaveCriticalSection(&session->lock);
2864 return S_OK;
2867 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2868 IChannelAudioVolume *iface, UINT32 count, float *levels)
2870 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2871 AudioSession *session = This->session;
2872 int i;
2874 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2876 if(!levels)
2877 return NULL_PTR_ERR;
2879 if(count != session->channel_count)
2880 return E_INVALIDARG;
2882 for(i = 0; i < count; ++i)
2883 levels[i] = session->channel_vols[i];
2885 return S_OK;
2888 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2890 ChannelAudioVolume_QueryInterface,
2891 ChannelAudioVolume_AddRef,
2892 ChannelAudioVolume_Release,
2893 ChannelAudioVolume_GetChannelCount,
2894 ChannelAudioVolume_SetChannelVolume,
2895 ChannelAudioVolume_GetChannelVolume,
2896 ChannelAudioVolume_SetAllVolumes,
2897 ChannelAudioVolume_GetAllVolumes
2900 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2901 REFIID riid, void **ppv)
2903 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2905 if(!ppv)
2906 return E_POINTER;
2907 *ppv = NULL;
2909 if(IsEqualIID(riid, &IID_IUnknown) ||
2910 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2911 IsEqualIID(riid, &IID_IAudioSessionManager2))
2912 *ppv = iface;
2913 if(*ppv){
2914 IUnknown_AddRef((IUnknown*)*ppv);
2915 return S_OK;
2918 WARN("Unknown interface %s\n", debugstr_guid(riid));
2919 return E_NOINTERFACE;
2922 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2924 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2925 ULONG ref;
2926 ref = InterlockedIncrement(&This->ref);
2927 TRACE("(%p) Refcount now %u\n", This, ref);
2928 return ref;
2931 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2933 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2934 ULONG ref;
2935 ref = InterlockedDecrement(&This->ref);
2936 TRACE("(%p) Refcount now %u\n", This, ref);
2937 if(!ref)
2938 HeapFree(GetProcessHeap(), 0, This);
2939 return ref;
2942 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2943 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2944 IAudioSessionControl **out)
2946 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2947 AudioSession *session;
2948 AudioSessionWrapper *wrapper;
2949 HRESULT hr;
2951 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2952 flags, out);
2954 hr = get_audio_session(session_guid, This->device, 0, &session);
2955 if(FAILED(hr))
2956 return hr;
2958 wrapper = AudioSessionWrapper_Create(NULL);
2959 if(!wrapper)
2960 return E_OUTOFMEMORY;
2962 wrapper->session = session;
2964 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2966 return S_OK;
2969 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2970 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2971 ISimpleAudioVolume **out)
2973 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2974 AudioSession *session;
2975 AudioSessionWrapper *wrapper;
2976 HRESULT hr;
2978 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2979 flags, out);
2981 hr = get_audio_session(session_guid, This->device, 0, &session);
2982 if(FAILED(hr))
2983 return hr;
2985 wrapper = AudioSessionWrapper_Create(NULL);
2986 if(!wrapper)
2987 return E_OUTOFMEMORY;
2989 wrapper->session = session;
2991 *out = &wrapper->ISimpleAudioVolume_iface;
2993 return S_OK;
2996 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2997 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2999 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3000 FIXME("(%p)->(%p) - stub\n", This, out);
3001 return E_NOTIMPL;
3004 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3005 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3007 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3008 FIXME("(%p)->(%p) - stub\n", This, notification);
3009 return E_NOTIMPL;
3012 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3013 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3015 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3016 FIXME("(%p)->(%p) - stub\n", This, notification);
3017 return E_NOTIMPL;
3020 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3021 IAudioSessionManager2 *iface, const WCHAR *session_id,
3022 IAudioVolumeDuckNotification *notification)
3024 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3025 FIXME("(%p)->(%p) - stub\n", This, notification);
3026 return E_NOTIMPL;
3029 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3030 IAudioSessionManager2 *iface,
3031 IAudioVolumeDuckNotification *notification)
3033 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3034 FIXME("(%p)->(%p) - stub\n", This, notification);
3035 return E_NOTIMPL;
3038 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3040 AudioSessionManager_QueryInterface,
3041 AudioSessionManager_AddRef,
3042 AudioSessionManager_Release,
3043 AudioSessionManager_GetAudioSessionControl,
3044 AudioSessionManager_GetSimpleAudioVolume,
3045 AudioSessionManager_GetSessionEnumerator,
3046 AudioSessionManager_RegisterSessionNotification,
3047 AudioSessionManager_UnregisterSessionNotification,
3048 AudioSessionManager_RegisterDuckNotification,
3049 AudioSessionManager_UnregisterDuckNotification
3052 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3053 IAudioSessionManager2 **out)
3055 SessionMgr *This;
3057 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3058 if(!This)
3059 return E_OUTOFMEMORY;
3061 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3062 This->device = device;
3063 This->ref = 1;
3065 *out = &This->IAudioSessionManager2_iface;
3067 return S_OK;