mmdevapi: SHAREDMODE_EXCLUSIVE + EVENTCALLBACK is too ... exclusive for now.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob51ae720a93c68749dd333faba4c5c350766f8da7
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"
57 /* Some implementations of OSS, such as FreeBSD older than 9.0, lack
58 SNDCTL_DSP_HALT which is just a synonym for the older SNDCTL_DSP_RESET. */
59 #ifndef SNDCTL_DSP_HALT
60 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
61 #endif
63 WINE_DEFAULT_DEBUG_CHANNEL(oss);
65 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
67 static const REFERENCE_TIME DefaultPeriod = 200000;
68 static const REFERENCE_TIME MinimumPeriod = 100000;
70 struct ACImpl;
71 typedef struct ACImpl ACImpl;
73 typedef struct _AudioSession {
74 GUID guid;
75 struct list clients;
77 IMMDevice *device;
79 float master_vol;
80 UINT32 channel_count;
81 float *channel_vols;
82 BOOL mute;
84 CRITICAL_SECTION lock;
86 struct list entry;
87 } AudioSession;
89 typedef struct _AudioSessionWrapper {
90 IAudioSessionControl2 IAudioSessionControl2_iface;
91 IChannelAudioVolume IChannelAudioVolume_iface;
92 ISimpleAudioVolume ISimpleAudioVolume_iface;
94 LONG ref;
96 ACImpl *client;
97 AudioSession *session;
98 } AudioSessionWrapper;
100 struct ACImpl {
101 IAudioClient IAudioClient_iface;
102 IAudioRenderClient IAudioRenderClient_iface;
103 IAudioCaptureClient IAudioCaptureClient_iface;
104 IAudioClock IAudioClock_iface;
105 IAudioClock2 IAudioClock2_iface;
106 IAudioStreamVolume IAudioStreamVolume_iface;
108 LONG ref;
110 IMMDevice *parent;
112 WAVEFORMATEX *fmt;
114 EDataFlow dataflow;
115 DWORD flags;
116 AUDCLNT_SHAREMODE share;
117 HANDLE event;
118 float *vols;
120 int fd;
121 oss_audioinfo ai;
122 char devnode[OSS_DEVNODE_SIZE];
124 BOOL initted, playing;
125 UINT64 written_frames;
126 UINT32 period_us, bufsize_frames, held_frames, tmp_buffer_frames, inbuf_frames;
127 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
129 BYTE *local_buffer, *tmp_buffer;
130 int buf_state;
131 HANDLE timer;
133 CRITICAL_SECTION lock;
135 AudioSession *session;
136 AudioSessionWrapper *session_wrapper;
138 struct list entry;
141 enum BufferStates {
142 NOT_LOCKED = 0,
143 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
144 LOCKED_WRAPPED /* public buffer piece is in tmp_buffer */
147 typedef struct _SessionMgr {
148 IAudioSessionManager2 IAudioSessionManager2_iface;
150 LONG ref;
152 IMMDevice *device;
153 } SessionMgr;
155 static HANDLE g_timer_q;
157 static CRITICAL_SECTION g_sessions_lock;
158 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
160 0, 0, &g_sessions_lock,
161 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
162 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
164 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
165 static struct list g_sessions = LIST_INIT(g_sessions);
167 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
168 static HRESULT oss_setvol(ACImpl *This, UINT32 index);
170 static const IAudioClientVtbl AudioClient_Vtbl;
171 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
172 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
173 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
174 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
175 static const IAudioClockVtbl AudioClock_Vtbl;
176 static const IAudioClock2Vtbl AudioClock2_Vtbl;
177 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
178 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
179 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
181 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
183 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
186 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
188 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
191 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
193 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
196 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
198 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
201 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
203 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
206 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
208 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
211 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
213 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
216 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
218 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
221 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
223 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
226 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
228 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
231 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
233 switch (reason)
235 case DLL_PROCESS_ATTACH:
236 g_timer_q = CreateTimerQueue();
237 if(!g_timer_q)
238 return FALSE;
239 break;
241 case DLL_PROCESS_DETACH:
242 DeleteCriticalSection(&g_sessions_lock);
243 break;
245 return TRUE;
248 /* From <dlls/mmdevapi/mmdevapi.h> */
249 enum DriverPriority {
250 Priority_Unavailable = 0,
251 Priority_Low,
252 Priority_Neutral,
253 Priority_Preferred
256 int WINAPI AUDDRV_GetPriority(void)
258 int mixer_fd;
259 oss_sysinfo sysinfo;
261 /* Attempt to determine if we are running on OSS or ALSA's OSS
262 * compatibility layer. There is no official way to do that, so just check
263 * for validity as best as possible, without rejecting valid OSS
264 * implementations. */
266 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
267 if(mixer_fd < 0){
268 TRACE("Priority_Unavailable: open failed\n");
269 return Priority_Unavailable;
272 sysinfo.version[0] = 0xFF;
273 sysinfo.versionnum = ~0;
274 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
275 TRACE("Priority_Unavailable: ioctl failed\n");
276 close(mixer_fd);
277 return Priority_Unavailable;
280 close(mixer_fd);
282 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
283 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
284 return Priority_Low;
286 if(sysinfo.versionnum & 0x80000000){
287 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
288 return Priority_Low;
291 TRACE("Priority_Preferred: Seems like valid OSS!\n");
293 return Priority_Preferred;
296 static const char *oss_clean_devnode(const char *devnode)
298 static char ret[OSS_DEVNODE_SIZE];
300 const char *dot, *slash;
301 size_t len;
303 dot = strrchr(devnode, '.');
304 if(!dot)
305 return devnode;
307 slash = strrchr(devnode, '/');
308 if(slash && dot < slash)
309 return devnode;
311 len = dot - devnode;
313 memcpy(ret, devnode, len);
314 ret[len] = '\0';
316 return ret;
319 static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
321 int fd = -1, err, i;
322 oss_audioinfo ai;
323 const char *devnode;
325 if(flow == eRender)
326 fd = open("/dev/dsp", O_WRONLY);
327 else
328 fd = open("/dev/dsp", O_RDONLY);
330 if(fd < 0){
331 WARN("Couldn't open default device!\n");
332 return 0;
335 ai.dev = -1;
336 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
337 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
338 close(fd);
339 return 0;
342 close(fd);
344 TRACE("Default devnode: %s\n", ai.devnode);
345 devnode = oss_clean_devnode(ai.devnode);
346 for(i = 0; i < num; ++i)
347 if(!strcmp(devnode, keys[i]))
348 return i;
350 WARN("Couldn't find default device! Choosing first.\n");
351 return 0;
354 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
355 UINT *num, UINT *def_index)
357 int i, j, mixer_fd;
358 oss_sysinfo sysinfo;
359 static int print_once = 0;
361 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
363 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
364 if(mixer_fd < 0){
365 ERR("OSS /dev/mixer doesn't seem to exist\n");
366 return AUDCLNT_E_SERVICE_NOT_RUNNING;
369 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
370 close(mixer_fd);
372 if(errno == EINVAL){
373 ERR("OSS version too old, need at least OSSv4\n");
374 return AUDCLNT_E_SERVICE_NOT_RUNNING;
377 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
378 return E_FAIL;
381 if(!print_once){
382 TRACE("OSS sysinfo:\n");
383 TRACE("product: %s\n", sysinfo.product);
384 TRACE("version: %s\n", sysinfo.version);
385 TRACE("versionnum: %x\n", sysinfo.versionnum);
386 TRACE("numaudios: %d\n", sysinfo.numaudios);
387 TRACE("nummixers: %d\n", sysinfo.nummixers);
388 TRACE("numcards: %d\n", sysinfo.numcards);
389 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
390 print_once = 1;
393 if(sysinfo.numaudios <= 0){
394 WARN("No audio devices!\n");
395 close(mixer_fd);
396 return AUDCLNT_E_SERVICE_NOT_RUNNING;
399 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
400 *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
402 *num = 0;
403 for(i = 0; i < sysinfo.numaudios; ++i){
404 oss_audioinfo ai = {0};
405 const char *devnode;
406 int fd;
408 ai.dev = i;
409 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
410 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
411 strerror(errno));
412 continue;
415 devnode = oss_clean_devnode(ai.devnode);
417 /* check for duplicates */
418 for(j = 0; j < *num; ++j)
419 if(!strcmp(devnode, (*keys)[j]))
420 break;
421 if(j != *num)
422 continue;
424 if(flow == eRender)
425 fd = open(devnode, O_WRONLY, 0);
426 else
427 fd = open(devnode, O_RDONLY, 0);
428 if(fd < 0){
429 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
430 devnode, errno, strerror(errno));
431 continue;
433 close(fd);
435 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
436 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
437 size_t len;
439 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
440 strlen(devnode) + 1);
441 if(!(*keys)[*num]){
442 for(i = 0; i < *num; ++i){
443 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
444 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
446 HeapFree(GetProcessHeap(), 0, *ids);
447 HeapFree(GetProcessHeap(), 0, *keys);
448 close(mixer_fd);
449 return E_OUTOFMEMORY;
451 strcpy((*keys)[*num], devnode);
453 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
454 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
455 len * sizeof(WCHAR));
456 if(!(*ids)[*num]){
457 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
458 for(i = 0; i < *num; ++i){
459 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
460 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
462 HeapFree(GetProcessHeap(), 0, *ids);
463 HeapFree(GetProcessHeap(), 0, *keys);
464 close(mixer_fd);
465 return E_OUTOFMEMORY;
467 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
468 (*ids)[*num], len);
470 (*num)++;
474 close(mixer_fd);
476 *def_index = get_default_index(flow, *keys, *num);
478 return S_OK;
481 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
482 EDataFlow dataflow, IAudioClient **out)
484 ACImpl *This;
486 TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
488 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
489 if(!This)
490 return E_OUTOFMEMORY;
492 if(dataflow == eRender)
493 This->fd = open(devnode, O_WRONLY, 0);
494 else if(dataflow == eCapture)
495 This->fd = open(devnode, O_RDONLY, 0);
496 else{
497 HeapFree(GetProcessHeap(), 0, This);
498 return E_INVALIDARG;
500 if(This->fd < 0){
501 ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
502 strerror(errno));
503 HeapFree(GetProcessHeap(), 0, This);
504 return AUDCLNT_E_DEVICE_INVALIDATED;
507 This->dataflow = dataflow;
509 This->ai.dev = -1;
510 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
511 ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
512 errno, strerror(errno));
513 close(This->fd);
514 HeapFree(GetProcessHeap(), 0, This);
515 return E_FAIL;
518 strcpy(This->devnode, devnode);
520 TRACE("OSS audioinfo:\n");
521 TRACE("devnode: %s\n", This->ai.devnode);
522 TRACE("name: %s\n", This->ai.name);
523 TRACE("busy: %x\n", This->ai.busy);
524 TRACE("caps: %x\n", This->ai.caps);
525 TRACE("iformats: %x\n", This->ai.iformats);
526 TRACE("oformats: %x\n", This->ai.oformats);
527 TRACE("enabled: %d\n", This->ai.enabled);
528 TRACE("min_rate: %d\n", This->ai.min_rate);
529 TRACE("max_rate: %d\n", This->ai.max_rate);
530 TRACE("min_channels: %d\n", This->ai.min_channels);
531 TRACE("max_channels: %d\n", This->ai.max_channels);
533 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
534 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
535 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
536 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
537 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
538 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
540 InitializeCriticalSection(&This->lock);
541 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
543 This->parent = dev;
544 IMMDevice_AddRef(This->parent);
546 IAudioClient_AddRef(&This->IAudioClient_iface);
548 *out = &This->IAudioClient_iface;
550 return S_OK;
553 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
554 REFIID riid, void **ppv)
556 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
558 if(!ppv)
559 return E_POINTER;
560 *ppv = NULL;
561 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
562 *ppv = iface;
563 if(*ppv){
564 IUnknown_AddRef((IUnknown*)*ppv);
565 return S_OK;
567 WARN("Unknown interface %s\n", debugstr_guid(riid));
568 return E_NOINTERFACE;
571 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
573 ACImpl *This = impl_from_IAudioClient(iface);
574 ULONG ref;
575 ref = InterlockedIncrement(&This->ref);
576 TRACE("(%p) Refcount now %u\n", This, ref);
577 return ref;
580 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
582 ACImpl *This = impl_from_IAudioClient(iface);
583 ULONG ref;
584 ref = InterlockedDecrement(&This->ref);
585 TRACE("(%p) Refcount now %u\n", This, ref);
586 if(!ref){
587 IAudioClient_Stop(iface);
588 IMMDevice_Release(This->parent);
589 This->lock.DebugInfo->Spare[0] = 0;
590 DeleteCriticalSection(&This->lock);
591 close(This->fd);
592 if(This->initted){
593 EnterCriticalSection(&g_sessions_lock);
594 list_remove(&This->entry);
595 LeaveCriticalSection(&g_sessions_lock);
597 HeapFree(GetProcessHeap(), 0, This->vols);
598 HeapFree(GetProcessHeap(), 0, This->local_buffer);
599 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
600 CoTaskMemFree(This->fmt);
601 HeapFree(GetProcessHeap(), 0, This);
603 return ref;
606 static void dump_fmt(const WAVEFORMATEX *fmt)
608 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
609 switch(fmt->wFormatTag){
610 case WAVE_FORMAT_PCM:
611 TRACE("WAVE_FORMAT_PCM");
612 break;
613 case WAVE_FORMAT_IEEE_FLOAT:
614 TRACE("WAVE_FORMAT_IEEE_FLOAT");
615 break;
616 case WAVE_FORMAT_EXTENSIBLE:
617 TRACE("WAVE_FORMAT_EXTENSIBLE");
618 break;
619 default:
620 TRACE("Unknown");
621 break;
623 TRACE(")\n");
625 TRACE("nChannels: %u\n", fmt->nChannels);
626 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
627 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
628 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
629 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
630 TRACE("cbSize: %u\n", fmt->cbSize);
632 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
633 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
634 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
635 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
636 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
640 static DWORD get_channel_mask(unsigned int channels)
642 switch(channels){
643 case 0:
644 return 0;
645 case 1:
646 return KSAUDIO_SPEAKER_MONO;
647 case 2:
648 return KSAUDIO_SPEAKER_STEREO;
649 case 3:
650 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
651 case 4:
652 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
653 case 5:
654 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
655 case 6:
656 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
657 case 7:
658 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
659 case 8:
660 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
662 FIXME("Unknown speaker configuration: %u\n", channels);
663 return 0;
666 static int get_oss_format(const WAVEFORMATEX *fmt)
668 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
670 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
671 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
672 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
673 switch(fmt->wBitsPerSample){
674 case 8:
675 return AFMT_U8;
676 case 16:
677 return AFMT_S16_LE;
678 case 24:
679 return AFMT_S24_LE;
680 case 32:
681 return AFMT_S32_LE;
683 return -1;
686 #ifdef AFMT_FLOAT
687 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
688 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
689 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
690 if(fmt->wBitsPerSample != 32)
691 return -1;
693 return AFMT_FLOAT;
695 #endif
697 return -1;
700 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
702 WAVEFORMATEX *ret;
703 size_t size;
705 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
706 size = sizeof(WAVEFORMATEXTENSIBLE);
707 else
708 size = sizeof(WAVEFORMATEX);
710 ret = CoTaskMemAlloc(size);
711 if(!ret)
712 return NULL;
714 memcpy(ret, fmt, size);
716 ret->cbSize = size - sizeof(WAVEFORMATEX);
718 return ret;
721 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
722 WAVEFORMATEX **out, BOOL query)
724 int tmp, oss_format;
725 double tenth;
726 HRESULT ret = S_OK;
727 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
728 WAVEFORMATEX *closest = NULL;
730 tmp = oss_format = get_oss_format(fmt);
731 if(oss_format < 0)
732 return AUDCLNT_E_UNSUPPORTED_FORMAT;
733 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
734 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
735 return E_FAIL;
737 if(tmp != oss_format){
738 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
739 return AUDCLNT_E_UNSUPPORTED_FORMAT;
742 closest = clone_format(fmt);
743 if(!closest)
744 return E_OUTOFMEMORY;
746 tmp = fmt->nSamplesPerSec;
747 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
748 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
749 CoTaskMemFree(closest);
750 return E_FAIL;
752 tenth = fmt->nSamplesPerSec * 0.1;
753 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
754 ret = S_FALSE;
755 closest->nSamplesPerSec = tmp;
758 tmp = fmt->nChannels;
759 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
760 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
761 CoTaskMemFree(closest);
762 return E_FAIL;
764 if(tmp != fmt->nChannels){
765 ret = S_FALSE;
766 closest->nChannels = tmp;
769 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
770 DWORD mask = get_channel_mask(closest->nChannels);
772 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
774 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
775 fmtex->dwChannelMask != 0 &&
776 fmtex->dwChannelMask != mask)
777 ret = S_FALSE;
780 if(ret == S_FALSE && !out)
781 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
783 if(ret == S_FALSE && out){
784 closest->nBlockAlign =
785 closest->nChannels * closest->wBitsPerSample / 8;
786 closest->nAvgBytesPerSec =
787 closest->nBlockAlign * closest->nSamplesPerSec;
788 *out = closest;
789 } else
790 CoTaskMemFree(closest);
792 TRACE("returning: %08x\n", ret);
793 return ret;
796 static void session_init_vols(AudioSession *session, UINT channels)
798 if(session->channel_count < channels){
799 UINT i;
801 if(session->channel_vols)
802 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
803 session->channel_vols, sizeof(float) * channels);
804 else
805 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
806 sizeof(float) * channels);
807 if(!session->channel_vols)
808 return;
810 for(i = session->channel_count; i < channels; ++i)
811 session->channel_vols[i] = 1.f;
813 session->channel_count = channels;
817 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
818 UINT num_channels)
820 AudioSession *ret;
822 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
823 if(!ret)
824 return NULL;
826 memcpy(&ret->guid, guid, sizeof(GUID));
828 ret->device = device;
830 list_init(&ret->clients);
832 list_add_head(&g_sessions, &ret->entry);
834 InitializeCriticalSection(&ret->lock);
835 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
837 session_init_vols(ret, num_channels);
839 ret->master_vol = 1.f;
841 return ret;
844 /* if channels == 0, then this will return or create a session with
845 * matching dataflow and GUID. otherwise, channels must also match */
846 static HRESULT get_audio_session(const GUID *sessionguid,
847 IMMDevice *device, UINT channels, AudioSession **out)
849 AudioSession *session;
851 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
852 *out = create_session(&GUID_NULL, device, channels);
853 if(!*out)
854 return E_OUTOFMEMORY;
856 return S_OK;
859 *out = NULL;
860 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
861 if(session->device == device &&
862 IsEqualGUID(sessionguid, &session->guid)){
863 session_init_vols(session, channels);
864 *out = session;
865 break;
869 if(!*out){
870 *out = create_session(sessionguid, device, channels);
871 if(!*out)
872 return E_OUTOFMEMORY;
875 return S_OK;
878 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
879 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
880 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
881 const GUID *sessionguid)
883 ACImpl *This = impl_from_IAudioClient(iface);
884 int mask, i;
885 HRESULT hr;
887 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
888 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
890 if(!fmt)
891 return E_POINTER;
893 dump_fmt(fmt);
895 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
896 return AUDCLNT_E_NOT_INITIALIZED;
898 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
899 AUDCLNT_STREAMFLAGS_LOOPBACK |
900 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
901 AUDCLNT_STREAMFLAGS_NOPERSIST |
902 AUDCLNT_STREAMFLAGS_RATEADJUST |
903 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
904 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
905 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
906 TRACE("Unknown flags: %08x\n", flags);
907 return E_INVALIDARG;
910 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE && flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
911 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
912 return AUDCLNT_E_DEVICE_IN_USE;
915 EnterCriticalSection(&This->lock);
917 if(This->initted){
918 LeaveCriticalSection(&This->lock);
919 return AUDCLNT_E_ALREADY_INITIALIZED;
922 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
923 if(FAILED(hr)){
924 LeaveCriticalSection(&This->lock);
925 return hr;
928 mask = 0;
929 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
930 LeaveCriticalSection(&This->lock);
931 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
932 return E_FAIL;
935 mask = (100 << 8) | 100;
936 if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
937 WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
939 This->fmt = clone_format(fmt);
940 if(!This->fmt){
941 LeaveCriticalSection(&This->lock);
942 return E_OUTOFMEMORY;
945 if(period)
946 This->period_us = period / 10;
947 else
948 This->period_us = DefaultPeriod / 10;
950 if(!duration)
951 duration = 300000; /* 0.03s */
952 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
953 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
954 This->bufsize_frames * fmt->nBlockAlign);
955 if(!This->local_buffer){
956 CoTaskMemFree(This->fmt);
957 This->fmt = NULL;
958 LeaveCriticalSection(&This->lock);
959 return E_OUTOFMEMORY;
962 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
963 if(!This->vols){
964 CoTaskMemFree(This->fmt);
965 This->fmt = NULL;
966 LeaveCriticalSection(&This->lock);
967 return E_OUTOFMEMORY;
970 for(i = 0; i < fmt->nChannels; ++i)
971 This->vols[i] = 1.f;
973 This->share = mode;
974 This->flags = flags;
976 EnterCriticalSection(&g_sessions_lock);
978 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
979 &This->session);
980 if(FAILED(hr)){
981 LeaveCriticalSection(&g_sessions_lock);
982 HeapFree(GetProcessHeap(), 0, This->vols);
983 This->vols = NULL;
984 CoTaskMemFree(This->fmt);
985 This->fmt = NULL;
986 LeaveCriticalSection(&This->lock);
987 return hr;
990 list_add_tail(&This->session->clients, &This->entry);
992 LeaveCriticalSection(&g_sessions_lock);
994 This->initted = TRUE;
996 oss_setvol(This, -1);
998 LeaveCriticalSection(&This->lock);
1000 return S_OK;
1003 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1004 UINT32 *frames)
1006 ACImpl *This = impl_from_IAudioClient(iface);
1008 TRACE("(%p)->(%p)\n", This, frames);
1010 if(!frames)
1011 return E_POINTER;
1013 EnterCriticalSection(&This->lock);
1015 if(!This->initted){
1016 LeaveCriticalSection(&This->lock);
1017 return AUDCLNT_E_NOT_INITIALIZED;
1020 *frames = This->bufsize_frames;
1022 LeaveCriticalSection(&This->lock);
1024 return S_OK;
1027 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1028 REFERENCE_TIME *latency)
1030 ACImpl *This = impl_from_IAudioClient(iface);
1032 TRACE("(%p)->(%p)\n", This, latency);
1034 if(!latency)
1035 return E_POINTER;
1037 EnterCriticalSection(&This->lock);
1039 if(!This->initted){
1040 LeaveCriticalSection(&This->lock);
1041 return AUDCLNT_E_NOT_INITIALIZED;
1044 if(This->dataflow == eRender){
1045 int delay_bytes;
1046 double delay_s;
1048 if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
1049 LeaveCriticalSection(&This->lock);
1050 WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
1051 return E_FAIL;
1054 delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
1055 This->fmt->nBlockAlign);
1057 *latency = delay_s * 10000000;
1058 }else
1059 *latency = 10000; /* OSS doesn't provide input latency */
1061 LeaveCriticalSection(&This->lock);
1063 return S_OK;
1066 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1067 UINT32 *numpad)
1069 ACImpl *This = impl_from_IAudioClient(iface);
1070 audio_buf_info bi;
1072 TRACE("(%p)->(%p)\n", This, numpad);
1074 if(!numpad)
1075 return E_POINTER;
1077 EnterCriticalSection(&This->lock);
1079 if(!This->initted){
1080 LeaveCriticalSection(&This->lock);
1081 return AUDCLNT_E_NOT_INITIALIZED;
1084 if(This->dataflow == eRender){
1085 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1086 LeaveCriticalSection(&This->lock);
1087 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1088 return E_FAIL;
1091 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1092 This->fmt->nBlockAlign;
1094 /* when the OSS buffer has less than one fragment of data, including
1095 * no data, it often reports it as some non-zero portion of a
1096 * fragment. when it has more than one fragment of data, it reports
1097 * it as some multiple of that portion of the fragment size.
1099 * so, we have to do some ugly workarounds to report the timing
1100 * as accurately as possible */
1101 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1102 *numpad = This->inbuf_frames;
1103 This->inbuf_frames = 0;
1104 }else{
1105 if(*numpad < This->inbuf_frames)
1106 This->inbuf_frames = *numpad;
1107 else
1108 *numpad = This->inbuf_frames;
1110 }else if(This->dataflow == eCapture){
1111 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1112 LeaveCriticalSection(&This->lock);
1113 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1114 return E_FAIL;
1117 if(bi.bytes <= bi.fragsize)
1118 *numpad = 0;
1119 else
1120 *numpad = bi.bytes / This->fmt->nBlockAlign;
1121 }else{
1122 LeaveCriticalSection(&This->lock);
1123 return E_UNEXPECTED;
1126 *numpad += This->held_frames;
1128 LeaveCriticalSection(&This->lock);
1130 return S_OK;
1133 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1134 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1135 WAVEFORMATEX **outpwfx)
1137 ACImpl *This = impl_from_IAudioClient(iface);
1138 int fd = -1;
1139 HRESULT ret;
1141 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1143 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1144 return E_POINTER;
1146 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1147 return E_INVALIDARG;
1149 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1150 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1151 return E_INVALIDARG;
1153 dump_fmt(pwfx);
1155 if(outpwfx){
1156 *outpwfx = NULL;
1157 if(mode != AUDCLNT_SHAREMODE_SHARED)
1158 outpwfx = NULL;
1161 if(This->dataflow == eRender)
1162 fd = open(This->devnode, O_WRONLY, 0);
1163 else if(This->dataflow == eCapture)
1164 fd = open(This->devnode, O_RDONLY, 0);
1166 if(fd < 0){
1167 ERR("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1168 strerror(errno));
1169 return AUDCLNT_E_DEVICE_INVALIDATED;
1172 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1174 close(fd);
1176 return ret;
1179 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1180 WAVEFORMATEX **pwfx)
1182 ACImpl *This = impl_from_IAudioClient(iface);
1183 WAVEFORMATEXTENSIBLE *fmt;
1184 int formats;
1186 TRACE("(%p)->(%p)\n", This, pwfx);
1188 if(!pwfx)
1189 return E_POINTER;
1190 *pwfx = NULL;
1192 if(This->dataflow == eRender)
1193 formats = This->ai.oformats;
1194 else if(This->dataflow == eCapture)
1195 formats = This->ai.iformats;
1196 else
1197 return E_UNEXPECTED;
1199 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1200 if(!fmt)
1201 return E_OUTOFMEMORY;
1203 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1204 if(formats & AFMT_S16_LE){
1205 fmt->Format.wBitsPerSample = 16;
1206 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1207 #ifdef AFMT_FLOAT
1208 }else if(formats & AFMT_FLOAT){
1209 fmt->Format.wBitsPerSample = 32;
1210 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1211 #endif
1212 }else if(formats & AFMT_U8){
1213 fmt->Format.wBitsPerSample = 8;
1214 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1215 }else if(formats & AFMT_S32_LE){
1216 fmt->Format.wBitsPerSample = 32;
1217 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1218 }else if(formats & AFMT_S24_LE){
1219 fmt->Format.wBitsPerSample = 24;
1220 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1221 }else{
1222 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1223 CoTaskMemFree(fmt);
1224 return E_FAIL;
1227 fmt->Format.nChannels = This->ai.max_channels;
1228 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1229 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1231 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1232 fmt->Format.nChannels) / 8;
1233 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1234 fmt->Format.nBlockAlign;
1236 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1237 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1239 *pwfx = (WAVEFORMATEX*)fmt;
1240 dump_fmt(*pwfx);
1242 return S_OK;
1245 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1246 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1248 ACImpl *This = impl_from_IAudioClient(iface);
1250 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1252 if(!defperiod && !minperiod)
1253 return E_POINTER;
1255 EnterCriticalSection(&This->lock);
1257 if(defperiod)
1258 *defperiod = DefaultPeriod;
1259 if(minperiod)
1260 *minperiod = MinimumPeriod;
1262 LeaveCriticalSection(&This->lock);
1264 return S_OK;
1267 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1269 if(This->fmt->wBitsPerSample == 8)
1270 memset(buf, 128, frames * This->fmt->nBlockAlign);
1271 else
1272 memset(buf, 0, frames * This->fmt->nBlockAlign);
1275 static void oss_write_data(ACImpl *This)
1277 ssize_t written_bytes;
1278 UINT32 written_frames;
1279 size_t to_write_frames, to_write_bytes;
1280 audio_buf_info bi;
1281 BYTE *buf =
1282 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1284 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1285 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1286 else
1287 to_write_frames = This->held_frames;
1288 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1290 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1291 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1292 return;
1295 if(bi.bytes < to_write_bytes){
1296 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1297 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1300 if(This->session->mute)
1301 oss_silence_buffer(This, buf, to_write_frames);
1303 written_bytes = write(This->fd, buf, to_write_bytes);
1304 if(written_bytes < 0){
1305 /* EAGAIN is OSS buffer full, log that too */
1306 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1307 return;
1309 written_frames = written_bytes / This->fmt->nBlockAlign;
1311 This->lcl_offs_frames += written_frames;
1312 This->lcl_offs_frames %= This->bufsize_frames;
1313 This->held_frames -= written_frames;
1314 This->inbuf_frames += written_frames;
1316 if(written_frames < to_write_frames){
1317 /* OSS buffer probably full */
1318 return;
1321 bi.bytes -= written_bytes;
1322 if(This->held_frames && bi.bytes >= This->fmt->nBlockAlign){
1323 /* wrapped and have some data back at the start to write */
1325 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1326 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1328 if(This->session->mute)
1329 oss_silence_buffer(This, This->local_buffer, to_write_frames);
1331 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1332 if(written_bytes < 0){
1333 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1334 return;
1336 written_frames = written_bytes / This->fmt->nBlockAlign;
1338 This->lcl_offs_frames += written_frames;
1339 This->lcl_offs_frames %= This->bufsize_frames;
1340 This->held_frames -= written_frames;
1341 This->inbuf_frames += written_frames;
1345 static void oss_read_data(ACImpl *This)
1347 UINT64 pos, readable;
1348 audio_buf_info bi;
1349 ssize_t nread;
1351 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1352 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1353 return;
1356 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1357 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1359 if(bi.bytes < readable)
1360 readable = bi.bytes;
1362 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1363 readable);
1364 if(nread < 0){
1365 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1366 return;
1369 This->held_frames += nread / This->fmt->nBlockAlign;
1371 if(This->held_frames > This->bufsize_frames){
1372 WARN("Overflow of unread data\n");
1373 This->lcl_offs_frames += This->held_frames;
1374 This->lcl_offs_frames %= This->bufsize_frames;
1375 This->held_frames = This->bufsize_frames;
1379 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1381 ACImpl *This = user;
1383 EnterCriticalSection(&This->lock);
1385 if(This->dataflow == eRender && This->held_frames)
1386 oss_write_data(This);
1387 else if(This->dataflow == eCapture)
1388 oss_read_data(This);
1390 if(This->event)
1391 SetEvent(This->event);
1393 LeaveCriticalSection(&This->lock);
1396 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1398 ACImpl *This = impl_from_IAudioClient(iface);
1399 int mask;
1401 TRACE("(%p)\n", This);
1403 EnterCriticalSection(&This->lock);
1405 if(!This->initted){
1406 LeaveCriticalSection(&This->lock);
1407 return AUDCLNT_E_NOT_INITIALIZED;
1410 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1411 LeaveCriticalSection(&This->lock);
1412 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1415 if(This->playing){
1416 LeaveCriticalSection(&This->lock);
1417 return AUDCLNT_E_NOT_STOPPED;
1420 if(This->dataflow == eRender)
1421 mask = PCM_ENABLE_OUTPUT;
1422 else if(This->dataflow == eCapture)
1423 mask = PCM_ENABLE_INPUT;
1424 else{
1425 LeaveCriticalSection(&This->lock);
1426 return E_UNEXPECTED;
1429 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1430 LeaveCriticalSection(&This->lock);
1431 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1432 return E_FAIL;
1435 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1436 oss_period_callback, This, 0, This->period_us / 1000,
1437 WT_EXECUTEINTIMERTHREAD))
1438 ERR("Unable to create period timer: %u\n", GetLastError());
1440 This->playing = TRUE;
1442 LeaveCriticalSection(&This->lock);
1444 return S_OK;
1447 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1449 ACImpl *This = impl_from_IAudioClient(iface);
1450 int mask;
1452 TRACE("(%p)\n", This);
1454 EnterCriticalSection(&This->lock);
1456 if(!This->initted){
1457 LeaveCriticalSection(&This->lock);
1458 return AUDCLNT_E_NOT_INITIALIZED;
1461 if(!This->playing){
1462 LeaveCriticalSection(&This->lock);
1463 return S_FALSE;
1466 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1467 DeleteTimerQueueTimer(g_timer_q, This->timer,
1468 INVALID_HANDLE_VALUE);
1469 This->timer = NULL;
1472 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1473 LeaveCriticalSection(&This->lock);
1474 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1475 return E_FAIL;
1478 mask = 0;
1479 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1480 LeaveCriticalSection(&This->lock);
1481 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1482 return E_FAIL;
1485 This->playing = FALSE;
1487 LeaveCriticalSection(&This->lock);
1489 return S_OK;
1492 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1494 ACImpl *This = impl_from_IAudioClient(iface);
1496 TRACE("(%p)\n", This);
1498 EnterCriticalSection(&This->lock);
1500 if(!This->initted){
1501 LeaveCriticalSection(&This->lock);
1502 return AUDCLNT_E_NOT_INITIALIZED;
1505 if(This->playing){
1506 LeaveCriticalSection(&This->lock);
1507 return AUDCLNT_E_NOT_STOPPED;
1510 if(This->buf_state != NOT_LOCKED){
1511 LeaveCriticalSection(&This->lock);
1512 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1515 This->written_frames = 0;
1516 This->inbuf_frames = 0;
1517 This->held_frames = 0;
1519 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1520 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1522 LeaveCriticalSection(&This->lock);
1524 return S_OK;
1527 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1528 HANDLE event)
1530 ACImpl *This = impl_from_IAudioClient(iface);
1532 TRACE("(%p)->(%p)\n", This, event);
1534 if(!event)
1535 return E_INVALIDARG;
1537 EnterCriticalSection(&This->lock);
1539 if(!This->initted){
1540 LeaveCriticalSection(&This->lock);
1541 return AUDCLNT_E_NOT_INITIALIZED;
1544 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1545 LeaveCriticalSection(&This->lock);
1546 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1549 This->event = event;
1551 LeaveCriticalSection(&This->lock);
1553 return S_OK;
1556 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1557 void **ppv)
1559 ACImpl *This = impl_from_IAudioClient(iface);
1561 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1563 if(!ppv)
1564 return E_POINTER;
1565 *ppv = NULL;
1567 EnterCriticalSection(&This->lock);
1569 if(!This->initted){
1570 LeaveCriticalSection(&This->lock);
1571 return AUDCLNT_E_NOT_INITIALIZED;
1574 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1575 if(This->dataflow != eRender){
1576 LeaveCriticalSection(&This->lock);
1577 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1579 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1580 *ppv = &This->IAudioRenderClient_iface;
1581 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1582 if(This->dataflow != eCapture){
1583 LeaveCriticalSection(&This->lock);
1584 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1586 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1587 *ppv = &This->IAudioCaptureClient_iface;
1588 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1589 IAudioClock_AddRef(&This->IAudioClock_iface);
1590 *ppv = &This->IAudioClock_iface;
1591 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1592 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1593 *ppv = &This->IAudioStreamVolume_iface;
1594 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1595 if(!This->session_wrapper){
1596 This->session_wrapper = AudioSessionWrapper_Create(This);
1597 if(!This->session_wrapper){
1598 LeaveCriticalSection(&This->lock);
1599 return E_OUTOFMEMORY;
1601 }else
1602 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1604 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1605 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1606 if(!This->session_wrapper){
1607 This->session_wrapper = AudioSessionWrapper_Create(This);
1608 if(!This->session_wrapper){
1609 LeaveCriticalSection(&This->lock);
1610 return E_OUTOFMEMORY;
1612 }else
1613 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1615 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1616 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1617 if(!This->session_wrapper){
1618 This->session_wrapper = AudioSessionWrapper_Create(This);
1619 if(!This->session_wrapper){
1620 LeaveCriticalSection(&This->lock);
1621 return E_OUTOFMEMORY;
1623 }else
1624 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1626 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1629 if(*ppv){
1630 LeaveCriticalSection(&This->lock);
1631 return S_OK;
1634 LeaveCriticalSection(&This->lock);
1636 FIXME("stub %s\n", debugstr_guid(riid));
1637 return E_NOINTERFACE;
1640 static const IAudioClientVtbl AudioClient_Vtbl =
1642 AudioClient_QueryInterface,
1643 AudioClient_AddRef,
1644 AudioClient_Release,
1645 AudioClient_Initialize,
1646 AudioClient_GetBufferSize,
1647 AudioClient_GetStreamLatency,
1648 AudioClient_GetCurrentPadding,
1649 AudioClient_IsFormatSupported,
1650 AudioClient_GetMixFormat,
1651 AudioClient_GetDevicePeriod,
1652 AudioClient_Start,
1653 AudioClient_Stop,
1654 AudioClient_Reset,
1655 AudioClient_SetEventHandle,
1656 AudioClient_GetService
1659 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1660 IAudioRenderClient *iface, REFIID riid, void **ppv)
1662 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1664 if(!ppv)
1665 return E_POINTER;
1666 *ppv = NULL;
1668 if(IsEqualIID(riid, &IID_IUnknown) ||
1669 IsEqualIID(riid, &IID_IAudioRenderClient))
1670 *ppv = iface;
1671 if(*ppv){
1672 IUnknown_AddRef((IUnknown*)*ppv);
1673 return S_OK;
1676 WARN("Unknown interface %s\n", debugstr_guid(riid));
1677 return E_NOINTERFACE;
1680 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1682 ACImpl *This = impl_from_IAudioRenderClient(iface);
1683 return AudioClient_AddRef(&This->IAudioClient_iface);
1686 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1688 ACImpl *This = impl_from_IAudioRenderClient(iface);
1689 return AudioClient_Release(&This->IAudioClient_iface);
1692 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1693 UINT32 frames, BYTE **data)
1695 ACImpl *This = impl_from_IAudioRenderClient(iface);
1696 UINT32 pad, write_pos;
1697 HRESULT hr;
1699 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1701 if(!data)
1702 return E_POINTER;
1704 EnterCriticalSection(&This->lock);
1706 if(This->buf_state != NOT_LOCKED){
1707 LeaveCriticalSection(&This->lock);
1708 return AUDCLNT_E_OUT_OF_ORDER;
1711 if(!frames){
1712 This->buf_state = LOCKED_NORMAL;
1713 LeaveCriticalSection(&This->lock);
1714 return S_OK;
1717 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1718 if(FAILED(hr)){
1719 LeaveCriticalSection(&This->lock);
1720 return hr;
1723 if(pad + frames > This->bufsize_frames){
1724 LeaveCriticalSection(&This->lock);
1725 return AUDCLNT_E_BUFFER_TOO_LARGE;
1728 write_pos =
1729 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1730 if(write_pos + frames > This->bufsize_frames){
1731 if(This->tmp_buffer_frames < frames){
1732 if(This->tmp_buffer)
1733 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1734 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1735 else
1736 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1737 frames * This->fmt->nBlockAlign);
1738 if(!This->tmp_buffer){
1739 LeaveCriticalSection(&This->lock);
1740 return E_OUTOFMEMORY;
1742 This->tmp_buffer_frames = frames;
1744 *data = This->tmp_buffer;
1745 This->buf_state = LOCKED_WRAPPED;
1746 }else{
1747 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1748 This->buf_state = LOCKED_NORMAL;
1751 LeaveCriticalSection(&This->lock);
1753 return S_OK;
1756 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1758 UINT32 write_offs_frames =
1759 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1760 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1761 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1762 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1763 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1765 if(written_bytes <= chunk_bytes){
1766 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1767 }else{
1768 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1769 memcpy(This->local_buffer, buffer + chunk_bytes,
1770 written_bytes - chunk_bytes);
1774 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1775 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1777 ACImpl *This = impl_from_IAudioRenderClient(iface);
1778 BYTE *buffer;
1780 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1782 EnterCriticalSection(&This->lock);
1784 if(This->buf_state == NOT_LOCKED || !written_frames){
1785 This->buf_state = NOT_LOCKED;
1786 LeaveCriticalSection(&This->lock);
1787 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1790 if(This->buf_state == LOCKED_NORMAL)
1791 buffer = This->local_buffer + This->fmt->nBlockAlign *
1792 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1793 else
1794 buffer = This->tmp_buffer;
1796 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1797 oss_silence_buffer(This, buffer, written_frames);
1799 if(This->held_frames){
1800 if(This->buf_state == LOCKED_WRAPPED)
1801 oss_wrap_buffer(This, buffer, written_frames);
1803 This->held_frames += written_frames;
1804 }else{
1805 ssize_t w_bytes;
1806 UINT32 w_frames;
1808 if(This->session->mute)
1809 oss_silence_buffer(This, buffer, written_frames);
1811 w_bytes = write(This->fd, buffer,
1812 written_frames * This->fmt->nBlockAlign);
1813 if(w_bytes < 0){
1814 if(errno != EAGAIN){
1815 This->buf_state = NOT_LOCKED;
1816 LeaveCriticalSection(&This->lock);
1817 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1818 return E_FAIL;
1819 }else /* OSS buffer full */
1820 w_bytes = 0;
1822 w_frames = w_bytes / This->fmt->nBlockAlign;
1823 This->inbuf_frames += w_frames;
1825 if(w_frames < written_frames){
1826 if(This->buf_state == LOCKED_WRAPPED)
1827 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1828 written_frames - w_frames);
1829 else
1830 This->lcl_offs_frames += w_frames;
1831 This->held_frames = written_frames - w_frames;
1835 This->written_frames += written_frames;
1836 This->buf_state = NOT_LOCKED;
1838 LeaveCriticalSection(&This->lock);
1840 return S_OK;
1843 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1844 AudioRenderClient_QueryInterface,
1845 AudioRenderClient_AddRef,
1846 AudioRenderClient_Release,
1847 AudioRenderClient_GetBuffer,
1848 AudioRenderClient_ReleaseBuffer
1851 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1852 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1854 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1856 if(!ppv)
1857 return E_POINTER;
1858 *ppv = NULL;
1860 if(IsEqualIID(riid, &IID_IUnknown) ||
1861 IsEqualIID(riid, &IID_IAudioCaptureClient))
1862 *ppv = iface;
1863 if(*ppv){
1864 IUnknown_AddRef((IUnknown*)*ppv);
1865 return S_OK;
1868 WARN("Unknown interface %s\n", debugstr_guid(riid));
1869 return E_NOINTERFACE;
1872 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1874 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1875 return IAudioClient_AddRef(&This->IAudioClient_iface);
1878 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1880 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1881 return IAudioClient_Release(&This->IAudioClient_iface);
1884 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1885 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1886 UINT64 *qpcpos)
1888 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1889 HRESULT hr;
1891 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1892 devpos, qpcpos);
1894 if(!data || !frames || !flags)
1895 return E_POINTER;
1897 EnterCriticalSection(&This->lock);
1899 if(This->buf_state != NOT_LOCKED){
1900 LeaveCriticalSection(&This->lock);
1901 return AUDCLNT_E_OUT_OF_ORDER;
1904 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1905 if(FAILED(hr)){
1906 LeaveCriticalSection(&This->lock);
1907 return hr;
1910 *flags = 0;
1912 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1913 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1914 if(This->tmp_buffer_frames < *frames){
1915 if(This->tmp_buffer)
1916 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1917 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1918 else
1919 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1920 *frames * This->fmt->nBlockAlign);
1921 if(!This->tmp_buffer){
1922 LeaveCriticalSection(&This->lock);
1923 return E_OUTOFMEMORY;
1925 This->tmp_buffer_frames = *frames;
1928 *data = This->tmp_buffer;
1929 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1930 This->fmt->nBlockAlign;
1931 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1932 frames_bytes = *frames * This->fmt->nBlockAlign;
1933 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1934 memcpy(This->tmp_buffer, This->local_buffer,
1935 frames_bytes - chunk_bytes);
1936 }else
1937 *data = This->local_buffer +
1938 This->lcl_offs_frames * This->fmt->nBlockAlign;
1940 This->buf_state = LOCKED_NORMAL;
1942 if(devpos || qpcpos)
1943 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1945 LeaveCriticalSection(&This->lock);
1947 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1950 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1951 IAudioCaptureClient *iface, UINT32 done)
1953 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1955 TRACE("(%p)->(%u)\n", This, done);
1957 EnterCriticalSection(&This->lock);
1959 if(This->buf_state == NOT_LOCKED){
1960 LeaveCriticalSection(&This->lock);
1961 return AUDCLNT_E_OUT_OF_ORDER;
1964 This->held_frames -= done;
1965 This->lcl_offs_frames += done;
1966 This->lcl_offs_frames %= This->bufsize_frames;
1968 This->buf_state = NOT_LOCKED;
1970 LeaveCriticalSection(&This->lock);
1972 return S_OK;
1975 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1976 IAudioCaptureClient *iface, UINT32 *frames)
1978 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1980 TRACE("(%p)->(%p)\n", This, frames);
1982 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1985 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1987 AudioCaptureClient_QueryInterface,
1988 AudioCaptureClient_AddRef,
1989 AudioCaptureClient_Release,
1990 AudioCaptureClient_GetBuffer,
1991 AudioCaptureClient_ReleaseBuffer,
1992 AudioCaptureClient_GetNextPacketSize
1995 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1996 REFIID riid, void **ppv)
1998 ACImpl *This = impl_from_IAudioClock(iface);
2000 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2002 if(!ppv)
2003 return E_POINTER;
2004 *ppv = NULL;
2006 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2007 *ppv = iface;
2008 else if(IsEqualIID(riid, &IID_IAudioClock2))
2009 *ppv = &This->IAudioClock2_iface;
2010 if(*ppv){
2011 IUnknown_AddRef((IUnknown*)*ppv);
2012 return S_OK;
2015 WARN("Unknown interface %s\n", debugstr_guid(riid));
2016 return E_NOINTERFACE;
2019 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2021 ACImpl *This = impl_from_IAudioClock(iface);
2022 return IAudioClient_AddRef(&This->IAudioClient_iface);
2025 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2027 ACImpl *This = impl_from_IAudioClock(iface);
2028 return IAudioClient_Release(&This->IAudioClient_iface);
2031 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2033 ACImpl *This = impl_from_IAudioClock(iface);
2035 TRACE("(%p)->(%p)\n", This, freq);
2037 *freq = This->fmt->nSamplesPerSec;
2039 return S_OK;
2042 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2043 UINT64 *qpctime)
2045 ACImpl *This = impl_from_IAudioClock(iface);
2046 UINT32 pad;
2047 HRESULT hr;
2049 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2051 if(!pos)
2052 return E_POINTER;
2054 EnterCriticalSection(&This->lock);
2056 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2057 if(FAILED(hr)){
2058 LeaveCriticalSection(&This->lock);
2059 return hr;
2062 if(This->dataflow == eRender)
2063 *pos = This->written_frames - pad;
2064 else if(This->dataflow == eCapture)
2065 *pos = This->written_frames + pad;
2067 LeaveCriticalSection(&This->lock);
2069 if(qpctime){
2070 LARGE_INTEGER stamp, freq;
2071 QueryPerformanceCounter(&stamp);
2072 QueryPerformanceFrequency(&freq);
2073 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2076 return S_OK;
2079 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2080 DWORD *chars)
2082 ACImpl *This = impl_from_IAudioClock(iface);
2084 TRACE("(%p)->(%p)\n", This, chars);
2086 if(!chars)
2087 return E_POINTER;
2089 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2091 return S_OK;
2094 static const IAudioClockVtbl AudioClock_Vtbl =
2096 AudioClock_QueryInterface,
2097 AudioClock_AddRef,
2098 AudioClock_Release,
2099 AudioClock_GetFrequency,
2100 AudioClock_GetPosition,
2101 AudioClock_GetCharacteristics
2104 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2105 REFIID riid, void **ppv)
2107 ACImpl *This = impl_from_IAudioClock2(iface);
2108 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2111 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2113 ACImpl *This = impl_from_IAudioClock2(iface);
2114 return IAudioClient_AddRef(&This->IAudioClient_iface);
2117 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2119 ACImpl *This = impl_from_IAudioClock2(iface);
2120 return IAudioClient_Release(&This->IAudioClient_iface);
2123 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2124 UINT64 *pos, UINT64 *qpctime)
2126 ACImpl *This = impl_from_IAudioClock2(iface);
2128 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2130 return E_NOTIMPL;
2133 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2135 AudioClock2_QueryInterface,
2136 AudioClock2_AddRef,
2137 AudioClock2_Release,
2138 AudioClock2_GetDevicePosition
2141 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2143 AudioSessionWrapper *ret;
2145 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2146 sizeof(AudioSessionWrapper));
2147 if(!ret)
2148 return NULL;
2150 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2151 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2152 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2154 ret->ref = 1;
2156 ret->client = client;
2157 if(client){
2158 ret->session = client->session;
2159 AudioClient_AddRef(&client->IAudioClient_iface);
2162 return ret;
2165 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2166 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2168 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2170 if(!ppv)
2171 return E_POINTER;
2172 *ppv = NULL;
2174 if(IsEqualIID(riid, &IID_IUnknown) ||
2175 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2176 IsEqualIID(riid, &IID_IAudioSessionControl2))
2177 *ppv = iface;
2178 if(*ppv){
2179 IUnknown_AddRef((IUnknown*)*ppv);
2180 return S_OK;
2183 WARN("Unknown interface %s\n", debugstr_guid(riid));
2184 return E_NOINTERFACE;
2187 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2189 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2190 ULONG ref;
2191 ref = InterlockedIncrement(&This->ref);
2192 TRACE("(%p) Refcount now %u\n", This, ref);
2193 return ref;
2196 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2198 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2199 ULONG ref;
2200 ref = InterlockedDecrement(&This->ref);
2201 TRACE("(%p) Refcount now %u\n", This, ref);
2202 if(!ref){
2203 if(This->client){
2204 EnterCriticalSection(&This->client->lock);
2205 This->client->session_wrapper = NULL;
2206 LeaveCriticalSection(&This->client->lock);
2207 AudioClient_Release(&This->client->IAudioClient_iface);
2209 HeapFree(GetProcessHeap(), 0, This);
2211 return ref;
2214 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2215 AudioSessionState *state)
2217 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2218 ACImpl *client;
2220 TRACE("(%p)->(%p)\n", This, state);
2222 if(!state)
2223 return NULL_PTR_ERR;
2225 EnterCriticalSection(&g_sessions_lock);
2227 if(list_empty(&This->session->clients)){
2228 *state = AudioSessionStateExpired;
2229 LeaveCriticalSection(&g_sessions_lock);
2230 return S_OK;
2233 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2234 EnterCriticalSection(&client->lock);
2235 if(client->playing){
2236 *state = AudioSessionStateActive;
2237 LeaveCriticalSection(&client->lock);
2238 LeaveCriticalSection(&g_sessions_lock);
2239 return S_OK;
2241 LeaveCriticalSection(&client->lock);
2244 LeaveCriticalSection(&g_sessions_lock);
2246 *state = AudioSessionStateInactive;
2248 return S_OK;
2251 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2252 IAudioSessionControl2 *iface, WCHAR **name)
2254 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2256 FIXME("(%p)->(%p) - stub\n", This, name);
2258 return E_NOTIMPL;
2261 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2262 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2264 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2266 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2268 return E_NOTIMPL;
2271 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2272 IAudioSessionControl2 *iface, WCHAR **path)
2274 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2276 FIXME("(%p)->(%p) - stub\n", This, path);
2278 return E_NOTIMPL;
2281 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2282 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2284 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2286 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2288 return E_NOTIMPL;
2291 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2292 IAudioSessionControl2 *iface, GUID *group)
2294 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2296 FIXME("(%p)->(%p) - stub\n", This, group);
2298 return E_NOTIMPL;
2301 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2302 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2304 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2306 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2307 debugstr_guid(session));
2309 return E_NOTIMPL;
2312 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2313 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2315 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2317 FIXME("(%p)->(%p) - stub\n", This, events);
2319 return S_OK;
2322 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2323 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2325 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2327 FIXME("(%p)->(%p) - stub\n", This, events);
2329 return S_OK;
2332 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2333 IAudioSessionControl2 *iface, WCHAR **id)
2335 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2337 FIXME("(%p)->(%p) - stub\n", This, id);
2339 return E_NOTIMPL;
2342 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2343 IAudioSessionControl2 *iface, WCHAR **id)
2345 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2347 FIXME("(%p)->(%p) - stub\n", This, id);
2349 return E_NOTIMPL;
2352 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2353 IAudioSessionControl2 *iface, DWORD *pid)
2355 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2357 TRACE("(%p)->(%p)\n", This, pid);
2359 if(!pid)
2360 return E_POINTER;
2362 *pid = GetCurrentProcessId();
2364 return S_OK;
2367 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2368 IAudioSessionControl2 *iface)
2370 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2372 TRACE("(%p)\n", This);
2374 return S_FALSE;
2377 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2378 IAudioSessionControl2 *iface, BOOL optout)
2380 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2382 TRACE("(%p)->(%d)\n", This, optout);
2384 return S_OK;
2387 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2389 AudioSessionControl_QueryInterface,
2390 AudioSessionControl_AddRef,
2391 AudioSessionControl_Release,
2392 AudioSessionControl_GetState,
2393 AudioSessionControl_GetDisplayName,
2394 AudioSessionControl_SetDisplayName,
2395 AudioSessionControl_GetIconPath,
2396 AudioSessionControl_SetIconPath,
2397 AudioSessionControl_GetGroupingParam,
2398 AudioSessionControl_SetGroupingParam,
2399 AudioSessionControl_RegisterAudioSessionNotification,
2400 AudioSessionControl_UnregisterAudioSessionNotification,
2401 AudioSessionControl_GetSessionIdentifier,
2402 AudioSessionControl_GetSessionInstanceIdentifier,
2403 AudioSessionControl_GetProcessId,
2404 AudioSessionControl_IsSystemSoundsSession,
2405 AudioSessionControl_SetDuckingPreference
2408 /* index == -1 means set all channels, otherwise sets only the given channel */
2409 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2411 int setreq, getreq;
2412 unsigned int vol;
2413 unsigned short l;
2414 float level;
2416 if(index == (UINT32)-1){
2417 HRESULT ret = S_OK;
2418 UINT32 i;
2419 for(i = 0; i < This->fmt->nChannels; ++i){
2420 HRESULT hr;
2421 hr = oss_setvol(This, i);
2422 if(FAILED(hr))
2423 ret = hr;
2425 return ret;
2428 if(index > 1)
2429 /* OSS doesn't support volume control past the first two channels */
2430 return S_OK;
2432 if(This->dataflow == eRender){
2433 setreq = SNDCTL_DSP_SETPLAYVOL;
2434 getreq = SNDCTL_DSP_GETPLAYVOL;
2435 }else if(This->dataflow == eCapture){
2436 setreq = SNDCTL_DSP_SETRECVOL;
2437 getreq = SNDCTL_DSP_GETRECVOL;
2438 }else
2439 return E_UNEXPECTED;
2441 if(ioctl(This->fd, getreq, &vol) < 0){
2442 if(errno == EINVAL)
2443 /* device doesn't support this call */
2444 return S_OK;
2446 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2447 return E_FAIL;
2450 level = This->session->master_vol * This->session->channel_vols[index] *
2451 This->vols[index];
2452 l = level * 100;
2453 if(index == 0)
2454 vol = l | (vol & 0xFF00);
2455 else
2456 vol = (vol & 0xFF) | (l << 8);
2458 if(ioctl(This->fd, setreq, &vol) < 0){
2459 if(errno == EINVAL)
2460 /* device doesn't support this call */
2461 return S_OK;
2463 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2464 return E_FAIL;
2467 return S_OK;
2470 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2472 HRESULT ret = S_OK;
2473 ACImpl *client;
2475 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2476 HRESULT hr;
2477 hr = oss_setvol(client, index);
2478 if(FAILED(hr))
2479 ret = hr;
2482 return ret;
2485 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2486 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2488 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2490 if(!ppv)
2491 return E_POINTER;
2492 *ppv = NULL;
2494 if(IsEqualIID(riid, &IID_IUnknown) ||
2495 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2496 *ppv = iface;
2497 if(*ppv){
2498 IUnknown_AddRef((IUnknown*)*ppv);
2499 return S_OK;
2502 WARN("Unknown interface %s\n", debugstr_guid(riid));
2503 return E_NOINTERFACE;
2506 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2508 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2509 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2512 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2514 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2515 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2518 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2519 ISimpleAudioVolume *iface, float level, const GUID *context)
2521 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2522 AudioSession *session = This->session;
2523 HRESULT ret;
2525 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2527 if(level < 0.f || level > 1.f)
2528 return E_INVALIDARG;
2530 if(context)
2531 FIXME("Notifications not supported yet\n");
2533 EnterCriticalSection(&session->lock);
2535 session->master_vol = level;
2537 ret = oss_session_setvol(session, -1);
2539 LeaveCriticalSection(&session->lock);
2541 return ret;
2544 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2545 ISimpleAudioVolume *iface, float *level)
2547 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2548 AudioSession *session = This->session;
2550 TRACE("(%p)->(%p)\n", session, level);
2552 if(!level)
2553 return NULL_PTR_ERR;
2555 *level = session->master_vol;
2557 return S_OK;
2560 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2561 BOOL mute, const GUID *context)
2563 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2564 AudioSession *session = This->session;
2566 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2568 EnterCriticalSection(&session->lock);
2570 if(!mute && session->mute){
2571 ACImpl *client;
2573 session->mute = mute;
2575 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2576 EnterCriticalSection(&client->lock);
2577 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2578 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2579 strerror(errno));
2580 oss_write_data(client);
2581 LeaveCriticalSection(&client->lock);
2583 }else
2584 session->mute = mute;
2586 LeaveCriticalSection(&session->lock);
2588 return S_OK;
2591 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2592 BOOL *mute)
2594 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2595 AudioSession *session = This->session;
2597 TRACE("(%p)->(%p)\n", session, mute);
2599 if(!mute)
2600 return NULL_PTR_ERR;
2602 *mute = This->session->mute;
2604 return S_OK;
2607 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2609 SimpleAudioVolume_QueryInterface,
2610 SimpleAudioVolume_AddRef,
2611 SimpleAudioVolume_Release,
2612 SimpleAudioVolume_SetMasterVolume,
2613 SimpleAudioVolume_GetMasterVolume,
2614 SimpleAudioVolume_SetMute,
2615 SimpleAudioVolume_GetMute
2618 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2619 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2621 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2623 if(!ppv)
2624 return E_POINTER;
2625 *ppv = NULL;
2627 if(IsEqualIID(riid, &IID_IUnknown) ||
2628 IsEqualIID(riid, &IID_IAudioStreamVolume))
2629 *ppv = iface;
2630 if(*ppv){
2631 IUnknown_AddRef((IUnknown*)*ppv);
2632 return S_OK;
2635 WARN("Unknown interface %s\n", debugstr_guid(riid));
2636 return E_NOINTERFACE;
2639 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2641 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2642 return IAudioClient_AddRef(&This->IAudioClient_iface);
2645 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2647 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2648 return IAudioClient_Release(&This->IAudioClient_iface);
2651 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2652 IAudioStreamVolume *iface, UINT32 *out)
2654 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2656 TRACE("(%p)->(%p)\n", This, out);
2658 if(!out)
2659 return E_POINTER;
2661 *out = This->fmt->nChannels;
2663 return S_OK;
2666 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2667 IAudioStreamVolume *iface, UINT32 index, float level)
2669 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2670 HRESULT ret;
2672 TRACE("(%p)->(%d, %f)\n", This, index, level);
2674 if(level < 0.f || level > 1.f)
2675 return E_INVALIDARG;
2677 if(index >= This->fmt->nChannels)
2678 return E_INVALIDARG;
2680 EnterCriticalSection(&This->lock);
2682 This->vols[index] = level;
2684 ret = oss_setvol(This, index);
2686 LeaveCriticalSection(&This->lock);
2688 return ret;
2691 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2692 IAudioStreamVolume *iface, UINT32 index, float *level)
2694 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2696 TRACE("(%p)->(%d, %p)\n", This, index, level);
2698 if(!level)
2699 return E_POINTER;
2701 if(index >= This->fmt->nChannels)
2702 return E_INVALIDARG;
2704 *level = This->vols[index];
2706 return S_OK;
2709 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2710 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2712 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2713 int i;
2714 HRESULT ret;
2716 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2718 if(!levels)
2719 return E_POINTER;
2721 if(count != This->fmt->nChannels)
2722 return E_INVALIDARG;
2724 EnterCriticalSection(&This->lock);
2726 for(i = 0; i < count; ++i)
2727 This->vols[i] = levels[i];
2729 ret = oss_setvol(This, -1);
2731 LeaveCriticalSection(&This->lock);
2733 return ret;
2736 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2737 IAudioStreamVolume *iface, UINT32 count, float *levels)
2739 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2740 int i;
2742 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2744 if(!levels)
2745 return E_POINTER;
2747 if(count != This->fmt->nChannels)
2748 return E_INVALIDARG;
2750 EnterCriticalSection(&This->lock);
2752 for(i = 0; i < count; ++i)
2753 levels[i] = This->vols[i];
2755 LeaveCriticalSection(&This->lock);
2757 return S_OK;
2760 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2762 AudioStreamVolume_QueryInterface,
2763 AudioStreamVolume_AddRef,
2764 AudioStreamVolume_Release,
2765 AudioStreamVolume_GetChannelCount,
2766 AudioStreamVolume_SetChannelVolume,
2767 AudioStreamVolume_GetChannelVolume,
2768 AudioStreamVolume_SetAllVolumes,
2769 AudioStreamVolume_GetAllVolumes
2772 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2773 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2775 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2777 if(!ppv)
2778 return E_POINTER;
2779 *ppv = NULL;
2781 if(IsEqualIID(riid, &IID_IUnknown) ||
2782 IsEqualIID(riid, &IID_IChannelAudioVolume))
2783 *ppv = iface;
2784 if(*ppv){
2785 IUnknown_AddRef((IUnknown*)*ppv);
2786 return S_OK;
2789 WARN("Unknown interface %s\n", debugstr_guid(riid));
2790 return E_NOINTERFACE;
2793 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2795 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2796 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2799 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2801 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2802 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2805 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2806 IChannelAudioVolume *iface, UINT32 *out)
2808 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2809 AudioSession *session = This->session;
2811 TRACE("(%p)->(%p)\n", session, out);
2813 if(!out)
2814 return NULL_PTR_ERR;
2816 *out = session->channel_count;
2818 return S_OK;
2821 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2822 IChannelAudioVolume *iface, UINT32 index, float level,
2823 const GUID *context)
2825 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2826 AudioSession *session = This->session;
2827 HRESULT ret;
2829 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2830 wine_dbgstr_guid(context));
2832 if(level < 0.f || level > 1.f)
2833 return E_INVALIDARG;
2835 if(index >= session->channel_count)
2836 return E_INVALIDARG;
2838 if(context)
2839 FIXME("Notifications not supported yet\n");
2841 EnterCriticalSection(&session->lock);
2843 session->channel_vols[index] = level;
2845 ret = oss_session_setvol(session, index);
2847 LeaveCriticalSection(&session->lock);
2849 return ret;
2852 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2853 IChannelAudioVolume *iface, UINT32 index, float *level)
2855 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2856 AudioSession *session = This->session;
2858 TRACE("(%p)->(%d, %p)\n", session, index, level);
2860 if(!level)
2861 return NULL_PTR_ERR;
2863 if(index >= session->channel_count)
2864 return E_INVALIDARG;
2866 *level = session->channel_vols[index];
2868 return S_OK;
2871 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2872 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2873 const GUID *context)
2875 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2876 AudioSession *session = This->session;
2877 int i;
2878 HRESULT ret;
2880 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2881 wine_dbgstr_guid(context));
2883 if(!levels)
2884 return NULL_PTR_ERR;
2886 if(count != session->channel_count)
2887 return E_INVALIDARG;
2889 if(context)
2890 FIXME("Notifications not supported yet\n");
2892 EnterCriticalSection(&session->lock);
2894 for(i = 0; i < count; ++i)
2895 session->channel_vols[i] = levels[i];
2897 ret = oss_session_setvol(session, -1);
2899 LeaveCriticalSection(&session->lock);
2901 return ret;
2904 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2905 IChannelAudioVolume *iface, UINT32 count, float *levels)
2907 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2908 AudioSession *session = This->session;
2909 int i;
2911 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2913 if(!levels)
2914 return NULL_PTR_ERR;
2916 if(count != session->channel_count)
2917 return E_INVALIDARG;
2919 for(i = 0; i < count; ++i)
2920 levels[i] = session->channel_vols[i];
2922 return S_OK;
2925 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2927 ChannelAudioVolume_QueryInterface,
2928 ChannelAudioVolume_AddRef,
2929 ChannelAudioVolume_Release,
2930 ChannelAudioVolume_GetChannelCount,
2931 ChannelAudioVolume_SetChannelVolume,
2932 ChannelAudioVolume_GetChannelVolume,
2933 ChannelAudioVolume_SetAllVolumes,
2934 ChannelAudioVolume_GetAllVolumes
2937 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2938 REFIID riid, void **ppv)
2940 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2942 if(!ppv)
2943 return E_POINTER;
2944 *ppv = NULL;
2946 if(IsEqualIID(riid, &IID_IUnknown) ||
2947 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2948 IsEqualIID(riid, &IID_IAudioSessionManager2))
2949 *ppv = iface;
2950 if(*ppv){
2951 IUnknown_AddRef((IUnknown*)*ppv);
2952 return S_OK;
2955 WARN("Unknown interface %s\n", debugstr_guid(riid));
2956 return E_NOINTERFACE;
2959 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2961 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2962 ULONG ref;
2963 ref = InterlockedIncrement(&This->ref);
2964 TRACE("(%p) Refcount now %u\n", This, ref);
2965 return ref;
2968 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2970 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2971 ULONG ref;
2972 ref = InterlockedDecrement(&This->ref);
2973 TRACE("(%p) Refcount now %u\n", This, ref);
2974 if(!ref)
2975 HeapFree(GetProcessHeap(), 0, This);
2976 return ref;
2979 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2980 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2981 IAudioSessionControl **out)
2983 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2984 AudioSession *session;
2985 AudioSessionWrapper *wrapper;
2986 HRESULT hr;
2988 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2989 flags, out);
2991 hr = get_audio_session(session_guid, This->device, 0, &session);
2992 if(FAILED(hr))
2993 return hr;
2995 wrapper = AudioSessionWrapper_Create(NULL);
2996 if(!wrapper)
2997 return E_OUTOFMEMORY;
2999 wrapper->session = session;
3001 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3003 return S_OK;
3006 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3007 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3008 ISimpleAudioVolume **out)
3010 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3011 AudioSession *session;
3012 AudioSessionWrapper *wrapper;
3013 HRESULT hr;
3015 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3016 flags, out);
3018 hr = get_audio_session(session_guid, This->device, 0, &session);
3019 if(FAILED(hr))
3020 return hr;
3022 wrapper = AudioSessionWrapper_Create(NULL);
3023 if(!wrapper)
3024 return E_OUTOFMEMORY;
3026 wrapper->session = session;
3028 *out = &wrapper->ISimpleAudioVolume_iface;
3030 return S_OK;
3033 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3034 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3036 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3037 FIXME("(%p)->(%p) - stub\n", This, out);
3038 return E_NOTIMPL;
3041 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3042 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3044 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3045 FIXME("(%p)->(%p) - stub\n", This, notification);
3046 return E_NOTIMPL;
3049 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3050 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3052 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3053 FIXME("(%p)->(%p) - stub\n", This, notification);
3054 return E_NOTIMPL;
3057 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3058 IAudioSessionManager2 *iface, const WCHAR *session_id,
3059 IAudioVolumeDuckNotification *notification)
3061 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3062 FIXME("(%p)->(%p) - stub\n", This, notification);
3063 return E_NOTIMPL;
3066 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3067 IAudioSessionManager2 *iface,
3068 IAudioVolumeDuckNotification *notification)
3070 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3071 FIXME("(%p)->(%p) - stub\n", This, notification);
3072 return E_NOTIMPL;
3075 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3077 AudioSessionManager_QueryInterface,
3078 AudioSessionManager_AddRef,
3079 AudioSessionManager_Release,
3080 AudioSessionManager_GetAudioSessionControl,
3081 AudioSessionManager_GetSimpleAudioVolume,
3082 AudioSessionManager_GetSessionEnumerator,
3083 AudioSessionManager_RegisterSessionNotification,
3084 AudioSessionManager_UnregisterSessionNotification,
3085 AudioSessionManager_RegisterDuckNotification,
3086 AudioSessionManager_UnregisterDuckNotification
3089 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3090 IAudioSessionManager2 **out)
3092 SessionMgr *This;
3094 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3095 if(!This)
3096 return E_OUTOFMEMORY;
3098 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3099 This->device = device;
3100 This->ref = 1;
3102 *out = &This->IAudioSessionManager2_iface;
3104 return S_OK;