regedit: Allow importing strings with escaped NULL.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobce95138c334a5f5b15408edb8af9f5fafdf4b782
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;
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_keyW[] = {'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',0};
156 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
157 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
158 'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
159 static const WCHAR guidW[] = {'g','u','i','d',0};
161 static HANDLE g_timer_q;
163 static CRITICAL_SECTION g_sessions_lock;
164 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
166 0, 0, &g_sessions_lock,
167 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
168 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
170 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
171 static struct list g_sessions = LIST_INIT(g_sessions);
173 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
175 static const IAudioClientVtbl AudioClient_Vtbl;
176 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
177 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
178 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
179 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
180 static const IAudioClockVtbl AudioClock_Vtbl;
181 static const IAudioClock2Vtbl AudioClock2_Vtbl;
182 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
183 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
184 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
186 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
188 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
191 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
193 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
196 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
198 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
201 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
203 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
206 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
208 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
211 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
213 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
216 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
218 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
221 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
223 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
226 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
228 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
231 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
233 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
236 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
238 switch (reason)
240 case DLL_PROCESS_ATTACH:
241 g_timer_q = CreateTimerQueue();
242 if(!g_timer_q)
243 return FALSE;
244 break;
246 case DLL_PROCESS_DETACH:
247 if (!reserved)
249 OSSDevice *iter, *iter2;
251 DeleteCriticalSection(&g_sessions_lock);
253 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
254 HeapFree(GetProcessHeap(), 0, iter);
257 break;
259 return TRUE;
262 /* From <dlls/mmdevapi/mmdevapi.h> */
263 enum DriverPriority {
264 Priority_Unavailable = 0,
265 Priority_Low,
266 Priority_Neutral,
267 Priority_Preferred
270 int WINAPI AUDDRV_GetPriority(void)
272 int mixer_fd;
273 oss_sysinfo sysinfo;
275 /* Attempt to determine if we are running on OSS or ALSA's OSS
276 * compatibility layer. There is no official way to do that, so just check
277 * for validity as best as possible, without rejecting valid OSS
278 * implementations. */
280 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
281 if(mixer_fd < 0){
282 TRACE("Priority_Unavailable: open failed\n");
283 return Priority_Unavailable;
286 sysinfo.version[0] = 0xFF;
287 sysinfo.versionnum = ~0;
288 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
289 TRACE("Priority_Unavailable: ioctl failed\n");
290 close(mixer_fd);
291 return Priority_Unavailable;
294 close(mixer_fd);
296 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
297 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
298 return Priority_Low;
300 if(sysinfo.versionnum & 0x80000000){
301 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
302 return Priority_Low;
305 TRACE("Priority_Preferred: Seems like valid OSS!\n");
307 return Priority_Preferred;
310 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
311 GUID *guid)
313 HKEY key;
314 BOOL opened = FALSE;
315 LONG lr;
317 if(!drv_key){
318 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
319 NULL, &drv_key, NULL);
320 if(lr != ERROR_SUCCESS){
321 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
322 return;
324 opened = TRUE;
327 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
328 NULL, &key, NULL);
329 if(lr != ERROR_SUCCESS){
330 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
331 goto exit;
334 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
335 sizeof(GUID));
336 if(lr != ERROR_SUCCESS)
337 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
339 RegCloseKey(key);
340 exit:
341 if(opened)
342 RegCloseKey(drv_key);
345 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
347 HKEY key = NULL, dev_key;
348 DWORD type, size = sizeof(*guid);
349 WCHAR key_name[256];
351 if(flow == eCapture)
352 key_name[0] = '1';
353 else
354 key_name[0] = '0';
355 key_name[1] = ',';
356 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
357 (sizeof(key_name) / sizeof(*key_name)) - 2);
359 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
360 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
361 if(RegQueryValueExW(dev_key, guidW, 0, &type,
362 (BYTE*)guid, &size) == ERROR_SUCCESS){
363 if(type == REG_BINARY){
364 RegCloseKey(dev_key);
365 RegCloseKey(key);
366 return;
368 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
369 wine_dbgstr_w(key_name), type);
371 RegCloseKey(dev_key);
375 CoCreateGuid(guid);
377 set_device_guid(flow, key, key_name, guid);
379 if(key)
380 RegCloseKey(key);
383 static const char *oss_clean_devnode(const char *devnode)
385 static char ret[OSS_DEVNODE_SIZE];
387 const char *dot, *slash;
388 size_t len;
390 dot = strrchr(devnode, '.');
391 if(!dot)
392 return devnode;
394 slash = strrchr(devnode, '/');
395 if(slash && dot < slash)
396 return devnode;
398 len = dot - devnode;
400 memcpy(ret, devnode, len);
401 ret[len] = '\0';
403 return ret;
406 static UINT get_default_index(EDataFlow flow)
408 int fd = -1, err;
409 UINT i;
410 oss_audioinfo ai;
411 const char *devnode;
412 OSSDevice *dev_item;
414 if(flow == eRender)
415 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
416 else
417 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
419 if(fd < 0){
420 WARN("Couldn't open default device!\n");
421 return 0;
424 ai.dev = -1;
425 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
426 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
427 close(fd);
428 return 0;
431 close(fd);
433 TRACE("Default devnode: %s\n", ai.devnode);
434 devnode = oss_clean_devnode(ai.devnode);
435 i = 0;
436 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
437 if(dev_item->flow == flow){
438 if(!strcmp(devnode, dev_item->devnode))
439 return i;
440 ++i;
444 WARN("Couldn't find default device! Choosing first.\n");
445 return 0;
448 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
449 UINT *num, UINT *def_index)
451 int i, mixer_fd;
452 oss_sysinfo sysinfo;
453 static int print_once = 0;
455 static const WCHAR outW[] = {'O','u','t',':',' ',0};
456 static const WCHAR inW[] = {'I','n',':',' ',0};
458 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
460 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
461 if(mixer_fd < 0){
462 ERR("OSS /dev/mixer doesn't seem to exist\n");
463 return AUDCLNT_E_SERVICE_NOT_RUNNING;
466 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
467 close(mixer_fd);
469 if(errno == EINVAL){
470 ERR("OSS version too old, need at least OSSv4\n");
471 return AUDCLNT_E_SERVICE_NOT_RUNNING;
474 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
475 return E_FAIL;
478 if(!print_once){
479 TRACE("OSS sysinfo:\n");
480 TRACE("product: %s\n", sysinfo.product);
481 TRACE("version: %s\n", sysinfo.version);
482 TRACE("versionnum: %x\n", sysinfo.versionnum);
483 TRACE("numaudios: %d\n", sysinfo.numaudios);
484 TRACE("nummixers: %d\n", sysinfo.nummixers);
485 TRACE("numcards: %d\n", sysinfo.numcards);
486 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
487 print_once = 1;
490 if(sysinfo.numaudios <= 0){
491 WARN("No audio devices!\n");
492 close(mixer_fd);
493 return AUDCLNT_E_SERVICE_NOT_RUNNING;
496 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
497 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
499 *num = 0;
500 for(i = 0; i < sysinfo.numaudios; ++i){
501 oss_audioinfo ai = {0};
502 const char *devnode;
503 OSSDevice *dev_item;
504 int fd;
506 ai.dev = i;
507 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
508 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
509 strerror(errno));
510 continue;
513 devnode = oss_clean_devnode(ai.devnode);
515 /* check for duplicates */
516 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
517 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
518 break;
520 if(&dev_item->entry != &g_devices)
521 continue;
523 if(flow == eRender)
524 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
525 else
526 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
527 if(fd < 0){
528 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
529 devnode, errno, strerror(errno));
530 continue;
532 close(fd);
534 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
535 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
536 size_t len, prefix_len;
537 const WCHAR *prefix;
539 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
541 dev_item->flow = flow;
542 get_device_guid(flow, devnode, &dev_item->guid);
543 strcpy(dev_item->devnode, devnode);
545 (*guids)[*num] = dev_item->guid;
547 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
548 if(flow == eRender){
549 prefix = outW;
550 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
551 len += prefix_len;
552 }else{
553 prefix = inW;
554 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
555 len += prefix_len;
557 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
558 len * sizeof(WCHAR));
559 if(!(*ids)[*num]){
560 for(i = 0; i < *num; ++i)
561 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
562 HeapFree(GetProcessHeap(), 0, *ids);
563 HeapFree(GetProcessHeap(), 0, *guids);
564 HeapFree(GetProcessHeap(), 0, dev_item);
565 close(mixer_fd);
566 return E_OUTOFMEMORY;
568 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
569 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
570 (*ids)[*num] + prefix_len, len - prefix_len);
572 list_add_tail(&g_devices, &dev_item->entry);
574 (*num)++;
578 close(mixer_fd);
580 *def_index = get_default_index(flow);
582 return S_OK;
585 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
587 OSSDevice *dev_item;
588 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
589 if(IsEqualGUID(guid, &dev_item->guid))
590 return dev_item;
591 return NULL;
594 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
595 IAudioClient **out)
597 ACImpl *This;
598 const OSSDevice *oss_dev;
599 HRESULT hr;
601 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
603 oss_dev = get_ossdevice_from_guid(guid);
604 if(!oss_dev){
605 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
606 return AUDCLNT_E_DEVICE_INVALIDATED;
609 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
610 if(!This)
611 return E_OUTOFMEMORY;
613 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
614 (IUnknown **)&This->pUnkFTMarshal);
615 if (FAILED(hr)) {
616 HeapFree(GetProcessHeap(), 0, This);
617 return hr;
620 if(oss_dev->flow == eRender)
621 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
622 else if(oss_dev->flow == eCapture)
623 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
624 else{
625 HeapFree(GetProcessHeap(), 0, This);
626 return E_INVALIDARG;
628 if(This->fd < 0){
629 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
630 strerror(errno));
631 HeapFree(GetProcessHeap(), 0, This);
632 return AUDCLNT_E_DEVICE_INVALIDATED;
635 This->dataflow = oss_dev->flow;
637 This->ai.dev = -1;
638 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
639 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
640 errno, strerror(errno));
641 close(This->fd);
642 HeapFree(GetProcessHeap(), 0, This);
643 return E_FAIL;
646 strcpy(This->devnode, oss_dev->devnode);
648 TRACE("OSS audioinfo:\n");
649 TRACE("devnode: %s\n", This->ai.devnode);
650 TRACE("name: %s\n", This->ai.name);
651 TRACE("busy: %x\n", This->ai.busy);
652 TRACE("caps: %x\n", This->ai.caps);
653 TRACE("iformats: %x\n", This->ai.iformats);
654 TRACE("oformats: %x\n", This->ai.oformats);
655 TRACE("enabled: %d\n", This->ai.enabled);
656 TRACE("min_rate: %d\n", This->ai.min_rate);
657 TRACE("max_rate: %d\n", This->ai.max_rate);
658 TRACE("min_channels: %d\n", This->ai.min_channels);
659 TRACE("max_channels: %d\n", This->ai.max_channels);
661 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
662 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
663 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
664 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
665 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
666 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
668 InitializeCriticalSection(&This->lock);
669 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
671 This->parent = dev;
672 IMMDevice_AddRef(This->parent);
674 IAudioClient_AddRef(&This->IAudioClient_iface);
676 *out = &This->IAudioClient_iface;
678 return S_OK;
681 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
682 REFIID riid, void **ppv)
684 ACImpl *This = impl_from_IAudioClient(iface);
685 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
687 if(!ppv)
688 return E_POINTER;
689 *ppv = NULL;
690 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
691 *ppv = iface;
692 else if(IsEqualIID(riid, &IID_IMarshal))
693 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
694 if(*ppv){
695 IUnknown_AddRef((IUnknown*)*ppv);
696 return S_OK;
698 WARN("Unknown interface %s\n", debugstr_guid(riid));
699 return E_NOINTERFACE;
702 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
704 ACImpl *This = impl_from_IAudioClient(iface);
705 ULONG ref;
706 ref = InterlockedIncrement(&This->ref);
707 TRACE("(%p) Refcount now %u\n", This, ref);
708 return ref;
711 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
713 ACImpl *This = impl_from_IAudioClient(iface);
714 ULONG ref;
716 ref = InterlockedDecrement(&This->ref);
717 TRACE("(%p) Refcount now %u\n", This, ref);
718 if(!ref){
719 if(This->timer){
720 HANDLE event;
721 DWORD wait;
722 event = CreateEventW(NULL, TRUE, FALSE, NULL);
723 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
724 wait = wait && GetLastError() == ERROR_IO_PENDING;
725 if(event && wait)
726 WaitForSingleObject(event, INFINITE);
727 CloseHandle(event);
730 IAudioClient_Stop(iface);
731 IMMDevice_Release(This->parent);
732 IUnknown_Release(This->pUnkFTMarshal);
733 This->lock.DebugInfo->Spare[0] = 0;
734 DeleteCriticalSection(&This->lock);
735 close(This->fd);
736 if(This->initted){
737 EnterCriticalSection(&g_sessions_lock);
738 list_remove(&This->entry);
739 LeaveCriticalSection(&g_sessions_lock);
741 HeapFree(GetProcessHeap(), 0, This->vols);
742 HeapFree(GetProcessHeap(), 0, This->local_buffer);
743 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
744 CoTaskMemFree(This->fmt);
745 HeapFree(GetProcessHeap(), 0, This);
747 return ref;
750 static void dump_fmt(const WAVEFORMATEX *fmt)
752 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
753 switch(fmt->wFormatTag){
754 case WAVE_FORMAT_PCM:
755 TRACE("WAVE_FORMAT_PCM");
756 break;
757 case WAVE_FORMAT_IEEE_FLOAT:
758 TRACE("WAVE_FORMAT_IEEE_FLOAT");
759 break;
760 case WAVE_FORMAT_EXTENSIBLE:
761 TRACE("WAVE_FORMAT_EXTENSIBLE");
762 break;
763 default:
764 TRACE("Unknown");
765 break;
767 TRACE(")\n");
769 TRACE("nChannels: %u\n", fmt->nChannels);
770 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
771 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
772 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
773 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
774 TRACE("cbSize: %u\n", fmt->cbSize);
776 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
777 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
778 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
779 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
780 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
784 static DWORD get_channel_mask(unsigned int channels)
786 switch(channels){
787 case 0:
788 return 0;
789 case 1:
790 return KSAUDIO_SPEAKER_MONO;
791 case 2:
792 return KSAUDIO_SPEAKER_STEREO;
793 case 3:
794 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
795 case 4:
796 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
797 case 5:
798 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
799 case 6:
800 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
801 case 7:
802 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
803 case 8:
804 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
806 FIXME("Unknown speaker configuration: %u\n", channels);
807 return 0;
810 static int get_oss_format(const WAVEFORMATEX *fmt)
812 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
814 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
815 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
816 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
817 switch(fmt->wBitsPerSample){
818 case 8:
819 return AFMT_U8;
820 case 16:
821 return AFMT_S16_LE;
822 case 24:
823 return AFMT_S24_LE;
824 case 32:
825 return AFMT_S32_LE;
827 return -1;
830 #ifdef AFMT_FLOAT
831 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
832 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
833 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
834 if(fmt->wBitsPerSample != 32)
835 return -1;
837 return AFMT_FLOAT;
839 #endif
841 return -1;
844 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
846 WAVEFORMATEX *ret;
847 size_t size;
849 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
850 size = sizeof(WAVEFORMATEXTENSIBLE);
851 else
852 size = sizeof(WAVEFORMATEX);
854 ret = CoTaskMemAlloc(size);
855 if(!ret)
856 return NULL;
858 memcpy(ret, fmt, size);
860 ret->cbSize = size - sizeof(WAVEFORMATEX);
862 return ret;
865 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
866 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
868 int tmp, oss_format;
869 double tenth;
870 HRESULT ret = S_OK;
871 WAVEFORMATEX *closest = NULL;
873 tmp = oss_format = get_oss_format(fmt);
874 if(oss_format < 0)
875 return AUDCLNT_E_UNSUPPORTED_FORMAT;
876 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
877 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
878 return E_FAIL;
880 if(tmp != oss_format){
881 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
882 return AUDCLNT_E_UNSUPPORTED_FORMAT;
885 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
886 (fmt->nAvgBytesPerSec == 0 ||
887 fmt->nBlockAlign == 0 ||
888 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
889 return E_INVALIDARG;
891 if(fmt->nChannels == 0)
892 return AUDCLNT_E_UNSUPPORTED_FORMAT;
894 closest = clone_format(fmt);
895 if(!closest)
896 return E_OUTOFMEMORY;
898 tmp = fmt->nSamplesPerSec;
899 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
900 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
901 CoTaskMemFree(closest);
902 return E_FAIL;
904 tenth = fmt->nSamplesPerSec * 0.1;
905 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
906 ret = S_FALSE;
907 closest->nSamplesPerSec = tmp;
910 tmp = fmt->nChannels;
911 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
912 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
913 CoTaskMemFree(closest);
914 return E_FAIL;
916 if(tmp != fmt->nChannels){
917 ret = S_FALSE;
918 closest->nChannels = tmp;
921 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
922 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
924 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
925 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
926 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
927 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
928 ret = S_FALSE;
930 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
931 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
932 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
933 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
934 ret = S_FALSE;
937 if(ret == S_FALSE && !out)
938 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
940 if(ret == S_FALSE && out){
941 closest->nBlockAlign =
942 closest->nChannels * closest->wBitsPerSample / 8;
943 closest->nAvgBytesPerSec =
944 closest->nBlockAlign * closest->nSamplesPerSec;
945 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
946 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
947 *out = closest;
948 } else
949 CoTaskMemFree(closest);
951 TRACE("returning: %08x\n", ret);
952 return ret;
955 static void session_init_vols(AudioSession *session, UINT channels)
957 if(session->channel_count < channels){
958 UINT i;
960 if(session->channel_vols)
961 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
962 session->channel_vols, sizeof(float) * channels);
963 else
964 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
965 sizeof(float) * channels);
966 if(!session->channel_vols)
967 return;
969 for(i = session->channel_count; i < channels; ++i)
970 session->channel_vols[i] = 1.f;
972 session->channel_count = channels;
976 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
977 UINT num_channels)
979 AudioSession *ret;
981 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
982 if(!ret)
983 return NULL;
985 memcpy(&ret->guid, guid, sizeof(GUID));
987 ret->device = device;
989 list_init(&ret->clients);
991 list_add_head(&g_sessions, &ret->entry);
993 InitializeCriticalSection(&ret->lock);
994 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
996 session_init_vols(ret, num_channels);
998 ret->master_vol = 1.f;
1000 return ret;
1003 /* if channels == 0, then this will return or create a session with
1004 * matching dataflow and GUID. otherwise, channels must also match */
1005 static HRESULT get_audio_session(const GUID *sessionguid,
1006 IMMDevice *device, UINT channels, AudioSession **out)
1008 AudioSession *session;
1010 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1011 *out = create_session(&GUID_NULL, device, channels);
1012 if(!*out)
1013 return E_OUTOFMEMORY;
1015 return S_OK;
1018 *out = NULL;
1019 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1020 if(session->device == device &&
1021 IsEqualGUID(sessionguid, &session->guid)){
1022 session_init_vols(session, channels);
1023 *out = session;
1024 break;
1028 if(!*out){
1029 *out = create_session(sessionguid, device, channels);
1030 if(!*out)
1031 return E_OUTOFMEMORY;
1034 return S_OK;
1037 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1038 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1039 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1040 const GUID *sessionguid)
1042 ACImpl *This = impl_from_IAudioClient(iface);
1043 int i;
1044 HRESULT hr;
1046 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1047 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1049 if(!fmt)
1050 return E_POINTER;
1052 dump_fmt(fmt);
1054 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1055 return AUDCLNT_E_NOT_INITIALIZED;
1057 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1058 AUDCLNT_STREAMFLAGS_LOOPBACK |
1059 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1060 AUDCLNT_STREAMFLAGS_NOPERSIST |
1061 AUDCLNT_STREAMFLAGS_RATEADJUST |
1062 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1063 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1064 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1065 TRACE("Unknown flags: %08x\n", flags);
1066 return E_INVALIDARG;
1069 if(mode == AUDCLNT_SHAREMODE_SHARED){
1070 period = DefaultPeriod;
1071 if( duration < 3 * period)
1072 duration = 3 * period;
1073 }else{
1074 if(!period)
1075 period = DefaultPeriod; /* not minimum */
1076 if(period < MinimumPeriod || period > 5000000)
1077 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1078 if(duration > 20000000) /* the smaller the period, the lower this limit */
1079 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1080 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1081 if(duration != period)
1082 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1083 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1084 return AUDCLNT_E_DEVICE_IN_USE;
1085 }else{
1086 if( duration < 8 * period)
1087 duration = 8 * period; /* may grow above 2s */
1091 EnterCriticalSection(&This->lock);
1093 if(This->initted){
1094 LeaveCriticalSection(&This->lock);
1095 return AUDCLNT_E_ALREADY_INITIALIZED;
1098 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1099 if(FAILED(hr)){
1100 LeaveCriticalSection(&This->lock);
1101 return hr;
1104 This->fmt = clone_format(fmt);
1105 if(!This->fmt){
1106 LeaveCriticalSection(&This->lock);
1107 return E_OUTOFMEMORY;
1110 This->period_us = period / 10;
1111 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1113 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1114 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1115 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1116 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1117 This->bufsize_frames * fmt->nBlockAlign);
1118 if(!This->local_buffer){
1119 CoTaskMemFree(This->fmt);
1120 This->fmt = NULL;
1121 LeaveCriticalSection(&This->lock);
1122 return E_OUTOFMEMORY;
1125 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1126 if(!This->vols){
1127 CoTaskMemFree(This->fmt);
1128 This->fmt = NULL;
1129 LeaveCriticalSection(&This->lock);
1130 return E_OUTOFMEMORY;
1133 for(i = 0; i < fmt->nChannels; ++i)
1134 This->vols[i] = 1.f;
1136 This->share = mode;
1137 This->flags = flags;
1138 This->oss_bufsize_bytes = 0;
1140 EnterCriticalSection(&g_sessions_lock);
1142 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1143 &This->session);
1144 if(FAILED(hr)){
1145 LeaveCriticalSection(&g_sessions_lock);
1146 HeapFree(GetProcessHeap(), 0, This->vols);
1147 This->vols = NULL;
1148 CoTaskMemFree(This->fmt);
1149 This->fmt = NULL;
1150 LeaveCriticalSection(&This->lock);
1151 return hr;
1154 list_add_tail(&This->session->clients, &This->entry);
1156 LeaveCriticalSection(&g_sessions_lock);
1158 This->initted = TRUE;
1160 LeaveCriticalSection(&This->lock);
1162 return S_OK;
1165 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1166 UINT32 *frames)
1168 ACImpl *This = impl_from_IAudioClient(iface);
1170 TRACE("(%p)->(%p)\n", This, frames);
1172 if(!frames)
1173 return E_POINTER;
1175 EnterCriticalSection(&This->lock);
1177 if(!This->initted){
1178 LeaveCriticalSection(&This->lock);
1179 return AUDCLNT_E_NOT_INITIALIZED;
1182 *frames = This->bufsize_frames;
1184 TRACE("buffer size: %u\n", *frames);
1186 LeaveCriticalSection(&This->lock);
1188 return S_OK;
1191 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1192 REFERENCE_TIME *latency)
1194 ACImpl *This = impl_from_IAudioClient(iface);
1196 TRACE("(%p)->(%p)\n", This, latency);
1198 if(!latency)
1199 return E_POINTER;
1201 EnterCriticalSection(&This->lock);
1203 if(!This->initted){
1204 LeaveCriticalSection(&This->lock);
1205 return AUDCLNT_E_NOT_INITIALIZED;
1208 /* pretend we process audio in Period chunks, so max latency includes
1209 * the period time. Some native machines add .6666ms in shared mode. */
1210 *latency = This->period_us * 10 + 6666;
1212 LeaveCriticalSection(&This->lock);
1214 return S_OK;
1217 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1218 UINT32 *numpad)
1220 ACImpl *This = impl_from_IAudioClient(iface);
1222 TRACE("(%p)->(%p)\n", This, numpad);
1224 if(!numpad)
1225 return E_POINTER;
1227 EnterCriticalSection(&This->lock);
1229 if(!This->initted){
1230 LeaveCriticalSection(&This->lock);
1231 return AUDCLNT_E_NOT_INITIALIZED;
1234 *numpad = This->held_frames;
1236 TRACE("padding: %u\n", *numpad);
1238 LeaveCriticalSection(&This->lock);
1240 return S_OK;
1243 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1244 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1245 WAVEFORMATEX **outpwfx)
1247 ACImpl *This = impl_from_IAudioClient(iface);
1248 int fd = -1;
1249 HRESULT ret;
1251 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1253 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1254 return E_POINTER;
1256 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1257 return E_INVALIDARG;
1259 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1260 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1261 return E_INVALIDARG;
1263 dump_fmt(pwfx);
1265 if(outpwfx){
1266 *outpwfx = NULL;
1267 if(mode != AUDCLNT_SHAREMODE_SHARED)
1268 outpwfx = NULL;
1271 if(This->dataflow == eRender)
1272 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1273 else if(This->dataflow == eCapture)
1274 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1276 if(fd < 0){
1277 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1278 strerror(errno));
1279 return AUDCLNT_E_DEVICE_INVALIDATED;
1282 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1284 close(fd);
1286 return ret;
1289 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1290 WAVEFORMATEX **pwfx)
1292 ACImpl *This = impl_from_IAudioClient(iface);
1293 WAVEFORMATEXTENSIBLE *fmt;
1294 int formats;
1296 TRACE("(%p)->(%p)\n", This, pwfx);
1298 if(!pwfx)
1299 return E_POINTER;
1300 *pwfx = NULL;
1302 if(This->dataflow == eRender)
1303 formats = This->ai.oformats;
1304 else if(This->dataflow == eCapture)
1305 formats = This->ai.iformats;
1306 else
1307 return E_UNEXPECTED;
1309 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1310 if(!fmt)
1311 return E_OUTOFMEMORY;
1313 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1314 if(formats & AFMT_S16_LE){
1315 fmt->Format.wBitsPerSample = 16;
1316 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1317 #ifdef AFMT_FLOAT
1318 }else if(formats & AFMT_FLOAT){
1319 fmt->Format.wBitsPerSample = 32;
1320 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1321 #endif
1322 }else if(formats & AFMT_U8){
1323 fmt->Format.wBitsPerSample = 8;
1324 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1325 }else if(formats & AFMT_S32_LE){
1326 fmt->Format.wBitsPerSample = 32;
1327 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1328 }else if(formats & AFMT_S24_LE){
1329 fmt->Format.wBitsPerSample = 24;
1330 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1331 }else{
1332 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1333 CoTaskMemFree(fmt);
1334 return E_FAIL;
1337 /* some OSS drivers are buggy, so set reasonable defaults if
1338 * the reported values seem wacky */
1339 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1340 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1341 fmt->Format.nChannels = 2;
1343 if(This->ai.max_rate == 0)
1344 fmt->Format.nSamplesPerSec = 44100;
1345 else
1346 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1347 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1348 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1350 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1352 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1353 fmt->Format.nChannels) / 8;
1354 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1355 fmt->Format.nBlockAlign;
1357 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1358 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1360 *pwfx = (WAVEFORMATEX*)fmt;
1361 dump_fmt(*pwfx);
1363 return S_OK;
1366 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1367 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1369 ACImpl *This = impl_from_IAudioClient(iface);
1371 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1373 if(!defperiod && !minperiod)
1374 return E_POINTER;
1376 if(defperiod)
1377 *defperiod = DefaultPeriod;
1378 if(minperiod)
1379 *minperiod = MinimumPeriod;
1381 return S_OK;
1384 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1386 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1387 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1388 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1389 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1390 This->fmt->wBitsPerSample == 8)
1391 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1392 else
1393 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1396 static void oss_write_data(ACImpl *This)
1398 ssize_t written_bytes;
1399 UINT32 written_frames, in_oss_frames, write_limit, max_period;
1400 size_t to_write_frames, to_write_bytes;
1401 audio_buf_info bi;
1402 BYTE *buf =
1403 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1405 if(This->held_frames == 0)
1406 return;
1408 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1409 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1410 else
1411 to_write_frames = This->held_frames;
1413 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1414 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1415 return;
1418 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1420 if(bi.bytes > This->oss_bufsize_bytes){
1421 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1422 bi.bytes, This->oss_bufsize_bytes);
1423 This->oss_bufsize_bytes = bi.bytes;
1424 in_oss_frames = 0;
1425 }else if(This->oss_bufsize_bytes - bi.bytes <= bi.fragsize)
1426 in_oss_frames = 0;
1427 else
1428 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1430 write_limit = 0;
1431 while(write_limit + in_oss_frames < max_period * 3)
1432 write_limit += max_period;
1433 if(write_limit == 0)
1434 return;
1436 to_write_frames = min(to_write_frames, write_limit);
1437 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1439 if(This->session->mute)
1440 silence_buffer(This, buf, to_write_frames);
1442 written_bytes = write(This->fd, buf, to_write_bytes);
1443 if(written_bytes < 0){
1444 /* EAGAIN is OSS buffer full, log that too */
1445 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1446 return;
1448 written_frames = written_bytes / This->fmt->nBlockAlign;
1450 This->lcl_offs_frames += written_frames;
1451 This->lcl_offs_frames %= This->bufsize_frames;
1452 This->held_frames -= written_frames;
1454 if(written_frames < to_write_frames){
1455 /* OSS buffer probably full */
1456 return;
1459 if(This->held_frames && written_frames < write_limit){
1460 /* wrapped and have some data back at the start to write */
1462 to_write_frames = min(write_limit - written_frames, This->held_frames);
1463 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1465 if(This->session->mute)
1466 silence_buffer(This, This->local_buffer, to_write_frames);
1468 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1469 if(written_bytes < 0){
1470 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1471 return;
1473 written_frames = written_bytes / This->fmt->nBlockAlign;
1475 This->lcl_offs_frames += written_frames;
1476 This->lcl_offs_frames %= This->bufsize_frames;
1477 This->held_frames -= written_frames;
1481 static void oss_read_data(ACImpl *This)
1483 UINT64 pos, readable;
1484 audio_buf_info bi;
1485 ssize_t nread;
1487 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1488 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1489 return;
1492 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1493 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1495 if(bi.bytes < readable)
1496 readable = bi.bytes;
1498 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1499 readable);
1500 if(nread < 0){
1501 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1502 return;
1505 This->held_frames += nread / This->fmt->nBlockAlign;
1507 if(This->held_frames > This->bufsize_frames){
1508 WARN("Overflow of unread data\n");
1509 This->lcl_offs_frames += This->held_frames;
1510 This->lcl_offs_frames %= This->bufsize_frames;
1511 This->held_frames = This->bufsize_frames;
1515 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1517 ACImpl *This = user;
1519 EnterCriticalSection(&This->lock);
1521 if(This->playing){
1522 if(This->dataflow == eRender && This->held_frames)
1523 oss_write_data(This);
1524 else if(This->dataflow == eCapture)
1525 oss_read_data(This);
1528 LeaveCriticalSection(&This->lock);
1530 if(This->event)
1531 SetEvent(This->event);
1534 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1536 ACImpl *This = impl_from_IAudioClient(iface);
1538 TRACE("(%p)\n", This);
1540 EnterCriticalSection(&This->lock);
1542 if(!This->initted){
1543 LeaveCriticalSection(&This->lock);
1544 return AUDCLNT_E_NOT_INITIALIZED;
1547 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1548 LeaveCriticalSection(&This->lock);
1549 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1552 if(This->playing){
1553 LeaveCriticalSection(&This->lock);
1554 return AUDCLNT_E_NOT_STOPPED;
1557 if(!This->timer){
1558 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1559 oss_period_callback, This, 0, This->period_us / 1000,
1560 WT_EXECUTEINTIMERTHREAD))
1561 ERR("Unable to create period timer: %u\n", GetLastError());
1564 This->playing = TRUE;
1566 LeaveCriticalSection(&This->lock);
1568 return S_OK;
1571 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1573 ACImpl *This = impl_from_IAudioClient(iface);
1575 TRACE("(%p)\n", This);
1577 EnterCriticalSection(&This->lock);
1579 if(!This->initted){
1580 LeaveCriticalSection(&This->lock);
1581 return AUDCLNT_E_NOT_INITIALIZED;
1584 if(!This->playing){
1585 LeaveCriticalSection(&This->lock);
1586 return S_FALSE;
1589 This->playing = FALSE;
1591 LeaveCriticalSection(&This->lock);
1593 return S_OK;
1596 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1598 ACImpl *This = impl_from_IAudioClient(iface);
1600 TRACE("(%p)\n", This);
1602 EnterCriticalSection(&This->lock);
1604 if(!This->initted){
1605 LeaveCriticalSection(&This->lock);
1606 return AUDCLNT_E_NOT_INITIALIZED;
1609 if(This->playing){
1610 LeaveCriticalSection(&This->lock);
1611 return AUDCLNT_E_NOT_STOPPED;
1614 if(This->getbuf_last){
1615 LeaveCriticalSection(&This->lock);
1616 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1619 if(This->dataflow == eRender){
1620 This->written_frames = 0;
1621 This->last_pos_frames = 0;
1622 }else{
1623 This->written_frames += This->held_frames;
1625 This->held_frames = 0;
1626 This->lcl_offs_frames = 0;
1628 LeaveCriticalSection(&This->lock);
1630 return S_OK;
1633 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1634 HANDLE event)
1636 ACImpl *This = impl_from_IAudioClient(iface);
1638 TRACE("(%p)->(%p)\n", This, event);
1640 if(!event)
1641 return E_INVALIDARG;
1643 EnterCriticalSection(&This->lock);
1645 if(!This->initted){
1646 LeaveCriticalSection(&This->lock);
1647 return AUDCLNT_E_NOT_INITIALIZED;
1650 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1651 LeaveCriticalSection(&This->lock);
1652 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1655 if (This->event){
1656 LeaveCriticalSection(&This->lock);
1657 FIXME("called twice\n");
1658 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1661 This->event = event;
1663 LeaveCriticalSection(&This->lock);
1665 return S_OK;
1668 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1669 void **ppv)
1671 ACImpl *This = impl_from_IAudioClient(iface);
1673 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1675 if(!ppv)
1676 return E_POINTER;
1677 *ppv = NULL;
1679 EnterCriticalSection(&This->lock);
1681 if(!This->initted){
1682 LeaveCriticalSection(&This->lock);
1683 return AUDCLNT_E_NOT_INITIALIZED;
1686 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1687 if(This->dataflow != eRender){
1688 LeaveCriticalSection(&This->lock);
1689 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1691 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1692 *ppv = &This->IAudioRenderClient_iface;
1693 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1694 if(This->dataflow != eCapture){
1695 LeaveCriticalSection(&This->lock);
1696 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1698 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1699 *ppv = &This->IAudioCaptureClient_iface;
1700 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1701 IAudioClock_AddRef(&This->IAudioClock_iface);
1702 *ppv = &This->IAudioClock_iface;
1703 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1704 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1705 *ppv = &This->IAudioStreamVolume_iface;
1706 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1707 if(!This->session_wrapper){
1708 This->session_wrapper = AudioSessionWrapper_Create(This);
1709 if(!This->session_wrapper){
1710 LeaveCriticalSection(&This->lock);
1711 return E_OUTOFMEMORY;
1713 }else
1714 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1716 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1717 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1718 if(!This->session_wrapper){
1719 This->session_wrapper = AudioSessionWrapper_Create(This);
1720 if(!This->session_wrapper){
1721 LeaveCriticalSection(&This->lock);
1722 return E_OUTOFMEMORY;
1724 }else
1725 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1727 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1728 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1729 if(!This->session_wrapper){
1730 This->session_wrapper = AudioSessionWrapper_Create(This);
1731 if(!This->session_wrapper){
1732 LeaveCriticalSection(&This->lock);
1733 return E_OUTOFMEMORY;
1735 }else
1736 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1738 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1741 if(*ppv){
1742 LeaveCriticalSection(&This->lock);
1743 return S_OK;
1746 LeaveCriticalSection(&This->lock);
1748 FIXME("stub %s\n", debugstr_guid(riid));
1749 return E_NOINTERFACE;
1752 static const IAudioClientVtbl AudioClient_Vtbl =
1754 AudioClient_QueryInterface,
1755 AudioClient_AddRef,
1756 AudioClient_Release,
1757 AudioClient_Initialize,
1758 AudioClient_GetBufferSize,
1759 AudioClient_GetStreamLatency,
1760 AudioClient_GetCurrentPadding,
1761 AudioClient_IsFormatSupported,
1762 AudioClient_GetMixFormat,
1763 AudioClient_GetDevicePeriod,
1764 AudioClient_Start,
1765 AudioClient_Stop,
1766 AudioClient_Reset,
1767 AudioClient_SetEventHandle,
1768 AudioClient_GetService
1771 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1772 IAudioRenderClient *iface, REFIID riid, void **ppv)
1774 ACImpl *This = impl_from_IAudioRenderClient(iface);
1775 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1777 if(!ppv)
1778 return E_POINTER;
1779 *ppv = NULL;
1781 if(IsEqualIID(riid, &IID_IUnknown) ||
1782 IsEqualIID(riid, &IID_IAudioRenderClient))
1783 *ppv = iface;
1784 else if(IsEqualIID(riid, &IID_IMarshal))
1785 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1786 if(*ppv){
1787 IUnknown_AddRef((IUnknown*)*ppv);
1788 return S_OK;
1791 WARN("Unknown interface %s\n", debugstr_guid(riid));
1792 return E_NOINTERFACE;
1795 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1797 ACImpl *This = impl_from_IAudioRenderClient(iface);
1798 return AudioClient_AddRef(&This->IAudioClient_iface);
1801 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1803 ACImpl *This = impl_from_IAudioRenderClient(iface);
1804 return AudioClient_Release(&This->IAudioClient_iface);
1807 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1808 UINT32 frames, BYTE **data)
1810 ACImpl *This = impl_from_IAudioRenderClient(iface);
1811 UINT32 write_pos;
1813 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1815 if(!data)
1816 return E_POINTER;
1818 *data = NULL;
1820 EnterCriticalSection(&This->lock);
1822 if(This->getbuf_last){
1823 LeaveCriticalSection(&This->lock);
1824 return AUDCLNT_E_OUT_OF_ORDER;
1827 if(!frames){
1828 LeaveCriticalSection(&This->lock);
1829 return S_OK;
1832 if(This->held_frames + frames > This->bufsize_frames){
1833 LeaveCriticalSection(&This->lock);
1834 return AUDCLNT_E_BUFFER_TOO_LARGE;
1837 write_pos =
1838 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1839 if(write_pos + frames > This->bufsize_frames){
1840 if(This->tmp_buffer_frames < frames){
1841 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1842 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1843 frames * This->fmt->nBlockAlign);
1844 if(!This->tmp_buffer){
1845 LeaveCriticalSection(&This->lock);
1846 return E_OUTOFMEMORY;
1848 This->tmp_buffer_frames = frames;
1850 *data = This->tmp_buffer;
1851 This->getbuf_last = -frames;
1852 }else{
1853 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1854 This->getbuf_last = frames;
1857 silence_buffer(This, *data, frames);
1859 LeaveCriticalSection(&This->lock);
1861 return S_OK;
1864 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1866 UINT32 write_offs_frames =
1867 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1868 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1869 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1870 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1871 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1873 if(written_bytes <= chunk_bytes){
1874 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1875 }else{
1876 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1877 memcpy(This->local_buffer, buffer + chunk_bytes,
1878 written_bytes - chunk_bytes);
1882 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1883 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1885 ACImpl *This = impl_from_IAudioRenderClient(iface);
1886 BYTE *buffer;
1888 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1890 EnterCriticalSection(&This->lock);
1892 if(!written_frames){
1893 This->getbuf_last = 0;
1894 LeaveCriticalSection(&This->lock);
1895 return S_OK;
1898 if(!This->getbuf_last){
1899 LeaveCriticalSection(&This->lock);
1900 return AUDCLNT_E_OUT_OF_ORDER;
1903 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1904 LeaveCriticalSection(&This->lock);
1905 return AUDCLNT_E_INVALID_SIZE;
1908 if(This->getbuf_last >= 0)
1909 buffer = This->local_buffer + This->fmt->nBlockAlign *
1910 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1911 else
1912 buffer = This->tmp_buffer;
1914 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1915 silence_buffer(This, buffer, written_frames);
1917 if(This->getbuf_last < 0)
1918 oss_wrap_buffer(This, buffer, written_frames);
1920 This->held_frames += written_frames;
1921 This->written_frames += written_frames;
1922 This->getbuf_last = 0;
1924 LeaveCriticalSection(&This->lock);
1926 return S_OK;
1929 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1930 AudioRenderClient_QueryInterface,
1931 AudioRenderClient_AddRef,
1932 AudioRenderClient_Release,
1933 AudioRenderClient_GetBuffer,
1934 AudioRenderClient_ReleaseBuffer
1937 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1938 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1940 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1941 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1943 if(!ppv)
1944 return E_POINTER;
1945 *ppv = NULL;
1947 if(IsEqualIID(riid, &IID_IUnknown) ||
1948 IsEqualIID(riid, &IID_IAudioCaptureClient))
1949 *ppv = iface;
1950 else if(IsEqualIID(riid, &IID_IMarshal))
1951 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1952 if(*ppv){
1953 IUnknown_AddRef((IUnknown*)*ppv);
1954 return S_OK;
1957 WARN("Unknown interface %s\n", debugstr_guid(riid));
1958 return E_NOINTERFACE;
1961 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1963 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1964 return IAudioClient_AddRef(&This->IAudioClient_iface);
1967 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1969 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1970 return IAudioClient_Release(&This->IAudioClient_iface);
1973 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1974 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1975 UINT64 *qpcpos)
1977 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1979 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1980 devpos, qpcpos);
1982 if(!data || !frames || !flags)
1983 return E_POINTER;
1985 EnterCriticalSection(&This->lock);
1987 if(This->getbuf_last){
1988 LeaveCriticalSection(&This->lock);
1989 return AUDCLNT_E_OUT_OF_ORDER;
1992 if(This->held_frames < This->period_frames){
1993 *frames = 0;
1994 LeaveCriticalSection(&This->lock);
1995 return AUDCLNT_S_BUFFER_EMPTY;
1998 *flags = 0;
2000 *frames = This->period_frames;
2002 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2003 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2004 if(This->tmp_buffer_frames < *frames){
2005 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2006 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2007 *frames * This->fmt->nBlockAlign);
2008 if(!This->tmp_buffer){
2009 LeaveCriticalSection(&This->lock);
2010 return E_OUTOFMEMORY;
2012 This->tmp_buffer_frames = *frames;
2015 *data = This->tmp_buffer;
2016 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2017 This->fmt->nBlockAlign;
2018 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2019 frames_bytes = *frames * This->fmt->nBlockAlign;
2020 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2021 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2022 frames_bytes - chunk_bytes);
2023 }else
2024 *data = This->local_buffer +
2025 This->lcl_offs_frames * This->fmt->nBlockAlign;
2027 This->getbuf_last = *frames;
2029 if(devpos)
2030 *devpos = This->written_frames;
2031 if(qpcpos){
2032 LARGE_INTEGER stamp, freq;
2033 QueryPerformanceCounter(&stamp);
2034 QueryPerformanceFrequency(&freq);
2035 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2038 LeaveCriticalSection(&This->lock);
2040 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2043 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2044 IAudioCaptureClient *iface, UINT32 done)
2046 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2048 TRACE("(%p)->(%u)\n", This, done);
2050 EnterCriticalSection(&This->lock);
2052 if(!done){
2053 This->getbuf_last = 0;
2054 LeaveCriticalSection(&This->lock);
2055 return S_OK;
2058 if(!This->getbuf_last){
2059 LeaveCriticalSection(&This->lock);
2060 return AUDCLNT_E_OUT_OF_ORDER;
2063 if(This->getbuf_last != done){
2064 LeaveCriticalSection(&This->lock);
2065 return AUDCLNT_E_INVALID_SIZE;
2068 This->written_frames += done;
2069 This->held_frames -= done;
2070 This->lcl_offs_frames += done;
2071 This->lcl_offs_frames %= This->bufsize_frames;
2072 This->getbuf_last = 0;
2074 LeaveCriticalSection(&This->lock);
2076 return S_OK;
2079 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2080 IAudioCaptureClient *iface, UINT32 *frames)
2082 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2084 TRACE("(%p)->(%p)\n", This, frames);
2086 if(!frames)
2087 return E_POINTER;
2089 EnterCriticalSection(&This->lock);
2091 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2093 LeaveCriticalSection(&This->lock);
2095 return S_OK;
2098 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2100 AudioCaptureClient_QueryInterface,
2101 AudioCaptureClient_AddRef,
2102 AudioCaptureClient_Release,
2103 AudioCaptureClient_GetBuffer,
2104 AudioCaptureClient_ReleaseBuffer,
2105 AudioCaptureClient_GetNextPacketSize
2108 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2109 REFIID riid, void **ppv)
2111 ACImpl *This = impl_from_IAudioClock(iface);
2113 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2115 if(!ppv)
2116 return E_POINTER;
2117 *ppv = NULL;
2119 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2120 *ppv = iface;
2121 else if(IsEqualIID(riid, &IID_IAudioClock2))
2122 *ppv = &This->IAudioClock2_iface;
2123 if(*ppv){
2124 IUnknown_AddRef((IUnknown*)*ppv);
2125 return S_OK;
2128 WARN("Unknown interface %s\n", debugstr_guid(riid));
2129 return E_NOINTERFACE;
2132 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2134 ACImpl *This = impl_from_IAudioClock(iface);
2135 return IAudioClient_AddRef(&This->IAudioClient_iface);
2138 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2140 ACImpl *This = impl_from_IAudioClock(iface);
2141 return IAudioClient_Release(&This->IAudioClient_iface);
2144 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2146 ACImpl *This = impl_from_IAudioClock(iface);
2148 TRACE("(%p)->(%p)\n", This, freq);
2150 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2151 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2152 else
2153 *freq = This->fmt->nSamplesPerSec;
2155 return S_OK;
2158 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2159 UINT64 *qpctime)
2161 ACImpl *This = impl_from_IAudioClock(iface);
2162 int delay;
2164 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2166 if(!pos)
2167 return E_POINTER;
2169 EnterCriticalSection(&This->lock);
2171 if(This->dataflow == eRender){
2172 if(!This->playing || !This->held_frames ||
2173 ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay) < 0)
2174 delay = 0;
2175 else
2176 delay /= This->fmt->nBlockAlign;
2177 if(This->held_frames + delay >= This->written_frames)
2178 *pos = This->last_pos_frames;
2179 else{
2180 *pos = This->written_frames - This->held_frames - delay;
2181 if(*pos < This->last_pos_frames)
2182 *pos = This->last_pos_frames;
2184 }else if(This->dataflow == eCapture){
2185 audio_buf_info bi;
2186 UINT32 held;
2188 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2189 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2190 held = 0;
2191 }else{
2192 if(bi.bytes <= bi.fragsize)
2193 held = 0;
2194 else
2195 held = bi.bytes / This->fmt->nBlockAlign;
2198 *pos = This->written_frames + held;
2201 This->last_pos_frames = *pos;
2203 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2204 *pos *= This->fmt->nBlockAlign;
2206 LeaveCriticalSection(&This->lock);
2208 if(qpctime){
2209 LARGE_INTEGER stamp, freq;
2210 QueryPerformanceCounter(&stamp);
2211 QueryPerformanceFrequency(&freq);
2212 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2215 return S_OK;
2218 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2219 DWORD *chars)
2221 ACImpl *This = impl_from_IAudioClock(iface);
2223 TRACE("(%p)->(%p)\n", This, chars);
2225 if(!chars)
2226 return E_POINTER;
2228 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2230 return S_OK;
2233 static const IAudioClockVtbl AudioClock_Vtbl =
2235 AudioClock_QueryInterface,
2236 AudioClock_AddRef,
2237 AudioClock_Release,
2238 AudioClock_GetFrequency,
2239 AudioClock_GetPosition,
2240 AudioClock_GetCharacteristics
2243 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2244 REFIID riid, void **ppv)
2246 ACImpl *This = impl_from_IAudioClock2(iface);
2247 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2250 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2252 ACImpl *This = impl_from_IAudioClock2(iface);
2253 return IAudioClient_AddRef(&This->IAudioClient_iface);
2256 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2258 ACImpl *This = impl_from_IAudioClock2(iface);
2259 return IAudioClient_Release(&This->IAudioClient_iface);
2262 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2263 UINT64 *pos, UINT64 *qpctime)
2265 ACImpl *This = impl_from_IAudioClock2(iface);
2267 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2269 return E_NOTIMPL;
2272 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2274 AudioClock2_QueryInterface,
2275 AudioClock2_AddRef,
2276 AudioClock2_Release,
2277 AudioClock2_GetDevicePosition
2280 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2282 AudioSessionWrapper *ret;
2284 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2285 sizeof(AudioSessionWrapper));
2286 if(!ret)
2287 return NULL;
2289 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2290 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2291 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2293 ret->ref = 1;
2295 ret->client = client;
2296 if(client){
2297 ret->session = client->session;
2298 AudioClient_AddRef(&client->IAudioClient_iface);
2301 return ret;
2304 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2305 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2307 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2309 if(!ppv)
2310 return E_POINTER;
2311 *ppv = NULL;
2313 if(IsEqualIID(riid, &IID_IUnknown) ||
2314 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2315 IsEqualIID(riid, &IID_IAudioSessionControl2))
2316 *ppv = iface;
2317 if(*ppv){
2318 IUnknown_AddRef((IUnknown*)*ppv);
2319 return S_OK;
2322 WARN("Unknown interface %s\n", debugstr_guid(riid));
2323 return E_NOINTERFACE;
2326 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2328 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2329 ULONG ref;
2330 ref = InterlockedIncrement(&This->ref);
2331 TRACE("(%p) Refcount now %u\n", This, ref);
2332 return ref;
2335 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2337 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2338 ULONG ref;
2339 ref = InterlockedDecrement(&This->ref);
2340 TRACE("(%p) Refcount now %u\n", This, ref);
2341 if(!ref){
2342 if(This->client){
2343 EnterCriticalSection(&This->client->lock);
2344 This->client->session_wrapper = NULL;
2345 LeaveCriticalSection(&This->client->lock);
2346 AudioClient_Release(&This->client->IAudioClient_iface);
2348 HeapFree(GetProcessHeap(), 0, This);
2350 return ref;
2353 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2354 AudioSessionState *state)
2356 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2357 ACImpl *client;
2359 TRACE("(%p)->(%p)\n", This, state);
2361 if(!state)
2362 return NULL_PTR_ERR;
2364 EnterCriticalSection(&g_sessions_lock);
2366 if(list_empty(&This->session->clients)){
2367 *state = AudioSessionStateExpired;
2368 LeaveCriticalSection(&g_sessions_lock);
2369 return S_OK;
2372 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2373 EnterCriticalSection(&client->lock);
2374 if(client->playing){
2375 *state = AudioSessionStateActive;
2376 LeaveCriticalSection(&client->lock);
2377 LeaveCriticalSection(&g_sessions_lock);
2378 return S_OK;
2380 LeaveCriticalSection(&client->lock);
2383 LeaveCriticalSection(&g_sessions_lock);
2385 *state = AudioSessionStateInactive;
2387 return S_OK;
2390 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2391 IAudioSessionControl2 *iface, WCHAR **name)
2393 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2395 FIXME("(%p)->(%p) - stub\n", This, name);
2397 return E_NOTIMPL;
2400 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2401 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2403 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2405 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2407 return E_NOTIMPL;
2410 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2411 IAudioSessionControl2 *iface, WCHAR **path)
2413 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2415 FIXME("(%p)->(%p) - stub\n", This, path);
2417 return E_NOTIMPL;
2420 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2421 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2423 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2425 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2427 return E_NOTIMPL;
2430 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2431 IAudioSessionControl2 *iface, GUID *group)
2433 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2435 FIXME("(%p)->(%p) - stub\n", This, group);
2437 return E_NOTIMPL;
2440 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2441 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2443 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2445 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2446 debugstr_guid(session));
2448 return E_NOTIMPL;
2451 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2452 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2454 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2456 FIXME("(%p)->(%p) - stub\n", This, events);
2458 return S_OK;
2461 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2462 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2464 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2466 FIXME("(%p)->(%p) - stub\n", This, events);
2468 return S_OK;
2471 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2472 IAudioSessionControl2 *iface, WCHAR **id)
2474 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2476 FIXME("(%p)->(%p) - stub\n", This, id);
2478 return E_NOTIMPL;
2481 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2482 IAudioSessionControl2 *iface, WCHAR **id)
2484 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2486 FIXME("(%p)->(%p) - stub\n", This, id);
2488 return E_NOTIMPL;
2491 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2492 IAudioSessionControl2 *iface, DWORD *pid)
2494 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2496 TRACE("(%p)->(%p)\n", This, pid);
2498 if(!pid)
2499 return E_POINTER;
2501 *pid = GetCurrentProcessId();
2503 return S_OK;
2506 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2507 IAudioSessionControl2 *iface)
2509 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2511 TRACE("(%p)\n", This);
2513 return S_FALSE;
2516 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2517 IAudioSessionControl2 *iface, BOOL optout)
2519 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2521 TRACE("(%p)->(%d)\n", This, optout);
2523 return S_OK;
2526 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2528 AudioSessionControl_QueryInterface,
2529 AudioSessionControl_AddRef,
2530 AudioSessionControl_Release,
2531 AudioSessionControl_GetState,
2532 AudioSessionControl_GetDisplayName,
2533 AudioSessionControl_SetDisplayName,
2534 AudioSessionControl_GetIconPath,
2535 AudioSessionControl_SetIconPath,
2536 AudioSessionControl_GetGroupingParam,
2537 AudioSessionControl_SetGroupingParam,
2538 AudioSessionControl_RegisterAudioSessionNotification,
2539 AudioSessionControl_UnregisterAudioSessionNotification,
2540 AudioSessionControl_GetSessionIdentifier,
2541 AudioSessionControl_GetSessionInstanceIdentifier,
2542 AudioSessionControl_GetProcessId,
2543 AudioSessionControl_IsSystemSoundsSession,
2544 AudioSessionControl_SetDuckingPreference
2547 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2548 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2550 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2552 if(!ppv)
2553 return E_POINTER;
2554 *ppv = NULL;
2556 if(IsEqualIID(riid, &IID_IUnknown) ||
2557 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2558 *ppv = iface;
2559 if(*ppv){
2560 IUnknown_AddRef((IUnknown*)*ppv);
2561 return S_OK;
2564 WARN("Unknown interface %s\n", debugstr_guid(riid));
2565 return E_NOINTERFACE;
2568 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2570 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2571 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2574 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2576 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2577 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2580 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2581 ISimpleAudioVolume *iface, float level, const GUID *context)
2583 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2584 AudioSession *session = This->session;
2586 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2588 if(level < 0.f || level > 1.f)
2589 return E_INVALIDARG;
2591 if(context)
2592 FIXME("Notifications not supported yet\n");
2594 EnterCriticalSection(&session->lock);
2596 session->master_vol = level;
2598 TRACE("OSS doesn't support setting volume\n");
2600 LeaveCriticalSection(&session->lock);
2602 return S_OK;
2605 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2606 ISimpleAudioVolume *iface, float *level)
2608 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2609 AudioSession *session = This->session;
2611 TRACE("(%p)->(%p)\n", session, level);
2613 if(!level)
2614 return NULL_PTR_ERR;
2616 *level = session->master_vol;
2618 return S_OK;
2621 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2622 BOOL mute, const GUID *context)
2624 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2625 AudioSession *session = This->session;
2627 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2629 EnterCriticalSection(&session->lock);
2631 session->mute = mute;
2633 LeaveCriticalSection(&session->lock);
2635 return S_OK;
2638 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2639 BOOL *mute)
2641 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2642 AudioSession *session = This->session;
2644 TRACE("(%p)->(%p)\n", session, mute);
2646 if(!mute)
2647 return NULL_PTR_ERR;
2649 *mute = This->session->mute;
2651 return S_OK;
2654 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2656 SimpleAudioVolume_QueryInterface,
2657 SimpleAudioVolume_AddRef,
2658 SimpleAudioVolume_Release,
2659 SimpleAudioVolume_SetMasterVolume,
2660 SimpleAudioVolume_GetMasterVolume,
2661 SimpleAudioVolume_SetMute,
2662 SimpleAudioVolume_GetMute
2665 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2666 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2668 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2670 if(!ppv)
2671 return E_POINTER;
2672 *ppv = NULL;
2674 if(IsEqualIID(riid, &IID_IUnknown) ||
2675 IsEqualIID(riid, &IID_IAudioStreamVolume))
2676 *ppv = iface;
2677 if(*ppv){
2678 IUnknown_AddRef((IUnknown*)*ppv);
2679 return S_OK;
2682 WARN("Unknown interface %s\n", debugstr_guid(riid));
2683 return E_NOINTERFACE;
2686 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2688 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2689 return IAudioClient_AddRef(&This->IAudioClient_iface);
2692 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2694 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2695 return IAudioClient_Release(&This->IAudioClient_iface);
2698 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2699 IAudioStreamVolume *iface, UINT32 *out)
2701 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2703 TRACE("(%p)->(%p)\n", This, out);
2705 if(!out)
2706 return E_POINTER;
2708 *out = This->fmt->nChannels;
2710 return S_OK;
2713 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2714 IAudioStreamVolume *iface, UINT32 index, float level)
2716 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2718 TRACE("(%p)->(%d, %f)\n", This, index, level);
2720 if(level < 0.f || level > 1.f)
2721 return E_INVALIDARG;
2723 if(index >= This->fmt->nChannels)
2724 return E_INVALIDARG;
2726 EnterCriticalSection(&This->lock);
2728 This->vols[index] = level;
2730 TRACE("OSS doesn't support setting volume\n");
2732 LeaveCriticalSection(&This->lock);
2734 return S_OK;
2737 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2738 IAudioStreamVolume *iface, UINT32 index, float *level)
2740 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2742 TRACE("(%p)->(%d, %p)\n", This, index, level);
2744 if(!level)
2745 return E_POINTER;
2747 if(index >= This->fmt->nChannels)
2748 return E_INVALIDARG;
2750 *level = This->vols[index];
2752 return S_OK;
2755 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2756 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2758 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2759 int i;
2761 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2763 if(!levels)
2764 return E_POINTER;
2766 if(count != This->fmt->nChannels)
2767 return E_INVALIDARG;
2769 EnterCriticalSection(&This->lock);
2771 for(i = 0; i < count; ++i)
2772 This->vols[i] = levels[i];
2774 TRACE("OSS doesn't support setting volume\n");
2776 LeaveCriticalSection(&This->lock);
2778 return S_OK;
2781 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2782 IAudioStreamVolume *iface, UINT32 count, float *levels)
2784 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2785 int i;
2787 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2789 if(!levels)
2790 return E_POINTER;
2792 if(count != This->fmt->nChannels)
2793 return E_INVALIDARG;
2795 EnterCriticalSection(&This->lock);
2797 for(i = 0; i < count; ++i)
2798 levels[i] = This->vols[i];
2800 LeaveCriticalSection(&This->lock);
2802 return S_OK;
2805 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2807 AudioStreamVolume_QueryInterface,
2808 AudioStreamVolume_AddRef,
2809 AudioStreamVolume_Release,
2810 AudioStreamVolume_GetChannelCount,
2811 AudioStreamVolume_SetChannelVolume,
2812 AudioStreamVolume_GetChannelVolume,
2813 AudioStreamVolume_SetAllVolumes,
2814 AudioStreamVolume_GetAllVolumes
2817 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2818 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2820 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2822 if(!ppv)
2823 return E_POINTER;
2824 *ppv = NULL;
2826 if(IsEqualIID(riid, &IID_IUnknown) ||
2827 IsEqualIID(riid, &IID_IChannelAudioVolume))
2828 *ppv = iface;
2829 if(*ppv){
2830 IUnknown_AddRef((IUnknown*)*ppv);
2831 return S_OK;
2834 WARN("Unknown interface %s\n", debugstr_guid(riid));
2835 return E_NOINTERFACE;
2838 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2840 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2841 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2844 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2846 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2847 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2850 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2851 IChannelAudioVolume *iface, UINT32 *out)
2853 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2854 AudioSession *session = This->session;
2856 TRACE("(%p)->(%p)\n", session, out);
2858 if(!out)
2859 return NULL_PTR_ERR;
2861 *out = session->channel_count;
2863 return S_OK;
2866 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2867 IChannelAudioVolume *iface, UINT32 index, float level,
2868 const GUID *context)
2870 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2871 AudioSession *session = This->session;
2873 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2874 wine_dbgstr_guid(context));
2876 if(level < 0.f || level > 1.f)
2877 return E_INVALIDARG;
2879 if(index >= session->channel_count)
2880 return E_INVALIDARG;
2882 if(context)
2883 FIXME("Notifications not supported yet\n");
2885 EnterCriticalSection(&session->lock);
2887 session->channel_vols[index] = level;
2889 TRACE("OSS doesn't support setting volume\n");
2891 LeaveCriticalSection(&session->lock);
2893 return S_OK;
2896 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2897 IChannelAudioVolume *iface, UINT32 index, float *level)
2899 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2900 AudioSession *session = This->session;
2902 TRACE("(%p)->(%d, %p)\n", session, index, level);
2904 if(!level)
2905 return NULL_PTR_ERR;
2907 if(index >= session->channel_count)
2908 return E_INVALIDARG;
2910 *level = session->channel_vols[index];
2912 return S_OK;
2915 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2916 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2917 const GUID *context)
2919 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2920 AudioSession *session = This->session;
2921 int i;
2923 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2924 wine_dbgstr_guid(context));
2926 if(!levels)
2927 return NULL_PTR_ERR;
2929 if(count != session->channel_count)
2930 return E_INVALIDARG;
2932 if(context)
2933 FIXME("Notifications not supported yet\n");
2935 EnterCriticalSection(&session->lock);
2937 for(i = 0; i < count; ++i)
2938 session->channel_vols[i] = levels[i];
2940 TRACE("OSS doesn't support setting volume\n");
2942 LeaveCriticalSection(&session->lock);
2944 return S_OK;
2947 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2948 IChannelAudioVolume *iface, UINT32 count, float *levels)
2950 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2951 AudioSession *session = This->session;
2952 int i;
2954 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2956 if(!levels)
2957 return NULL_PTR_ERR;
2959 if(count != session->channel_count)
2960 return E_INVALIDARG;
2962 for(i = 0; i < count; ++i)
2963 levels[i] = session->channel_vols[i];
2965 return S_OK;
2968 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2970 ChannelAudioVolume_QueryInterface,
2971 ChannelAudioVolume_AddRef,
2972 ChannelAudioVolume_Release,
2973 ChannelAudioVolume_GetChannelCount,
2974 ChannelAudioVolume_SetChannelVolume,
2975 ChannelAudioVolume_GetChannelVolume,
2976 ChannelAudioVolume_SetAllVolumes,
2977 ChannelAudioVolume_GetAllVolumes
2980 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2981 REFIID riid, void **ppv)
2983 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2985 if(!ppv)
2986 return E_POINTER;
2987 *ppv = NULL;
2989 if(IsEqualIID(riid, &IID_IUnknown) ||
2990 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2991 IsEqualIID(riid, &IID_IAudioSessionManager2))
2992 *ppv = iface;
2993 if(*ppv){
2994 IUnknown_AddRef((IUnknown*)*ppv);
2995 return S_OK;
2998 WARN("Unknown interface %s\n", debugstr_guid(riid));
2999 return E_NOINTERFACE;
3002 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3004 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3005 ULONG ref;
3006 ref = InterlockedIncrement(&This->ref);
3007 TRACE("(%p) Refcount now %u\n", This, ref);
3008 return ref;
3011 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3013 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3014 ULONG ref;
3015 ref = InterlockedDecrement(&This->ref);
3016 TRACE("(%p) Refcount now %u\n", This, ref);
3017 if(!ref)
3018 HeapFree(GetProcessHeap(), 0, This);
3019 return ref;
3022 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3023 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3024 IAudioSessionControl **out)
3026 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3027 AudioSession *session;
3028 AudioSessionWrapper *wrapper;
3029 HRESULT hr;
3031 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3032 flags, out);
3034 hr = get_audio_session(session_guid, This->device, 0, &session);
3035 if(FAILED(hr))
3036 return hr;
3038 wrapper = AudioSessionWrapper_Create(NULL);
3039 if(!wrapper)
3040 return E_OUTOFMEMORY;
3042 wrapper->session = session;
3044 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3046 return S_OK;
3049 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3050 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3051 ISimpleAudioVolume **out)
3053 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3054 AudioSession *session;
3055 AudioSessionWrapper *wrapper;
3056 HRESULT hr;
3058 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3059 flags, out);
3061 hr = get_audio_session(session_guid, This->device, 0, &session);
3062 if(FAILED(hr))
3063 return hr;
3065 wrapper = AudioSessionWrapper_Create(NULL);
3066 if(!wrapper)
3067 return E_OUTOFMEMORY;
3069 wrapper->session = session;
3071 *out = &wrapper->ISimpleAudioVolume_iface;
3073 return S_OK;
3076 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3077 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3079 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3080 FIXME("(%p)->(%p) - stub\n", This, out);
3081 return E_NOTIMPL;
3084 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3085 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3087 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3088 FIXME("(%p)->(%p) - stub\n", This, notification);
3089 return E_NOTIMPL;
3092 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3093 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3095 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3096 FIXME("(%p)->(%p) - stub\n", This, notification);
3097 return E_NOTIMPL;
3100 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3101 IAudioSessionManager2 *iface, const WCHAR *session_id,
3102 IAudioVolumeDuckNotification *notification)
3104 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3105 FIXME("(%p)->(%p) - stub\n", This, notification);
3106 return E_NOTIMPL;
3109 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3110 IAudioSessionManager2 *iface,
3111 IAudioVolumeDuckNotification *notification)
3113 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3114 FIXME("(%p)->(%p) - stub\n", This, notification);
3115 return E_NOTIMPL;
3118 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3120 AudioSessionManager_QueryInterface,
3121 AudioSessionManager_AddRef,
3122 AudioSessionManager_Release,
3123 AudioSessionManager_GetAudioSessionControl,
3124 AudioSessionManager_GetSimpleAudioVolume,
3125 AudioSessionManager_GetSessionEnumerator,
3126 AudioSessionManager_RegisterSessionNotification,
3127 AudioSessionManager_UnregisterSessionNotification,
3128 AudioSessionManager_RegisterDuckNotification,
3129 AudioSessionManager_UnregisterDuckNotification
3132 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3133 IAudioSessionManager2 **out)
3135 SessionMgr *This;
3137 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3138 if(!This)
3139 return E_OUTOFMEMORY;
3141 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3142 This->device = device;
3143 This->ref = 1;
3145 *out = &This->IAudioSessionManager2_iface;
3147 return S_OK;