taskmgr: Remove dead code.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob0688f9a3ad9cbfb8a20471d2457dcfe9f2813ab6
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <sys/soundcard.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
45 #include "ole2.h"
46 #include "mmdeviceapi.h"
47 #include "devpkey.h"
48 #include "dshow.h"
49 #include "dsound.h"
51 #include "initguid.h"
52 #include "endpointvolume.h"
53 #include "audiopolicy.h"
54 #include "audioclient.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(oss);
58 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
60 static const REFERENCE_TIME DefaultPeriod = 100000;
61 static const REFERENCE_TIME MinimumPeriod = 50000;
63 struct ACImpl;
64 typedef struct ACImpl ACImpl;
66 typedef struct _AudioSession {
67 GUID guid;
68 struct list clients;
70 IMMDevice *device;
72 float master_vol;
73 UINT32 channel_count;
74 float *channel_vols;
75 BOOL mute;
77 CRITICAL_SECTION lock;
79 struct list entry;
80 } AudioSession;
82 typedef struct _AudioSessionWrapper {
83 IAudioSessionControl2 IAudioSessionControl2_iface;
84 IChannelAudioVolume IChannelAudioVolume_iface;
85 ISimpleAudioVolume ISimpleAudioVolume_iface;
87 LONG ref;
89 ACImpl *client;
90 AudioSession *session;
91 } AudioSessionWrapper;
93 struct ACImpl {
94 IAudioClient IAudioClient_iface;
95 IAudioRenderClient IAudioRenderClient_iface;
96 IAudioCaptureClient IAudioCaptureClient_iface;
97 IAudioClock IAudioClock_iface;
98 IAudioClock2 IAudioClock2_iface;
99 IAudioStreamVolume IAudioStreamVolume_iface;
101 LONG ref;
103 IMMDevice *parent;
104 IUnknown *pUnkFTMarshal;
106 WAVEFORMATEX *fmt;
108 EDataFlow dataflow;
109 DWORD flags;
110 AUDCLNT_SHAREMODE share;
111 HANDLE event;
112 float *vols;
114 int fd;
115 oss_audioinfo ai;
116 char devnode[OSS_DEVNODE_SIZE];
118 BOOL initted, playing;
119 UINT64 written_frames, last_pos_frames;
120 UINT32 period_us, period_frames, bufsize_frames, held_frames, tmp_buffer_frames, in_oss_frames;
121 UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
123 BYTE *local_buffer, *tmp_buffer;
124 LONG32 getbuf_last; /* <0 when using tmp_buffer */
125 HANDLE timer;
127 CRITICAL_SECTION lock;
129 AudioSession *session;
130 AudioSessionWrapper *session_wrapper;
132 struct list entry;
135 typedef struct _SessionMgr {
136 IAudioSessionManager2 IAudioSessionManager2_iface;
138 LONG ref;
140 IMMDevice *device;
141 } SessionMgr;
143 typedef struct _OSSDevice {
144 EDataFlow flow;
145 char devnode[OSS_DEVNODE_SIZE];
146 GUID guid;
148 struct list entry;
149 } OSSDevice;
151 static struct list g_devices = LIST_INIT(g_devices);
153 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
154 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
155 'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
156 static const WCHAR guidW[] = {'g','u','i','d',0};
158 static HANDLE g_timer_q;
160 static CRITICAL_SECTION g_sessions_lock;
161 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
163 0, 0, &g_sessions_lock,
164 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
165 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
167 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
168 static struct list g_sessions = LIST_INIT(g_sessions);
170 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
172 static const IAudioClientVtbl AudioClient_Vtbl;
173 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
174 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
175 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
176 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
177 static const IAudioClockVtbl AudioClock_Vtbl;
178 static const IAudioClock2Vtbl AudioClock2_Vtbl;
179 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
180 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
181 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
183 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
185 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
188 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
190 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
193 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
195 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
198 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
200 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
203 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
205 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
208 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
210 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
213 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
215 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
218 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
220 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
223 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
225 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
228 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
230 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
233 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
235 switch (reason)
237 case DLL_PROCESS_ATTACH:
238 g_timer_q = CreateTimerQueue();
239 if(!g_timer_q)
240 return FALSE;
241 break;
243 case DLL_PROCESS_DETACH:
244 if (!reserved)
246 OSSDevice *iter, *iter2;
248 DeleteCriticalSection(&g_sessions_lock);
250 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
251 HeapFree(GetProcessHeap(), 0, iter);
254 break;
256 return TRUE;
259 /* From <dlls/mmdevapi/mmdevapi.h> */
260 enum DriverPriority {
261 Priority_Unavailable = 0,
262 Priority_Low,
263 Priority_Neutral,
264 Priority_Preferred
267 int WINAPI AUDDRV_GetPriority(void)
269 int mixer_fd;
270 oss_sysinfo sysinfo;
272 /* Attempt to determine if we are running on OSS or ALSA's OSS
273 * compatibility layer. There is no official way to do that, so just check
274 * for validity as best as possible, without rejecting valid OSS
275 * implementations. */
277 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
278 if(mixer_fd < 0){
279 TRACE("Priority_Unavailable: open failed\n");
280 return Priority_Unavailable;
283 sysinfo.version[0] = 0xFF;
284 sysinfo.versionnum = ~0;
285 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
286 TRACE("Priority_Unavailable: ioctl failed\n");
287 close(mixer_fd);
288 return Priority_Unavailable;
291 close(mixer_fd);
293 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
294 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
295 return Priority_Low;
297 if(sysinfo.versionnum & 0x80000000){
298 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
299 return Priority_Low;
302 TRACE("Priority_Preferred: Seems like valid OSS!\n");
304 return Priority_Preferred;
307 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
308 GUID *guid)
310 HKEY key;
311 BOOL opened = FALSE;
312 LONG lr;
314 if(!drv_key){
315 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
316 NULL, &drv_key, NULL);
317 if(lr != ERROR_SUCCESS){
318 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
319 return;
321 opened = TRUE;
324 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
325 NULL, &key, NULL);
326 if(lr != ERROR_SUCCESS){
327 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
328 goto exit;
331 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
332 sizeof(GUID));
333 if(lr != ERROR_SUCCESS)
334 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
336 RegCloseKey(key);
337 exit:
338 if(opened)
339 RegCloseKey(drv_key);
342 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
344 HKEY key = NULL, dev_key;
345 DWORD type, size = sizeof(*guid);
346 WCHAR key_name[256];
348 if(flow == eCapture)
349 key_name[0] = '1';
350 else
351 key_name[0] = '0';
352 key_name[1] = ',';
353 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
354 (sizeof(key_name) / sizeof(*key_name)) - 2);
356 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
357 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
358 if(RegQueryValueExW(dev_key, guidW, 0, &type,
359 (BYTE*)guid, &size) == ERROR_SUCCESS){
360 if(type == REG_BINARY){
361 RegCloseKey(dev_key);
362 RegCloseKey(key);
363 return;
365 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
366 wine_dbgstr_w(key_name), type);
368 RegCloseKey(dev_key);
372 CoCreateGuid(guid);
374 set_device_guid(flow, key, key_name, guid);
376 if(key)
377 RegCloseKey(key);
380 static const char *oss_clean_devnode(const char *devnode)
382 static char ret[OSS_DEVNODE_SIZE];
384 const char *dot, *slash;
385 size_t len;
387 dot = strrchr(devnode, '.');
388 if(!dot)
389 return devnode;
391 slash = strrchr(devnode, '/');
392 if(slash && dot < slash)
393 return devnode;
395 len = dot - devnode;
397 memcpy(ret, devnode, len);
398 ret[len] = '\0';
400 return ret;
403 static UINT get_default_index(EDataFlow flow)
405 int fd = -1, err;
406 UINT i;
407 oss_audioinfo ai;
408 const char *devnode;
409 OSSDevice *dev_item;
411 if(flow == eRender)
412 fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
413 else
414 fd = open("/dev/dsp", O_RDONLY | O_NONBLOCK);
416 if(fd < 0){
417 WARN("Couldn't open default device!\n");
418 return 0;
421 ai.dev = -1;
422 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
423 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
424 close(fd);
425 return 0;
428 close(fd);
430 TRACE("Default devnode: %s\n", ai.devnode);
431 devnode = oss_clean_devnode(ai.devnode);
432 i = 0;
433 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
434 if(dev_item->flow == flow){
435 if(!strcmp(devnode, dev_item->devnode))
436 return i;
437 ++i;
441 WARN("Couldn't find default device! Choosing first.\n");
442 return 0;
445 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
446 UINT *num, UINT *def_index)
448 int i, mixer_fd;
449 oss_sysinfo sysinfo;
450 static int print_once = 0;
452 static const WCHAR outW[] = {'O','u','t',':',' ',0};
453 static const WCHAR inW[] = {'I','n',':',' ',0};
455 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
457 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
458 if(mixer_fd < 0){
459 ERR("OSS /dev/mixer doesn't seem to exist\n");
460 return AUDCLNT_E_SERVICE_NOT_RUNNING;
463 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
464 close(mixer_fd);
466 if(errno == EINVAL){
467 ERR("OSS version too old, need at least OSSv4\n");
468 return AUDCLNT_E_SERVICE_NOT_RUNNING;
471 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
472 return E_FAIL;
475 if(!print_once){
476 TRACE("OSS sysinfo:\n");
477 TRACE("product: %s\n", sysinfo.product);
478 TRACE("version: %s\n", sysinfo.version);
479 TRACE("versionnum: %x\n", sysinfo.versionnum);
480 TRACE("numaudios: %d\n", sysinfo.numaudios);
481 TRACE("nummixers: %d\n", sysinfo.nummixers);
482 TRACE("numcards: %d\n", sysinfo.numcards);
483 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
484 print_once = 1;
487 if(sysinfo.numaudios <= 0){
488 WARN("No audio devices!\n");
489 close(mixer_fd);
490 return AUDCLNT_E_SERVICE_NOT_RUNNING;
493 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
494 *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
496 *num = 0;
497 for(i = 0; i < sysinfo.numaudios; ++i){
498 oss_audioinfo ai = {0};
499 const char *devnode;
500 OSSDevice *dev_item;
501 int fd;
503 ai.dev = i;
504 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
505 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
506 strerror(errno));
507 continue;
510 devnode = oss_clean_devnode(ai.devnode);
512 /* check for duplicates */
513 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
514 if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
515 break;
517 if(&dev_item->entry != &g_devices)
518 continue;
520 if(flow == eRender)
521 fd = open(devnode, O_WRONLY | O_NONBLOCK, 0);
522 else
523 fd = open(devnode, O_RDONLY | O_NONBLOCK, 0);
524 if(fd < 0){
525 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
526 devnode, errno, strerror(errno));
527 continue;
529 close(fd);
531 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
532 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
533 size_t len, prefix_len;
534 const WCHAR *prefix;
536 dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
538 dev_item->flow = flow;
539 get_device_guid(flow, devnode, &dev_item->guid);
540 strcpy(dev_item->devnode, devnode);
542 (*guids)[*num] = dev_item->guid;
544 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
545 if(flow == eRender){
546 prefix = outW;
547 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
548 len += prefix_len;
549 }else{
550 prefix = inW;
551 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
552 len += prefix_len;
554 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
555 len * sizeof(WCHAR));
556 if(!(*ids)[*num]){
557 for(i = 0; i < *num; ++i)
558 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
559 HeapFree(GetProcessHeap(), 0, *ids);
560 HeapFree(GetProcessHeap(), 0, *guids);
561 HeapFree(GetProcessHeap(), 0, dev_item);
562 close(mixer_fd);
563 return E_OUTOFMEMORY;
565 memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
566 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
567 (*ids)[*num] + prefix_len, len - prefix_len);
569 list_add_tail(&g_devices, &dev_item->entry);
571 (*num)++;
575 close(mixer_fd);
577 *def_index = get_default_index(flow);
579 return S_OK;
582 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
584 OSSDevice *dev_item;
585 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
586 if(IsEqualGUID(guid, &dev_item->guid))
587 return dev_item;
588 return NULL;
591 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
592 IAudioClient **out)
594 ACImpl *This;
595 const OSSDevice *oss_dev;
596 HRESULT hr;
598 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
600 oss_dev = get_ossdevice_from_guid(guid);
601 if(!oss_dev){
602 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
603 return AUDCLNT_E_DEVICE_INVALIDATED;
606 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
607 if(!This)
608 return E_OUTOFMEMORY;
610 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
611 (IUnknown **)&This->pUnkFTMarshal);
612 if (FAILED(hr)) {
613 HeapFree(GetProcessHeap(), 0, This);
614 return hr;
617 if(oss_dev->flow == eRender)
618 This->fd = open(oss_dev->devnode, O_WRONLY | O_NONBLOCK, 0);
619 else if(oss_dev->flow == eCapture)
620 This->fd = open(oss_dev->devnode, O_RDONLY | O_NONBLOCK, 0);
621 else{
622 HeapFree(GetProcessHeap(), 0, This);
623 return E_INVALIDARG;
625 if(This->fd < 0){
626 WARN("Unable to open device %s: %d (%s)\n", oss_dev->devnode, errno,
627 strerror(errno));
628 HeapFree(GetProcessHeap(), 0, This);
629 return AUDCLNT_E_DEVICE_INVALIDATED;
632 This->dataflow = oss_dev->flow;
634 This->ai.dev = -1;
635 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
636 WARN("Unable to get audio info for device %s: %d (%s)\n", oss_dev->devnode,
637 errno, strerror(errno));
638 close(This->fd);
639 HeapFree(GetProcessHeap(), 0, This);
640 return E_FAIL;
643 strcpy(This->devnode, oss_dev->devnode);
645 TRACE("OSS audioinfo:\n");
646 TRACE("devnode: %s\n", This->ai.devnode);
647 TRACE("name: %s\n", This->ai.name);
648 TRACE("busy: %x\n", This->ai.busy);
649 TRACE("caps: %x\n", This->ai.caps);
650 TRACE("iformats: %x\n", This->ai.iformats);
651 TRACE("oformats: %x\n", This->ai.oformats);
652 TRACE("enabled: %d\n", This->ai.enabled);
653 TRACE("min_rate: %d\n", This->ai.min_rate);
654 TRACE("max_rate: %d\n", This->ai.max_rate);
655 TRACE("min_channels: %d\n", This->ai.min_channels);
656 TRACE("max_channels: %d\n", This->ai.max_channels);
658 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
659 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
660 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
661 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
662 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
663 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
665 InitializeCriticalSection(&This->lock);
666 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
668 This->parent = dev;
669 IMMDevice_AddRef(This->parent);
671 IAudioClient_AddRef(&This->IAudioClient_iface);
673 *out = &This->IAudioClient_iface;
675 return S_OK;
678 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
679 REFIID riid, void **ppv)
681 ACImpl *This = impl_from_IAudioClient(iface);
682 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
684 if(!ppv)
685 return E_POINTER;
686 *ppv = NULL;
687 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
688 *ppv = iface;
689 else if(IsEqualIID(riid, &IID_IMarshal))
690 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
691 if(*ppv){
692 IUnknown_AddRef((IUnknown*)*ppv);
693 return S_OK;
695 WARN("Unknown interface %s\n", debugstr_guid(riid));
696 return E_NOINTERFACE;
699 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
701 ACImpl *This = impl_from_IAudioClient(iface);
702 ULONG ref;
703 ref = InterlockedIncrement(&This->ref);
704 TRACE("(%p) Refcount now %u\n", This, ref);
705 return ref;
708 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
710 ACImpl *This = impl_from_IAudioClient(iface);
711 ULONG ref;
713 ref = InterlockedDecrement(&This->ref);
714 TRACE("(%p) Refcount now %u\n", This, ref);
715 if(!ref){
716 if(This->timer){
717 HANDLE event;
718 DWORD wait;
719 event = CreateEventW(NULL, TRUE, FALSE, NULL);
720 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
721 wait = wait && GetLastError() == ERROR_IO_PENDING;
722 if(event && wait)
723 WaitForSingleObject(event, INFINITE);
724 CloseHandle(event);
727 IAudioClient_Stop(iface);
728 IMMDevice_Release(This->parent);
729 IUnknown_Release(This->pUnkFTMarshal);
730 This->lock.DebugInfo->Spare[0] = 0;
731 DeleteCriticalSection(&This->lock);
732 close(This->fd);
733 if(This->initted){
734 EnterCriticalSection(&g_sessions_lock);
735 list_remove(&This->entry);
736 LeaveCriticalSection(&g_sessions_lock);
738 HeapFree(GetProcessHeap(), 0, This->vols);
739 HeapFree(GetProcessHeap(), 0, This->local_buffer);
740 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
741 CoTaskMemFree(This->fmt);
742 HeapFree(GetProcessHeap(), 0, This);
744 return ref;
747 static void dump_fmt(const WAVEFORMATEX *fmt)
749 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
750 switch(fmt->wFormatTag){
751 case WAVE_FORMAT_PCM:
752 TRACE("WAVE_FORMAT_PCM");
753 break;
754 case WAVE_FORMAT_IEEE_FLOAT:
755 TRACE("WAVE_FORMAT_IEEE_FLOAT");
756 break;
757 case WAVE_FORMAT_EXTENSIBLE:
758 TRACE("WAVE_FORMAT_EXTENSIBLE");
759 break;
760 default:
761 TRACE("Unknown");
762 break;
764 TRACE(")\n");
766 TRACE("nChannels: %u\n", fmt->nChannels);
767 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
768 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
769 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
770 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
771 TRACE("cbSize: %u\n", fmt->cbSize);
773 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
774 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
775 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
776 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
777 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
781 static DWORD get_channel_mask(unsigned int channels)
783 switch(channels){
784 case 0:
785 return 0;
786 case 1:
787 return KSAUDIO_SPEAKER_MONO;
788 case 2:
789 return KSAUDIO_SPEAKER_STEREO;
790 case 3:
791 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
792 case 4:
793 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
794 case 5:
795 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
796 case 6:
797 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
798 case 7:
799 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
800 case 8:
801 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
803 FIXME("Unknown speaker configuration: %u\n", channels);
804 return 0;
807 static int get_oss_format(const WAVEFORMATEX *fmt)
809 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
811 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
812 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
813 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
814 switch(fmt->wBitsPerSample){
815 case 8:
816 return AFMT_U8;
817 case 16:
818 return AFMT_S16_LE;
819 case 24:
820 return AFMT_S24_LE;
821 case 32:
822 return AFMT_S32_LE;
824 return -1;
827 #ifdef AFMT_FLOAT
828 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
829 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
830 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
831 if(fmt->wBitsPerSample != 32)
832 return -1;
834 return AFMT_FLOAT;
836 #endif
838 return -1;
841 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
843 WAVEFORMATEX *ret;
844 size_t size;
846 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
847 size = sizeof(WAVEFORMATEXTENSIBLE);
848 else
849 size = sizeof(WAVEFORMATEX);
851 ret = CoTaskMemAlloc(size);
852 if(!ret)
853 return NULL;
855 memcpy(ret, fmt, size);
857 ret->cbSize = size - sizeof(WAVEFORMATEX);
859 return ret;
862 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd,
863 const WAVEFORMATEX *fmt, WAVEFORMATEX **out)
865 int tmp, oss_format;
866 double tenth;
867 HRESULT ret = S_OK;
868 WAVEFORMATEX *closest = NULL;
870 tmp = oss_format = get_oss_format(fmt);
871 if(oss_format < 0)
872 return AUDCLNT_E_UNSUPPORTED_FORMAT;
873 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
874 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
875 return E_FAIL;
877 if(tmp != oss_format){
878 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
879 return AUDCLNT_E_UNSUPPORTED_FORMAT;
882 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
883 (fmt->nAvgBytesPerSec == 0 ||
884 fmt->nBlockAlign == 0 ||
885 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
886 return E_INVALIDARG;
888 if(fmt->nChannels == 0)
889 return AUDCLNT_E_UNSUPPORTED_FORMAT;
891 closest = clone_format(fmt);
892 if(!closest)
893 return E_OUTOFMEMORY;
895 tmp = fmt->nSamplesPerSec;
896 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
897 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
898 CoTaskMemFree(closest);
899 return E_FAIL;
901 tenth = fmt->nSamplesPerSec * 0.1;
902 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
903 ret = S_FALSE;
904 closest->nSamplesPerSec = tmp;
907 tmp = fmt->nChannels;
908 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
909 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
910 CoTaskMemFree(closest);
911 return E_FAIL;
913 if(tmp != fmt->nChannels){
914 ret = S_FALSE;
915 closest->nChannels = tmp;
918 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
919 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
921 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
922 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
923 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
924 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
925 ret = S_FALSE;
927 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
928 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
929 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
930 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
931 ret = S_FALSE;
934 if(ret == S_FALSE && !out)
935 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
937 if(ret == S_FALSE && out){
938 closest->nBlockAlign =
939 closest->nChannels * closest->wBitsPerSample / 8;
940 closest->nAvgBytesPerSec =
941 closest->nBlockAlign * closest->nSamplesPerSec;
942 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
943 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
944 *out = closest;
945 } else
946 CoTaskMemFree(closest);
948 TRACE("returning: %08x\n", ret);
949 return ret;
952 static void session_init_vols(AudioSession *session, UINT channels)
954 if(session->channel_count < channels){
955 UINT i;
957 if(session->channel_vols)
958 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
959 session->channel_vols, sizeof(float) * channels);
960 else
961 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
962 sizeof(float) * channels);
963 if(!session->channel_vols)
964 return;
966 for(i = session->channel_count; i < channels; ++i)
967 session->channel_vols[i] = 1.f;
969 session->channel_count = channels;
973 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
974 UINT num_channels)
976 AudioSession *ret;
978 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
979 if(!ret)
980 return NULL;
982 memcpy(&ret->guid, guid, sizeof(GUID));
984 ret->device = device;
986 list_init(&ret->clients);
988 list_add_head(&g_sessions, &ret->entry);
990 InitializeCriticalSection(&ret->lock);
991 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
993 session_init_vols(ret, num_channels);
995 ret->master_vol = 1.f;
997 return ret;
1000 /* if channels == 0, then this will return or create a session with
1001 * matching dataflow and GUID. otherwise, channels must also match */
1002 static HRESULT get_audio_session(const GUID *sessionguid,
1003 IMMDevice *device, UINT channels, AudioSession **out)
1005 AudioSession *session;
1007 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1008 *out = create_session(&GUID_NULL, device, channels);
1009 if(!*out)
1010 return E_OUTOFMEMORY;
1012 return S_OK;
1015 *out = NULL;
1016 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1017 if(session->device == device &&
1018 IsEqualGUID(sessionguid, &session->guid)){
1019 session_init_vols(session, channels);
1020 *out = session;
1021 break;
1025 if(!*out){
1026 *out = create_session(sessionguid, device, channels);
1027 if(!*out)
1028 return E_OUTOFMEMORY;
1031 return S_OK;
1034 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1035 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1036 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1037 const GUID *sessionguid)
1039 ACImpl *This = impl_from_IAudioClient(iface);
1040 int i;
1041 HRESULT hr;
1043 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1044 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1046 if(!fmt)
1047 return E_POINTER;
1049 dump_fmt(fmt);
1051 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1052 return AUDCLNT_E_NOT_INITIALIZED;
1054 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1055 AUDCLNT_STREAMFLAGS_LOOPBACK |
1056 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1057 AUDCLNT_STREAMFLAGS_NOPERSIST |
1058 AUDCLNT_STREAMFLAGS_RATEADJUST |
1059 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1060 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1061 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1062 TRACE("Unknown flags: %08x\n", flags);
1063 return E_INVALIDARG;
1066 if(mode == AUDCLNT_SHAREMODE_SHARED){
1067 period = DefaultPeriod;
1068 if( duration < 3 * period)
1069 duration = 3 * period;
1070 }else{
1071 if(!period)
1072 period = DefaultPeriod; /* not minimum */
1073 if(period < MinimumPeriod || period > 5000000)
1074 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1075 if(duration > 20000000) /* the smaller the period, the lower this limit */
1076 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1077 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1078 if(duration != period)
1079 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1080 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1081 return AUDCLNT_E_DEVICE_IN_USE;
1082 }else{
1083 if( duration < 8 * period)
1084 duration = 8 * period; /* may grow above 2s */
1088 EnterCriticalSection(&This->lock);
1090 if(This->initted){
1091 LeaveCriticalSection(&This->lock);
1092 return AUDCLNT_E_ALREADY_INITIALIZED;
1095 hr = setup_oss_device(mode, This->fd, fmt, NULL);
1096 if(FAILED(hr)){
1097 LeaveCriticalSection(&This->lock);
1098 return hr;
1101 This->fmt = clone_format(fmt);
1102 if(!This->fmt){
1103 LeaveCriticalSection(&This->lock);
1104 return E_OUTOFMEMORY;
1107 This->period_us = period / 10;
1108 This->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000);
1110 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1111 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1112 This->bufsize_frames -= This->bufsize_frames % This->period_frames;
1113 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1114 This->bufsize_frames * fmt->nBlockAlign);
1115 if(!This->local_buffer){
1116 CoTaskMemFree(This->fmt);
1117 This->fmt = NULL;
1118 LeaveCriticalSection(&This->lock);
1119 return E_OUTOFMEMORY;
1122 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1123 if(!This->vols){
1124 CoTaskMemFree(This->fmt);
1125 This->fmt = NULL;
1126 LeaveCriticalSection(&This->lock);
1127 return E_OUTOFMEMORY;
1130 for(i = 0; i < fmt->nChannels; ++i)
1131 This->vols[i] = 1.f;
1133 This->share = mode;
1134 This->flags = flags;
1135 This->oss_bufsize_bytes = 0;
1137 EnterCriticalSection(&g_sessions_lock);
1139 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1140 &This->session);
1141 if(FAILED(hr)){
1142 LeaveCriticalSection(&g_sessions_lock);
1143 HeapFree(GetProcessHeap(), 0, This->vols);
1144 This->vols = NULL;
1145 CoTaskMemFree(This->fmt);
1146 This->fmt = NULL;
1147 LeaveCriticalSection(&This->lock);
1148 return hr;
1151 list_add_tail(&This->session->clients, &This->entry);
1153 LeaveCriticalSection(&g_sessions_lock);
1155 This->initted = TRUE;
1157 LeaveCriticalSection(&This->lock);
1159 return S_OK;
1162 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1163 UINT32 *frames)
1165 ACImpl *This = impl_from_IAudioClient(iface);
1167 TRACE("(%p)->(%p)\n", This, frames);
1169 if(!frames)
1170 return E_POINTER;
1172 EnterCriticalSection(&This->lock);
1174 if(!This->initted){
1175 LeaveCriticalSection(&This->lock);
1176 return AUDCLNT_E_NOT_INITIALIZED;
1179 *frames = This->bufsize_frames;
1181 TRACE("buffer size: %u\n", *frames);
1183 LeaveCriticalSection(&This->lock);
1185 return S_OK;
1188 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1189 REFERENCE_TIME *latency)
1191 ACImpl *This = impl_from_IAudioClient(iface);
1193 TRACE("(%p)->(%p)\n", This, latency);
1195 if(!latency)
1196 return E_POINTER;
1198 EnterCriticalSection(&This->lock);
1200 if(!This->initted){
1201 LeaveCriticalSection(&This->lock);
1202 return AUDCLNT_E_NOT_INITIALIZED;
1205 /* pretend we process audio in Period chunks, so max latency includes
1206 * the period time. Some native machines add .6666ms in shared mode. */
1207 *latency = This->period_us * 10 + 6666;
1209 LeaveCriticalSection(&This->lock);
1211 return S_OK;
1214 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1215 UINT32 *numpad)
1217 ACImpl *This = impl_from_IAudioClient(iface);
1219 TRACE("(%p)->(%p)\n", This, numpad);
1221 if(!numpad)
1222 return E_POINTER;
1224 EnterCriticalSection(&This->lock);
1226 if(!This->initted){
1227 LeaveCriticalSection(&This->lock);
1228 return AUDCLNT_E_NOT_INITIALIZED;
1231 *numpad = This->held_frames;
1233 TRACE("padding: %u\n", *numpad);
1235 LeaveCriticalSection(&This->lock);
1237 return S_OK;
1240 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1241 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1242 WAVEFORMATEX **outpwfx)
1244 ACImpl *This = impl_from_IAudioClient(iface);
1245 int fd = -1;
1246 HRESULT ret;
1248 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1250 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1251 return E_POINTER;
1253 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1254 return E_INVALIDARG;
1256 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1257 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1258 return E_INVALIDARG;
1260 dump_fmt(pwfx);
1262 if(outpwfx){
1263 *outpwfx = NULL;
1264 if(mode != AUDCLNT_SHAREMODE_SHARED)
1265 outpwfx = NULL;
1268 if(This->dataflow == eRender)
1269 fd = open(This->devnode, O_WRONLY | O_NONBLOCK, 0);
1270 else if(This->dataflow == eCapture)
1271 fd = open(This->devnode, O_RDONLY | O_NONBLOCK, 0);
1273 if(fd < 0){
1274 WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1275 strerror(errno));
1276 return AUDCLNT_E_DEVICE_INVALIDATED;
1279 ret = setup_oss_device(mode, fd, pwfx, outpwfx);
1281 close(fd);
1283 return ret;
1286 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1287 WAVEFORMATEX **pwfx)
1289 ACImpl *This = impl_from_IAudioClient(iface);
1290 WAVEFORMATEXTENSIBLE *fmt;
1291 int formats;
1293 TRACE("(%p)->(%p)\n", This, pwfx);
1295 if(!pwfx)
1296 return E_POINTER;
1297 *pwfx = NULL;
1299 if(This->dataflow == eRender)
1300 formats = This->ai.oformats;
1301 else if(This->dataflow == eCapture)
1302 formats = This->ai.iformats;
1303 else
1304 return E_UNEXPECTED;
1306 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1307 if(!fmt)
1308 return E_OUTOFMEMORY;
1310 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1311 if(formats & AFMT_S16_LE){
1312 fmt->Format.wBitsPerSample = 16;
1313 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1314 #ifdef AFMT_FLOAT
1315 }else if(formats & AFMT_FLOAT){
1316 fmt->Format.wBitsPerSample = 32;
1317 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1318 #endif
1319 }else if(formats & AFMT_U8){
1320 fmt->Format.wBitsPerSample = 8;
1321 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1322 }else if(formats & AFMT_S32_LE){
1323 fmt->Format.wBitsPerSample = 32;
1324 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1325 }else if(formats & AFMT_S24_LE){
1326 fmt->Format.wBitsPerSample = 24;
1327 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1328 }else{
1329 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1330 CoTaskMemFree(fmt);
1331 return E_FAIL;
1334 /* some OSS drivers are buggy, so set reasonable defaults if
1335 * the reported values seem wacky */
1336 fmt->Format.nChannels = max(This->ai.max_channels, This->ai.min_channels);
1337 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1338 fmt->Format.nChannels = 2;
1340 if(This->ai.max_rate == 0)
1341 fmt->Format.nSamplesPerSec = 44100;
1342 else
1343 fmt->Format.nSamplesPerSec = min(This->ai.max_rate, 44100);
1344 if(fmt->Format.nSamplesPerSec < This->ai.min_rate)
1345 fmt->Format.nSamplesPerSec = This->ai.min_rate;
1347 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1349 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1350 fmt->Format.nChannels) / 8;
1351 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1352 fmt->Format.nBlockAlign;
1354 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1355 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1357 *pwfx = (WAVEFORMATEX*)fmt;
1358 dump_fmt(*pwfx);
1360 return S_OK;
1363 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1364 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1366 ACImpl *This = impl_from_IAudioClient(iface);
1368 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1370 if(!defperiod && !minperiod)
1371 return E_POINTER;
1373 if(defperiod)
1374 *defperiod = DefaultPeriod;
1375 if(minperiod)
1376 *minperiod = MinimumPeriod;
1378 return S_OK;
1381 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1383 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1384 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1385 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1386 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1387 This->fmt->wBitsPerSample == 8)
1388 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1389 else
1390 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1393 static void oss_write_data(ACImpl *This)
1395 ssize_t written_bytes;
1396 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
1397 size_t to_write_frames, to_write_bytes, advanced;
1398 audio_buf_info bi;
1399 BYTE *buf;
1401 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1402 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1403 return;
1406 max_period = max(bi.fragsize / This->fmt->nBlockAlign, This->period_frames);
1408 if(bi.bytes > This->oss_bufsize_bytes){
1409 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
1410 bi.bytes, This->oss_bufsize_bytes);
1411 This->oss_bufsize_bytes = bi.bytes;
1412 in_oss_frames = 0;
1413 }else if(This->oss_bufsize_bytes - bi.bytes <= bi.fragsize)
1414 in_oss_frames = 0;
1415 else
1416 in_oss_frames = (This->oss_bufsize_bytes - bi.bytes) / This->fmt->nBlockAlign;
1418 write_limit = 0;
1419 while(write_limit + in_oss_frames < max_period * 3)
1420 write_limit += max_period;
1421 if(write_limit == 0)
1422 return;
1424 /* vvvvvv - in_oss_frames
1425 * [--xxxxxxxxxx]
1426 * [xxxxxxxxxx--]
1427 * ^^^^^^^^^^ - held_frames
1428 * ^ - lcl_offs_frames
1430 advanced = This->in_oss_frames - in_oss_frames;
1431 if(advanced > This->held_frames)
1432 advanced = This->held_frames;
1433 This->lcl_offs_frames += advanced;
1434 This->lcl_offs_frames %= This->bufsize_frames;
1435 This->held_frames -= advanced;
1436 This->in_oss_frames = in_oss_frames;
1439 if(This->held_frames == This->in_oss_frames)
1440 return;
1442 write_offs_frames = (This->lcl_offs_frames + This->in_oss_frames) % This->bufsize_frames;
1443 new_frames = This->held_frames - This->in_oss_frames;
1445 if(write_offs_frames + new_frames > This->bufsize_frames)
1446 to_write_frames = This->bufsize_frames - write_offs_frames;
1447 else
1448 to_write_frames = new_frames;
1450 to_write_frames = min(to_write_frames, write_limit);
1451 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1454 buf = This->local_buffer + write_offs_frames * This->fmt->nBlockAlign;
1456 if(This->session->mute)
1457 silence_buffer(This, buf, to_write_frames);
1459 written_bytes = write(This->fd, buf, to_write_bytes);
1460 if(written_bytes < 0){
1461 /* EAGAIN is OSS buffer full, log that too */
1462 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1463 return;
1465 written_frames = written_bytes / This->fmt->nBlockAlign;
1467 This->in_oss_frames += written_frames;
1469 if(written_frames < to_write_frames){
1470 /* OSS buffer probably full */
1471 return;
1474 if(new_frames > written_frames && written_frames < write_limit){
1475 /* wrapped and have some data back at the start to write */
1477 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
1478 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1480 if(This->session->mute)
1481 silence_buffer(This, This->local_buffer, to_write_frames);
1483 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1484 if(written_bytes < 0){
1485 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1486 return;
1488 written_frames = written_bytes / This->fmt->nBlockAlign;
1489 This->in_oss_frames += written_frames;
1493 static void oss_read_data(ACImpl *This)
1495 UINT64 pos, readable;
1496 ssize_t nread;
1498 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1499 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1501 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1502 readable);
1503 if(nread < 0){
1504 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1505 return;
1508 This->held_frames += nread / This->fmt->nBlockAlign;
1510 if(This->held_frames > This->bufsize_frames){
1511 WARN("Overflow of unread data\n");
1512 This->lcl_offs_frames += This->held_frames;
1513 This->lcl_offs_frames %= This->bufsize_frames;
1514 This->held_frames = This->bufsize_frames;
1518 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1520 ACImpl *This = user;
1522 EnterCriticalSection(&This->lock);
1524 if(This->playing){
1525 if(This->dataflow == eRender && This->held_frames)
1526 oss_write_data(This);
1527 else if(This->dataflow == eCapture)
1528 oss_read_data(This);
1531 LeaveCriticalSection(&This->lock);
1533 if(This->event)
1534 SetEvent(This->event);
1537 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1539 ACImpl *This = impl_from_IAudioClient(iface);
1541 TRACE("(%p)\n", This);
1543 EnterCriticalSection(&This->lock);
1545 if(!This->initted){
1546 LeaveCriticalSection(&This->lock);
1547 return AUDCLNT_E_NOT_INITIALIZED;
1550 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1551 LeaveCriticalSection(&This->lock);
1552 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1555 if(This->playing){
1556 LeaveCriticalSection(&This->lock);
1557 return AUDCLNT_E_NOT_STOPPED;
1560 if(!This->timer){
1561 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1562 oss_period_callback, This, 0, This->period_us / 1000,
1563 WT_EXECUTEINTIMERTHREAD))
1564 ERR("Unable to create period timer: %u\n", GetLastError());
1567 This->playing = TRUE;
1569 LeaveCriticalSection(&This->lock);
1571 return S_OK;
1574 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1576 ACImpl *This = impl_from_IAudioClient(iface);
1578 TRACE("(%p)\n", This);
1580 EnterCriticalSection(&This->lock);
1582 if(!This->initted){
1583 LeaveCriticalSection(&This->lock);
1584 return AUDCLNT_E_NOT_INITIALIZED;
1587 if(!This->playing){
1588 LeaveCriticalSection(&This->lock);
1589 return S_FALSE;
1592 This->playing = FALSE;
1593 This->in_oss_frames = 0;
1595 LeaveCriticalSection(&This->lock);
1597 return S_OK;
1600 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1602 ACImpl *This = impl_from_IAudioClient(iface);
1604 TRACE("(%p)\n", This);
1606 EnterCriticalSection(&This->lock);
1608 if(!This->initted){
1609 LeaveCriticalSection(&This->lock);
1610 return AUDCLNT_E_NOT_INITIALIZED;
1613 if(This->playing){
1614 LeaveCriticalSection(&This->lock);
1615 return AUDCLNT_E_NOT_STOPPED;
1618 if(This->getbuf_last){
1619 LeaveCriticalSection(&This->lock);
1620 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1623 if(This->dataflow == eRender){
1624 This->written_frames = 0;
1625 This->last_pos_frames = 0;
1626 }else{
1627 This->written_frames += This->held_frames;
1629 This->held_frames = 0;
1630 This->lcl_offs_frames = 0;
1631 This->in_oss_frames = 0;
1633 LeaveCriticalSection(&This->lock);
1635 return S_OK;
1638 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1639 HANDLE event)
1641 ACImpl *This = impl_from_IAudioClient(iface);
1643 TRACE("(%p)->(%p)\n", This, event);
1645 if(!event)
1646 return E_INVALIDARG;
1648 EnterCriticalSection(&This->lock);
1650 if(!This->initted){
1651 LeaveCriticalSection(&This->lock);
1652 return AUDCLNT_E_NOT_INITIALIZED;
1655 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1656 LeaveCriticalSection(&This->lock);
1657 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1660 if (This->event){
1661 LeaveCriticalSection(&This->lock);
1662 FIXME("called twice\n");
1663 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
1666 This->event = event;
1668 LeaveCriticalSection(&This->lock);
1670 return S_OK;
1673 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1674 void **ppv)
1676 ACImpl *This = impl_from_IAudioClient(iface);
1678 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1680 if(!ppv)
1681 return E_POINTER;
1682 *ppv = NULL;
1684 EnterCriticalSection(&This->lock);
1686 if(!This->initted){
1687 LeaveCriticalSection(&This->lock);
1688 return AUDCLNT_E_NOT_INITIALIZED;
1691 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1692 if(This->dataflow != eRender){
1693 LeaveCriticalSection(&This->lock);
1694 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1696 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1697 *ppv = &This->IAudioRenderClient_iface;
1698 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1699 if(This->dataflow != eCapture){
1700 LeaveCriticalSection(&This->lock);
1701 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1703 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1704 *ppv = &This->IAudioCaptureClient_iface;
1705 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1706 IAudioClock_AddRef(&This->IAudioClock_iface);
1707 *ppv = &This->IAudioClock_iface;
1708 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1709 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1710 *ppv = &This->IAudioStreamVolume_iface;
1711 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1712 if(!This->session_wrapper){
1713 This->session_wrapper = AudioSessionWrapper_Create(This);
1714 if(!This->session_wrapper){
1715 LeaveCriticalSection(&This->lock);
1716 return E_OUTOFMEMORY;
1718 }else
1719 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1721 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1722 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1723 if(!This->session_wrapper){
1724 This->session_wrapper = AudioSessionWrapper_Create(This);
1725 if(!This->session_wrapper){
1726 LeaveCriticalSection(&This->lock);
1727 return E_OUTOFMEMORY;
1729 }else
1730 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1732 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1733 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1734 if(!This->session_wrapper){
1735 This->session_wrapper = AudioSessionWrapper_Create(This);
1736 if(!This->session_wrapper){
1737 LeaveCriticalSection(&This->lock);
1738 return E_OUTOFMEMORY;
1740 }else
1741 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1743 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1746 if(*ppv){
1747 LeaveCriticalSection(&This->lock);
1748 return S_OK;
1751 LeaveCriticalSection(&This->lock);
1753 FIXME("stub %s\n", debugstr_guid(riid));
1754 return E_NOINTERFACE;
1757 static const IAudioClientVtbl AudioClient_Vtbl =
1759 AudioClient_QueryInterface,
1760 AudioClient_AddRef,
1761 AudioClient_Release,
1762 AudioClient_Initialize,
1763 AudioClient_GetBufferSize,
1764 AudioClient_GetStreamLatency,
1765 AudioClient_GetCurrentPadding,
1766 AudioClient_IsFormatSupported,
1767 AudioClient_GetMixFormat,
1768 AudioClient_GetDevicePeriod,
1769 AudioClient_Start,
1770 AudioClient_Stop,
1771 AudioClient_Reset,
1772 AudioClient_SetEventHandle,
1773 AudioClient_GetService
1776 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1777 IAudioRenderClient *iface, REFIID riid, void **ppv)
1779 ACImpl *This = impl_from_IAudioRenderClient(iface);
1780 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1782 if(!ppv)
1783 return E_POINTER;
1784 *ppv = NULL;
1786 if(IsEqualIID(riid, &IID_IUnknown) ||
1787 IsEqualIID(riid, &IID_IAudioRenderClient))
1788 *ppv = iface;
1789 else if(IsEqualIID(riid, &IID_IMarshal))
1790 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1791 if(*ppv){
1792 IUnknown_AddRef((IUnknown*)*ppv);
1793 return S_OK;
1796 WARN("Unknown interface %s\n", debugstr_guid(riid));
1797 return E_NOINTERFACE;
1800 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1802 ACImpl *This = impl_from_IAudioRenderClient(iface);
1803 return AudioClient_AddRef(&This->IAudioClient_iface);
1806 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1808 ACImpl *This = impl_from_IAudioRenderClient(iface);
1809 return AudioClient_Release(&This->IAudioClient_iface);
1812 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1813 UINT32 frames, BYTE **data)
1815 ACImpl *This = impl_from_IAudioRenderClient(iface);
1816 UINT32 write_pos;
1818 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1820 if(!data)
1821 return E_POINTER;
1823 *data = NULL;
1825 EnterCriticalSection(&This->lock);
1827 if(This->getbuf_last){
1828 LeaveCriticalSection(&This->lock);
1829 return AUDCLNT_E_OUT_OF_ORDER;
1832 if(!frames){
1833 LeaveCriticalSection(&This->lock);
1834 return S_OK;
1837 if(This->held_frames + frames > This->bufsize_frames){
1838 LeaveCriticalSection(&This->lock);
1839 return AUDCLNT_E_BUFFER_TOO_LARGE;
1842 write_pos =
1843 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1844 if(write_pos + frames > This->bufsize_frames){
1845 if(This->tmp_buffer_frames < frames){
1846 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1847 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1848 frames * This->fmt->nBlockAlign);
1849 if(!This->tmp_buffer){
1850 LeaveCriticalSection(&This->lock);
1851 return E_OUTOFMEMORY;
1853 This->tmp_buffer_frames = frames;
1855 *data = This->tmp_buffer;
1856 This->getbuf_last = -frames;
1857 }else{
1858 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1859 This->getbuf_last = frames;
1862 silence_buffer(This, *data, frames);
1864 LeaveCriticalSection(&This->lock);
1866 return S_OK;
1869 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1871 UINT32 write_offs_frames =
1872 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1873 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1874 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1875 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1876 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1878 if(written_bytes <= chunk_bytes){
1879 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1880 }else{
1881 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1882 memcpy(This->local_buffer, buffer + chunk_bytes,
1883 written_bytes - chunk_bytes);
1887 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1888 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1890 ACImpl *This = impl_from_IAudioRenderClient(iface);
1891 BYTE *buffer;
1893 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1895 EnterCriticalSection(&This->lock);
1897 if(!written_frames){
1898 This->getbuf_last = 0;
1899 LeaveCriticalSection(&This->lock);
1900 return S_OK;
1903 if(!This->getbuf_last){
1904 LeaveCriticalSection(&This->lock);
1905 return AUDCLNT_E_OUT_OF_ORDER;
1908 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
1909 LeaveCriticalSection(&This->lock);
1910 return AUDCLNT_E_INVALID_SIZE;
1913 if(This->getbuf_last >= 0)
1914 buffer = This->local_buffer + This->fmt->nBlockAlign *
1915 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1916 else
1917 buffer = This->tmp_buffer;
1919 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1920 silence_buffer(This, buffer, written_frames);
1922 if(This->getbuf_last < 0)
1923 oss_wrap_buffer(This, buffer, written_frames);
1925 This->held_frames += written_frames;
1926 This->written_frames += written_frames;
1927 This->getbuf_last = 0;
1929 LeaveCriticalSection(&This->lock);
1931 return S_OK;
1934 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1935 AudioRenderClient_QueryInterface,
1936 AudioRenderClient_AddRef,
1937 AudioRenderClient_Release,
1938 AudioRenderClient_GetBuffer,
1939 AudioRenderClient_ReleaseBuffer
1942 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1943 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1945 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1946 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1948 if(!ppv)
1949 return E_POINTER;
1950 *ppv = NULL;
1952 if(IsEqualIID(riid, &IID_IUnknown) ||
1953 IsEqualIID(riid, &IID_IAudioCaptureClient))
1954 *ppv = iface;
1955 else if(IsEqualIID(riid, &IID_IMarshal))
1956 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
1957 if(*ppv){
1958 IUnknown_AddRef((IUnknown*)*ppv);
1959 return S_OK;
1962 WARN("Unknown interface %s\n", debugstr_guid(riid));
1963 return E_NOINTERFACE;
1966 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1968 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1969 return IAudioClient_AddRef(&This->IAudioClient_iface);
1972 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1974 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1975 return IAudioClient_Release(&This->IAudioClient_iface);
1978 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1979 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1980 UINT64 *qpcpos)
1982 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1984 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1985 devpos, qpcpos);
1987 if(!data || !frames || !flags)
1988 return E_POINTER;
1990 EnterCriticalSection(&This->lock);
1992 if(This->getbuf_last){
1993 LeaveCriticalSection(&This->lock);
1994 return AUDCLNT_E_OUT_OF_ORDER;
1997 if(This->held_frames < This->period_frames){
1998 *frames = 0;
1999 LeaveCriticalSection(&This->lock);
2000 return AUDCLNT_S_BUFFER_EMPTY;
2003 *flags = 0;
2005 *frames = This->period_frames;
2007 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2008 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2009 if(This->tmp_buffer_frames < *frames){
2010 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2011 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2012 *frames * This->fmt->nBlockAlign);
2013 if(!This->tmp_buffer){
2014 LeaveCriticalSection(&This->lock);
2015 return E_OUTOFMEMORY;
2017 This->tmp_buffer_frames = *frames;
2020 *data = This->tmp_buffer;
2021 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2022 This->fmt->nBlockAlign;
2023 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2024 frames_bytes = *frames * This->fmt->nBlockAlign;
2025 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2026 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2027 frames_bytes - chunk_bytes);
2028 }else
2029 *data = This->local_buffer +
2030 This->lcl_offs_frames * This->fmt->nBlockAlign;
2032 This->getbuf_last = *frames;
2034 if(devpos)
2035 *devpos = This->written_frames;
2036 if(qpcpos){
2037 LARGE_INTEGER stamp, freq;
2038 QueryPerformanceCounter(&stamp);
2039 QueryPerformanceFrequency(&freq);
2040 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2043 LeaveCriticalSection(&This->lock);
2045 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2048 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2049 IAudioCaptureClient *iface, UINT32 done)
2051 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2053 TRACE("(%p)->(%u)\n", This, done);
2055 EnterCriticalSection(&This->lock);
2057 if(!done){
2058 This->getbuf_last = 0;
2059 LeaveCriticalSection(&This->lock);
2060 return S_OK;
2063 if(!This->getbuf_last){
2064 LeaveCriticalSection(&This->lock);
2065 return AUDCLNT_E_OUT_OF_ORDER;
2068 if(This->getbuf_last != done){
2069 LeaveCriticalSection(&This->lock);
2070 return AUDCLNT_E_INVALID_SIZE;
2073 This->written_frames += done;
2074 This->held_frames -= done;
2075 This->lcl_offs_frames += done;
2076 This->lcl_offs_frames %= This->bufsize_frames;
2077 This->getbuf_last = 0;
2079 LeaveCriticalSection(&This->lock);
2081 return S_OK;
2084 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2085 IAudioCaptureClient *iface, UINT32 *frames)
2087 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2089 TRACE("(%p)->(%p)\n", This, frames);
2091 if(!frames)
2092 return E_POINTER;
2094 EnterCriticalSection(&This->lock);
2096 *frames = This->held_frames < This->period_frames ? 0 : This->period_frames;
2098 LeaveCriticalSection(&This->lock);
2100 return S_OK;
2103 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2105 AudioCaptureClient_QueryInterface,
2106 AudioCaptureClient_AddRef,
2107 AudioCaptureClient_Release,
2108 AudioCaptureClient_GetBuffer,
2109 AudioCaptureClient_ReleaseBuffer,
2110 AudioCaptureClient_GetNextPacketSize
2113 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2114 REFIID riid, void **ppv)
2116 ACImpl *This = impl_from_IAudioClock(iface);
2118 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2120 if(!ppv)
2121 return E_POINTER;
2122 *ppv = NULL;
2124 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2125 *ppv = iface;
2126 else if(IsEqualIID(riid, &IID_IAudioClock2))
2127 *ppv = &This->IAudioClock2_iface;
2128 if(*ppv){
2129 IUnknown_AddRef((IUnknown*)*ppv);
2130 return S_OK;
2133 WARN("Unknown interface %s\n", debugstr_guid(riid));
2134 return E_NOINTERFACE;
2137 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2139 ACImpl *This = impl_from_IAudioClock(iface);
2140 return IAudioClient_AddRef(&This->IAudioClient_iface);
2143 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2145 ACImpl *This = impl_from_IAudioClock(iface);
2146 return IAudioClient_Release(&This->IAudioClient_iface);
2149 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2151 ACImpl *This = impl_from_IAudioClock(iface);
2153 TRACE("(%p)->(%p)\n", This, freq);
2155 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2156 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2157 else
2158 *freq = This->fmt->nSamplesPerSec;
2160 return S_OK;
2163 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2164 UINT64 *qpctime)
2166 ACImpl *This = impl_from_IAudioClock(iface);
2168 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2170 if(!pos)
2171 return E_POINTER;
2173 EnterCriticalSection(&This->lock);
2175 if(This->dataflow == eRender){
2176 *pos = This->written_frames - This->held_frames;
2177 if(*pos < This->last_pos_frames)
2178 *pos = This->last_pos_frames;
2179 }else if(This->dataflow == eCapture){
2180 audio_buf_info bi;
2181 UINT32 held;
2183 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
2184 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
2185 held = 0;
2186 }else{
2187 if(bi.bytes <= bi.fragsize)
2188 held = 0;
2189 else
2190 held = bi.bytes / This->fmt->nBlockAlign;
2193 *pos = This->written_frames + held;
2196 This->last_pos_frames = *pos;
2198 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
2199 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2200 *pos *= This->fmt->nBlockAlign;
2202 LeaveCriticalSection(&This->lock);
2204 if(qpctime){
2205 LARGE_INTEGER stamp, freq;
2206 QueryPerformanceCounter(&stamp);
2207 QueryPerformanceFrequency(&freq);
2208 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2211 return S_OK;
2214 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2215 DWORD *chars)
2217 ACImpl *This = impl_from_IAudioClock(iface);
2219 TRACE("(%p)->(%p)\n", This, chars);
2221 if(!chars)
2222 return E_POINTER;
2224 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2226 return S_OK;
2229 static const IAudioClockVtbl AudioClock_Vtbl =
2231 AudioClock_QueryInterface,
2232 AudioClock_AddRef,
2233 AudioClock_Release,
2234 AudioClock_GetFrequency,
2235 AudioClock_GetPosition,
2236 AudioClock_GetCharacteristics
2239 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2240 REFIID riid, void **ppv)
2242 ACImpl *This = impl_from_IAudioClock2(iface);
2243 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2246 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2248 ACImpl *This = impl_from_IAudioClock2(iface);
2249 return IAudioClient_AddRef(&This->IAudioClient_iface);
2252 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2254 ACImpl *This = impl_from_IAudioClock2(iface);
2255 return IAudioClient_Release(&This->IAudioClient_iface);
2258 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2259 UINT64 *pos, UINT64 *qpctime)
2261 ACImpl *This = impl_from_IAudioClock2(iface);
2263 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2265 return E_NOTIMPL;
2268 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2270 AudioClock2_QueryInterface,
2271 AudioClock2_AddRef,
2272 AudioClock2_Release,
2273 AudioClock2_GetDevicePosition
2276 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2278 AudioSessionWrapper *ret;
2280 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2281 sizeof(AudioSessionWrapper));
2282 if(!ret)
2283 return NULL;
2285 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2286 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2287 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2289 ret->ref = 1;
2291 ret->client = client;
2292 if(client){
2293 ret->session = client->session;
2294 AudioClient_AddRef(&client->IAudioClient_iface);
2297 return ret;
2300 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2301 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2303 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2305 if(!ppv)
2306 return E_POINTER;
2307 *ppv = NULL;
2309 if(IsEqualIID(riid, &IID_IUnknown) ||
2310 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2311 IsEqualIID(riid, &IID_IAudioSessionControl2))
2312 *ppv = iface;
2313 if(*ppv){
2314 IUnknown_AddRef((IUnknown*)*ppv);
2315 return S_OK;
2318 WARN("Unknown interface %s\n", debugstr_guid(riid));
2319 return E_NOINTERFACE;
2322 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2324 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2325 ULONG ref;
2326 ref = InterlockedIncrement(&This->ref);
2327 TRACE("(%p) Refcount now %u\n", This, ref);
2328 return ref;
2331 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2333 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2334 ULONG ref;
2335 ref = InterlockedDecrement(&This->ref);
2336 TRACE("(%p) Refcount now %u\n", This, ref);
2337 if(!ref){
2338 if(This->client){
2339 EnterCriticalSection(&This->client->lock);
2340 This->client->session_wrapper = NULL;
2341 LeaveCriticalSection(&This->client->lock);
2342 AudioClient_Release(&This->client->IAudioClient_iface);
2344 HeapFree(GetProcessHeap(), 0, This);
2346 return ref;
2349 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2350 AudioSessionState *state)
2352 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2353 ACImpl *client;
2355 TRACE("(%p)->(%p)\n", This, state);
2357 if(!state)
2358 return NULL_PTR_ERR;
2360 EnterCriticalSection(&g_sessions_lock);
2362 if(list_empty(&This->session->clients)){
2363 *state = AudioSessionStateExpired;
2364 LeaveCriticalSection(&g_sessions_lock);
2365 return S_OK;
2368 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2369 EnterCriticalSection(&client->lock);
2370 if(client->playing){
2371 *state = AudioSessionStateActive;
2372 LeaveCriticalSection(&client->lock);
2373 LeaveCriticalSection(&g_sessions_lock);
2374 return S_OK;
2376 LeaveCriticalSection(&client->lock);
2379 LeaveCriticalSection(&g_sessions_lock);
2381 *state = AudioSessionStateInactive;
2383 return S_OK;
2386 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2387 IAudioSessionControl2 *iface, WCHAR **name)
2389 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2391 FIXME("(%p)->(%p) - stub\n", This, name);
2393 return E_NOTIMPL;
2396 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2397 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2399 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2401 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2403 return E_NOTIMPL;
2406 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2407 IAudioSessionControl2 *iface, WCHAR **path)
2409 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2411 FIXME("(%p)->(%p) - stub\n", This, path);
2413 return E_NOTIMPL;
2416 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2417 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2419 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2421 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2423 return E_NOTIMPL;
2426 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2427 IAudioSessionControl2 *iface, GUID *group)
2429 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2431 FIXME("(%p)->(%p) - stub\n", This, group);
2433 return E_NOTIMPL;
2436 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2437 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2439 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2441 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2442 debugstr_guid(session));
2444 return E_NOTIMPL;
2447 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2448 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2450 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2452 FIXME("(%p)->(%p) - stub\n", This, events);
2454 return S_OK;
2457 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2458 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2460 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2462 FIXME("(%p)->(%p) - stub\n", This, events);
2464 return S_OK;
2467 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2468 IAudioSessionControl2 *iface, WCHAR **id)
2470 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2472 FIXME("(%p)->(%p) - stub\n", This, id);
2474 return E_NOTIMPL;
2477 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2478 IAudioSessionControl2 *iface, WCHAR **id)
2480 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2482 FIXME("(%p)->(%p) - stub\n", This, id);
2484 return E_NOTIMPL;
2487 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2488 IAudioSessionControl2 *iface, DWORD *pid)
2490 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2492 TRACE("(%p)->(%p)\n", This, pid);
2494 if(!pid)
2495 return E_POINTER;
2497 *pid = GetCurrentProcessId();
2499 return S_OK;
2502 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2503 IAudioSessionControl2 *iface)
2505 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2507 TRACE("(%p)\n", This);
2509 return S_FALSE;
2512 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2513 IAudioSessionControl2 *iface, BOOL optout)
2515 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2517 TRACE("(%p)->(%d)\n", This, optout);
2519 return S_OK;
2522 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2524 AudioSessionControl_QueryInterface,
2525 AudioSessionControl_AddRef,
2526 AudioSessionControl_Release,
2527 AudioSessionControl_GetState,
2528 AudioSessionControl_GetDisplayName,
2529 AudioSessionControl_SetDisplayName,
2530 AudioSessionControl_GetIconPath,
2531 AudioSessionControl_SetIconPath,
2532 AudioSessionControl_GetGroupingParam,
2533 AudioSessionControl_SetGroupingParam,
2534 AudioSessionControl_RegisterAudioSessionNotification,
2535 AudioSessionControl_UnregisterAudioSessionNotification,
2536 AudioSessionControl_GetSessionIdentifier,
2537 AudioSessionControl_GetSessionInstanceIdentifier,
2538 AudioSessionControl_GetProcessId,
2539 AudioSessionControl_IsSystemSoundsSession,
2540 AudioSessionControl_SetDuckingPreference
2543 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2544 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2546 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2548 if(!ppv)
2549 return E_POINTER;
2550 *ppv = NULL;
2552 if(IsEqualIID(riid, &IID_IUnknown) ||
2553 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2554 *ppv = iface;
2555 if(*ppv){
2556 IUnknown_AddRef((IUnknown*)*ppv);
2557 return S_OK;
2560 WARN("Unknown interface %s\n", debugstr_guid(riid));
2561 return E_NOINTERFACE;
2564 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2566 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2567 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2570 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2572 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2573 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2576 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2577 ISimpleAudioVolume *iface, float level, const GUID *context)
2579 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2580 AudioSession *session = This->session;
2582 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2584 if(level < 0.f || level > 1.f)
2585 return E_INVALIDARG;
2587 if(context)
2588 FIXME("Notifications not supported yet\n");
2590 EnterCriticalSection(&session->lock);
2592 session->master_vol = level;
2594 TRACE("OSS doesn't support setting volume\n");
2596 LeaveCriticalSection(&session->lock);
2598 return S_OK;
2601 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2602 ISimpleAudioVolume *iface, float *level)
2604 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2605 AudioSession *session = This->session;
2607 TRACE("(%p)->(%p)\n", session, level);
2609 if(!level)
2610 return NULL_PTR_ERR;
2612 *level = session->master_vol;
2614 return S_OK;
2617 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2618 BOOL mute, const GUID *context)
2620 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2621 AudioSession *session = This->session;
2623 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2625 EnterCriticalSection(&session->lock);
2627 session->mute = mute;
2629 LeaveCriticalSection(&session->lock);
2631 return S_OK;
2634 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2635 BOOL *mute)
2637 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2638 AudioSession *session = This->session;
2640 TRACE("(%p)->(%p)\n", session, mute);
2642 if(!mute)
2643 return NULL_PTR_ERR;
2645 *mute = This->session->mute;
2647 return S_OK;
2650 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2652 SimpleAudioVolume_QueryInterface,
2653 SimpleAudioVolume_AddRef,
2654 SimpleAudioVolume_Release,
2655 SimpleAudioVolume_SetMasterVolume,
2656 SimpleAudioVolume_GetMasterVolume,
2657 SimpleAudioVolume_SetMute,
2658 SimpleAudioVolume_GetMute
2661 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2662 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2664 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2666 if(!ppv)
2667 return E_POINTER;
2668 *ppv = NULL;
2670 if(IsEqualIID(riid, &IID_IUnknown) ||
2671 IsEqualIID(riid, &IID_IAudioStreamVolume))
2672 *ppv = iface;
2673 if(*ppv){
2674 IUnknown_AddRef((IUnknown*)*ppv);
2675 return S_OK;
2678 WARN("Unknown interface %s\n", debugstr_guid(riid));
2679 return E_NOINTERFACE;
2682 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2684 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2685 return IAudioClient_AddRef(&This->IAudioClient_iface);
2688 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2690 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2691 return IAudioClient_Release(&This->IAudioClient_iface);
2694 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2695 IAudioStreamVolume *iface, UINT32 *out)
2697 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2699 TRACE("(%p)->(%p)\n", This, out);
2701 if(!out)
2702 return E_POINTER;
2704 *out = This->fmt->nChannels;
2706 return S_OK;
2709 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2710 IAudioStreamVolume *iface, UINT32 index, float level)
2712 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2714 TRACE("(%p)->(%d, %f)\n", This, index, level);
2716 if(level < 0.f || level > 1.f)
2717 return E_INVALIDARG;
2719 if(index >= This->fmt->nChannels)
2720 return E_INVALIDARG;
2722 EnterCriticalSection(&This->lock);
2724 This->vols[index] = level;
2726 TRACE("OSS doesn't support setting volume\n");
2728 LeaveCriticalSection(&This->lock);
2730 return S_OK;
2733 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2734 IAudioStreamVolume *iface, UINT32 index, float *level)
2736 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2738 TRACE("(%p)->(%d, %p)\n", This, index, level);
2740 if(!level)
2741 return E_POINTER;
2743 if(index >= This->fmt->nChannels)
2744 return E_INVALIDARG;
2746 *level = This->vols[index];
2748 return S_OK;
2751 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2752 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2754 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2755 int i;
2757 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2759 if(!levels)
2760 return E_POINTER;
2762 if(count != This->fmt->nChannels)
2763 return E_INVALIDARG;
2765 EnterCriticalSection(&This->lock);
2767 for(i = 0; i < count; ++i)
2768 This->vols[i] = levels[i];
2770 TRACE("OSS doesn't support setting volume\n");
2772 LeaveCriticalSection(&This->lock);
2774 return S_OK;
2777 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2778 IAudioStreamVolume *iface, UINT32 count, float *levels)
2780 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2781 int i;
2783 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2785 if(!levels)
2786 return E_POINTER;
2788 if(count != This->fmt->nChannels)
2789 return E_INVALIDARG;
2791 EnterCriticalSection(&This->lock);
2793 for(i = 0; i < count; ++i)
2794 levels[i] = This->vols[i];
2796 LeaveCriticalSection(&This->lock);
2798 return S_OK;
2801 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2803 AudioStreamVolume_QueryInterface,
2804 AudioStreamVolume_AddRef,
2805 AudioStreamVolume_Release,
2806 AudioStreamVolume_GetChannelCount,
2807 AudioStreamVolume_SetChannelVolume,
2808 AudioStreamVolume_GetChannelVolume,
2809 AudioStreamVolume_SetAllVolumes,
2810 AudioStreamVolume_GetAllVolumes
2813 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2814 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2816 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2818 if(!ppv)
2819 return E_POINTER;
2820 *ppv = NULL;
2822 if(IsEqualIID(riid, &IID_IUnknown) ||
2823 IsEqualIID(riid, &IID_IChannelAudioVolume))
2824 *ppv = iface;
2825 if(*ppv){
2826 IUnknown_AddRef((IUnknown*)*ppv);
2827 return S_OK;
2830 WARN("Unknown interface %s\n", debugstr_guid(riid));
2831 return E_NOINTERFACE;
2834 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2836 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2837 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2840 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2842 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2843 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2846 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2847 IChannelAudioVolume *iface, UINT32 *out)
2849 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2850 AudioSession *session = This->session;
2852 TRACE("(%p)->(%p)\n", session, out);
2854 if(!out)
2855 return NULL_PTR_ERR;
2857 *out = session->channel_count;
2859 return S_OK;
2862 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2863 IChannelAudioVolume *iface, UINT32 index, float level,
2864 const GUID *context)
2866 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2867 AudioSession *session = This->session;
2869 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2870 wine_dbgstr_guid(context));
2872 if(level < 0.f || level > 1.f)
2873 return E_INVALIDARG;
2875 if(index >= session->channel_count)
2876 return E_INVALIDARG;
2878 if(context)
2879 FIXME("Notifications not supported yet\n");
2881 EnterCriticalSection(&session->lock);
2883 session->channel_vols[index] = level;
2885 TRACE("OSS doesn't support setting volume\n");
2887 LeaveCriticalSection(&session->lock);
2889 return S_OK;
2892 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2893 IChannelAudioVolume *iface, UINT32 index, float *level)
2895 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2896 AudioSession *session = This->session;
2898 TRACE("(%p)->(%d, %p)\n", session, index, level);
2900 if(!level)
2901 return NULL_PTR_ERR;
2903 if(index >= session->channel_count)
2904 return E_INVALIDARG;
2906 *level = session->channel_vols[index];
2908 return S_OK;
2911 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2912 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2913 const GUID *context)
2915 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2916 AudioSession *session = This->session;
2917 int i;
2919 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2920 wine_dbgstr_guid(context));
2922 if(!levels)
2923 return NULL_PTR_ERR;
2925 if(count != session->channel_count)
2926 return E_INVALIDARG;
2928 if(context)
2929 FIXME("Notifications not supported yet\n");
2931 EnterCriticalSection(&session->lock);
2933 for(i = 0; i < count; ++i)
2934 session->channel_vols[i] = levels[i];
2936 TRACE("OSS doesn't support setting volume\n");
2938 LeaveCriticalSection(&session->lock);
2940 return S_OK;
2943 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2944 IChannelAudioVolume *iface, UINT32 count, float *levels)
2946 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2947 AudioSession *session = This->session;
2948 int i;
2950 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2952 if(!levels)
2953 return NULL_PTR_ERR;
2955 if(count != session->channel_count)
2956 return E_INVALIDARG;
2958 for(i = 0; i < count; ++i)
2959 levels[i] = session->channel_vols[i];
2961 return S_OK;
2964 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2966 ChannelAudioVolume_QueryInterface,
2967 ChannelAudioVolume_AddRef,
2968 ChannelAudioVolume_Release,
2969 ChannelAudioVolume_GetChannelCount,
2970 ChannelAudioVolume_SetChannelVolume,
2971 ChannelAudioVolume_GetChannelVolume,
2972 ChannelAudioVolume_SetAllVolumes,
2973 ChannelAudioVolume_GetAllVolumes
2976 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2977 REFIID riid, void **ppv)
2979 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2981 if(!ppv)
2982 return E_POINTER;
2983 *ppv = NULL;
2985 if(IsEqualIID(riid, &IID_IUnknown) ||
2986 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2987 IsEqualIID(riid, &IID_IAudioSessionManager2))
2988 *ppv = iface;
2989 if(*ppv){
2990 IUnknown_AddRef((IUnknown*)*ppv);
2991 return S_OK;
2994 WARN("Unknown interface %s\n", debugstr_guid(riid));
2995 return E_NOINTERFACE;
2998 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3000 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3001 ULONG ref;
3002 ref = InterlockedIncrement(&This->ref);
3003 TRACE("(%p) Refcount now %u\n", This, ref);
3004 return ref;
3007 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3009 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3010 ULONG ref;
3011 ref = InterlockedDecrement(&This->ref);
3012 TRACE("(%p) Refcount now %u\n", This, ref);
3013 if(!ref)
3014 HeapFree(GetProcessHeap(), 0, This);
3015 return ref;
3018 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3019 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3020 IAudioSessionControl **out)
3022 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3023 AudioSession *session;
3024 AudioSessionWrapper *wrapper;
3025 HRESULT hr;
3027 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3028 flags, out);
3030 hr = get_audio_session(session_guid, This->device, 0, &session);
3031 if(FAILED(hr))
3032 return hr;
3034 wrapper = AudioSessionWrapper_Create(NULL);
3035 if(!wrapper)
3036 return E_OUTOFMEMORY;
3038 wrapper->session = session;
3040 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3042 return S_OK;
3045 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3046 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3047 ISimpleAudioVolume **out)
3049 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3050 AudioSession *session;
3051 AudioSessionWrapper *wrapper;
3052 HRESULT hr;
3054 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3055 flags, out);
3057 hr = get_audio_session(session_guid, This->device, 0, &session);
3058 if(FAILED(hr))
3059 return hr;
3061 wrapper = AudioSessionWrapper_Create(NULL);
3062 if(!wrapper)
3063 return E_OUTOFMEMORY;
3065 wrapper->session = session;
3067 *out = &wrapper->ISimpleAudioVolume_iface;
3069 return S_OK;
3072 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3073 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3075 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3076 FIXME("(%p)->(%p) - stub\n", This, out);
3077 return E_NOTIMPL;
3080 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3081 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3083 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3084 FIXME("(%p)->(%p) - stub\n", This, notification);
3085 return E_NOTIMPL;
3088 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3089 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3091 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3092 FIXME("(%p)->(%p) - stub\n", This, notification);
3093 return E_NOTIMPL;
3096 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3097 IAudioSessionManager2 *iface, const WCHAR *session_id,
3098 IAudioVolumeDuckNotification *notification)
3100 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3101 FIXME("(%p)->(%p) - stub\n", This, notification);
3102 return E_NOTIMPL;
3105 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3106 IAudioSessionManager2 *iface,
3107 IAudioVolumeDuckNotification *notification)
3109 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3110 FIXME("(%p)->(%p) - stub\n", This, notification);
3111 return E_NOTIMPL;
3114 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3116 AudioSessionManager_QueryInterface,
3117 AudioSessionManager_AddRef,
3118 AudioSessionManager_Release,
3119 AudioSessionManager_GetAudioSessionControl,
3120 AudioSessionManager_GetSimpleAudioVolume,
3121 AudioSessionManager_GetSessionEnumerator,
3122 AudioSessionManager_RegisterSessionNotification,
3123 AudioSessionManager_UnregisterSessionNotification,
3124 AudioSessionManager_RegisterDuckNotification,
3125 AudioSessionManager_UnregisterDuckNotification
3128 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3129 IAudioSessionManager2 **out)
3131 SessionMgr *This;
3133 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3134 if(!This)
3135 return E_OUTOFMEMORY;
3137 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3138 This->device = device;
3139 This->ref = 1;
3141 *out = &This->IAudioSessionManager2_iface;
3143 return S_OK;