mmdevapi: Round buffer size like native in shared mode.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blobbc4d9527d030dce9a10330eb9896cd4594e8ca69
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);
169 static const IAudioClientVtbl AudioClient_Vtbl;
170 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
171 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
172 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
173 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
174 static const IAudioClockVtbl AudioClock_Vtbl;
175 static const IAudioClock2Vtbl AudioClock2_Vtbl;
176 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
177 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
178 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
180 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
182 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
185 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
187 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
190 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
192 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
195 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
197 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
200 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
202 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
205 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
207 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
210 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
212 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
215 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
220 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
222 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
225 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
227 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
230 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
232 switch (reason)
234 case DLL_PROCESS_ATTACH:
235 g_timer_q = CreateTimerQueue();
236 if(!g_timer_q)
237 return FALSE;
238 break;
240 case DLL_PROCESS_DETACH:
241 DeleteCriticalSection(&g_sessions_lock);
242 break;
244 return TRUE;
247 /* From <dlls/mmdevapi/mmdevapi.h> */
248 enum DriverPriority {
249 Priority_Unavailable = 0,
250 Priority_Low,
251 Priority_Neutral,
252 Priority_Preferred
255 int WINAPI AUDDRV_GetPriority(void)
257 int mixer_fd;
258 oss_sysinfo sysinfo;
260 /* Attempt to determine if we are running on OSS or ALSA's OSS
261 * compatibility layer. There is no official way to do that, so just check
262 * for validity as best as possible, without rejecting valid OSS
263 * implementations. */
265 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
266 if(mixer_fd < 0){
267 TRACE("Priority_Unavailable: open failed\n");
268 return Priority_Unavailable;
271 sysinfo.version[0] = 0xFF;
272 sysinfo.versionnum = ~0;
273 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
274 TRACE("Priority_Unavailable: ioctl failed\n");
275 close(mixer_fd);
276 return Priority_Unavailable;
279 close(mixer_fd);
281 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
282 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
283 return Priority_Low;
285 if(sysinfo.versionnum & 0x80000000){
286 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
287 return Priority_Low;
290 TRACE("Priority_Preferred: Seems like valid OSS!\n");
292 return Priority_Preferred;
295 static const char *oss_clean_devnode(const char *devnode)
297 static char ret[OSS_DEVNODE_SIZE];
299 const char *dot, *slash;
300 size_t len;
302 dot = strrchr(devnode, '.');
303 if(!dot)
304 return devnode;
306 slash = strrchr(devnode, '/');
307 if(slash && dot < slash)
308 return devnode;
310 len = dot - devnode;
312 memcpy(ret, devnode, len);
313 ret[len] = '\0';
315 return ret;
318 static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
320 int fd = -1, err, i;
321 oss_audioinfo ai;
322 const char *devnode;
324 if(flow == eRender)
325 fd = open("/dev/dsp", O_WRONLY);
326 else
327 fd = open("/dev/dsp", O_RDONLY);
329 if(fd < 0){
330 WARN("Couldn't open default device!\n");
331 return 0;
334 ai.dev = -1;
335 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
336 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
337 close(fd);
338 return 0;
341 close(fd);
343 TRACE("Default devnode: %s\n", ai.devnode);
344 devnode = oss_clean_devnode(ai.devnode);
345 for(i = 0; i < num; ++i)
346 if(!strcmp(devnode, keys[i]))
347 return i;
349 WARN("Couldn't find default device! Choosing first.\n");
350 return 0;
353 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
354 UINT *num, UINT *def_index)
356 int i, j, mixer_fd;
357 oss_sysinfo sysinfo;
358 static int print_once = 0;
360 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
362 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
363 if(mixer_fd < 0){
364 ERR("OSS /dev/mixer doesn't seem to exist\n");
365 return AUDCLNT_E_SERVICE_NOT_RUNNING;
368 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
369 close(mixer_fd);
371 if(errno == EINVAL){
372 ERR("OSS version too old, need at least OSSv4\n");
373 return AUDCLNT_E_SERVICE_NOT_RUNNING;
376 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
377 return E_FAIL;
380 if(!print_once){
381 TRACE("OSS sysinfo:\n");
382 TRACE("product: %s\n", sysinfo.product);
383 TRACE("version: %s\n", sysinfo.version);
384 TRACE("versionnum: %x\n", sysinfo.versionnum);
385 TRACE("numaudios: %d\n", sysinfo.numaudios);
386 TRACE("nummixers: %d\n", sysinfo.nummixers);
387 TRACE("numcards: %d\n", sysinfo.numcards);
388 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
389 print_once = 1;
392 if(sysinfo.numaudios <= 0){
393 WARN("No audio devices!\n");
394 close(mixer_fd);
395 return AUDCLNT_E_SERVICE_NOT_RUNNING;
398 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
399 *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
401 *num = 0;
402 for(i = 0; i < sysinfo.numaudios; ++i){
403 oss_audioinfo ai = {0};
404 const char *devnode;
405 int fd;
407 ai.dev = i;
408 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
409 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
410 strerror(errno));
411 continue;
414 devnode = oss_clean_devnode(ai.devnode);
416 /* check for duplicates */
417 for(j = 0; j < *num; ++j)
418 if(!strcmp(devnode, (*keys)[j]))
419 break;
420 if(j != *num)
421 continue;
423 if(flow == eRender)
424 fd = open(devnode, O_WRONLY, 0);
425 else
426 fd = open(devnode, O_RDONLY, 0);
427 if(fd < 0){
428 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
429 devnode, errno, strerror(errno));
430 continue;
432 close(fd);
434 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
435 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
436 size_t len;
438 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
439 strlen(devnode) + 1);
440 if(!(*keys)[*num]){
441 for(i = 0; i < *num; ++i){
442 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
443 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
445 HeapFree(GetProcessHeap(), 0, *ids);
446 HeapFree(GetProcessHeap(), 0, *keys);
447 close(mixer_fd);
448 return E_OUTOFMEMORY;
450 strcpy((*keys)[*num], devnode);
452 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
453 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
454 len * sizeof(WCHAR));
455 if(!(*ids)[*num]){
456 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
457 for(i = 0; i < *num; ++i){
458 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
459 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
461 HeapFree(GetProcessHeap(), 0, *ids);
462 HeapFree(GetProcessHeap(), 0, *keys);
463 close(mixer_fd);
464 return E_OUTOFMEMORY;
466 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
467 (*ids)[*num], len);
469 (*num)++;
473 close(mixer_fd);
475 *def_index = get_default_index(flow, *keys, *num);
477 return S_OK;
480 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
481 EDataFlow dataflow, IAudioClient **out)
483 ACImpl *This;
485 TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
487 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
488 if(!This)
489 return E_OUTOFMEMORY;
491 if(dataflow == eRender)
492 This->fd = open(devnode, O_WRONLY, 0);
493 else if(dataflow == eCapture)
494 This->fd = open(devnode, O_RDONLY, 0);
495 else{
496 HeapFree(GetProcessHeap(), 0, This);
497 return E_INVALIDARG;
499 if(This->fd < 0){
500 ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
501 strerror(errno));
502 HeapFree(GetProcessHeap(), 0, This);
503 return AUDCLNT_E_DEVICE_INVALIDATED;
506 This->dataflow = dataflow;
508 This->ai.dev = -1;
509 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
510 ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
511 errno, strerror(errno));
512 close(This->fd);
513 HeapFree(GetProcessHeap(), 0, This);
514 return E_FAIL;
517 strcpy(This->devnode, devnode);
519 TRACE("OSS audioinfo:\n");
520 TRACE("devnode: %s\n", This->ai.devnode);
521 TRACE("name: %s\n", This->ai.name);
522 TRACE("busy: %x\n", This->ai.busy);
523 TRACE("caps: %x\n", This->ai.caps);
524 TRACE("iformats: %x\n", This->ai.iformats);
525 TRACE("oformats: %x\n", This->ai.oformats);
526 TRACE("enabled: %d\n", This->ai.enabled);
527 TRACE("min_rate: %d\n", This->ai.min_rate);
528 TRACE("max_rate: %d\n", This->ai.max_rate);
529 TRACE("min_channels: %d\n", This->ai.min_channels);
530 TRACE("max_channels: %d\n", This->ai.max_channels);
532 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
533 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
534 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
535 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
536 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
537 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
539 InitializeCriticalSection(&This->lock);
540 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
542 This->parent = dev;
543 IMMDevice_AddRef(This->parent);
545 IAudioClient_AddRef(&This->IAudioClient_iface);
547 *out = &This->IAudioClient_iface;
549 return S_OK;
552 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
553 REFIID riid, void **ppv)
555 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
557 if(!ppv)
558 return E_POINTER;
559 *ppv = NULL;
560 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
561 *ppv = iface;
562 if(*ppv){
563 IUnknown_AddRef((IUnknown*)*ppv);
564 return S_OK;
566 WARN("Unknown interface %s\n", debugstr_guid(riid));
567 return E_NOINTERFACE;
570 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
572 ACImpl *This = impl_from_IAudioClient(iface);
573 ULONG ref;
574 ref = InterlockedIncrement(&This->ref);
575 TRACE("(%p) Refcount now %u\n", This, ref);
576 return ref;
579 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
581 ACImpl *This = impl_from_IAudioClient(iface);
582 ULONG ref;
583 ref = InterlockedDecrement(&This->ref);
584 TRACE("(%p) Refcount now %u\n", This, ref);
585 if(!ref){
586 IAudioClient_Stop(iface);
587 IMMDevice_Release(This->parent);
588 This->lock.DebugInfo->Spare[0] = 0;
589 DeleteCriticalSection(&This->lock);
590 close(This->fd);
591 if(This->initted){
592 EnterCriticalSection(&g_sessions_lock);
593 list_remove(&This->entry);
594 LeaveCriticalSection(&g_sessions_lock);
596 HeapFree(GetProcessHeap(), 0, This->vols);
597 HeapFree(GetProcessHeap(), 0, This->local_buffer);
598 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
599 CoTaskMemFree(This->fmt);
600 HeapFree(GetProcessHeap(), 0, This);
602 return ref;
605 static void dump_fmt(const WAVEFORMATEX *fmt)
607 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
608 switch(fmt->wFormatTag){
609 case WAVE_FORMAT_PCM:
610 TRACE("WAVE_FORMAT_PCM");
611 break;
612 case WAVE_FORMAT_IEEE_FLOAT:
613 TRACE("WAVE_FORMAT_IEEE_FLOAT");
614 break;
615 case WAVE_FORMAT_EXTENSIBLE:
616 TRACE("WAVE_FORMAT_EXTENSIBLE");
617 break;
618 default:
619 TRACE("Unknown");
620 break;
622 TRACE(")\n");
624 TRACE("nChannels: %u\n", fmt->nChannels);
625 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
626 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
627 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
628 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
629 TRACE("cbSize: %u\n", fmt->cbSize);
631 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
632 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
633 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
634 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
635 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
639 static DWORD get_channel_mask(unsigned int channels)
641 switch(channels){
642 case 0:
643 return 0;
644 case 1:
645 return KSAUDIO_SPEAKER_MONO;
646 case 2:
647 return KSAUDIO_SPEAKER_STEREO;
648 case 3:
649 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
650 case 4:
651 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
652 case 5:
653 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
654 case 6:
655 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
656 case 7:
657 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
658 case 8:
659 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
661 FIXME("Unknown speaker configuration: %u\n", channels);
662 return 0;
665 static int get_oss_format(const WAVEFORMATEX *fmt)
667 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
669 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
670 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
671 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
672 switch(fmt->wBitsPerSample){
673 case 8:
674 return AFMT_U8;
675 case 16:
676 return AFMT_S16_LE;
677 case 24:
678 return AFMT_S24_LE;
679 case 32:
680 return AFMT_S32_LE;
682 return -1;
685 #ifdef AFMT_FLOAT
686 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
687 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
688 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
689 if(fmt->wBitsPerSample != 32)
690 return -1;
692 return AFMT_FLOAT;
694 #endif
696 return -1;
699 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
701 WAVEFORMATEX *ret;
702 size_t size;
704 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
705 size = sizeof(WAVEFORMATEXTENSIBLE);
706 else
707 size = sizeof(WAVEFORMATEX);
709 ret = CoTaskMemAlloc(size);
710 if(!ret)
711 return NULL;
713 memcpy(ret, fmt, size);
715 ret->cbSize = size - sizeof(WAVEFORMATEX);
717 return ret;
720 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
721 WAVEFORMATEX **out, BOOL query)
723 int tmp, oss_format;
724 double tenth;
725 HRESULT ret = S_OK;
726 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
727 WAVEFORMATEX *closest = NULL;
729 tmp = oss_format = get_oss_format(fmt);
730 if(oss_format < 0)
731 return AUDCLNT_E_UNSUPPORTED_FORMAT;
732 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
733 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
734 return E_FAIL;
736 if(tmp != oss_format){
737 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
738 return AUDCLNT_E_UNSUPPORTED_FORMAT;
741 closest = clone_format(fmt);
742 if(!closest)
743 return E_OUTOFMEMORY;
745 tmp = fmt->nSamplesPerSec;
746 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
747 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
748 CoTaskMemFree(closest);
749 return E_FAIL;
751 tenth = fmt->nSamplesPerSec * 0.1;
752 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
753 ret = S_FALSE;
754 closest->nSamplesPerSec = tmp;
757 tmp = fmt->nChannels;
758 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
759 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
760 CoTaskMemFree(closest);
761 return E_FAIL;
763 if(tmp != fmt->nChannels){
764 ret = S_FALSE;
765 closest->nChannels = tmp;
768 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
769 DWORD mask = get_channel_mask(closest->nChannels);
771 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
773 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
774 fmtex->dwChannelMask != 0 &&
775 fmtex->dwChannelMask != mask)
776 ret = S_FALSE;
779 if(ret == S_FALSE && !out)
780 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
782 if(ret == S_FALSE && out){
783 closest->nBlockAlign =
784 closest->nChannels * closest->wBitsPerSample / 8;
785 closest->nAvgBytesPerSec =
786 closest->nBlockAlign * closest->nSamplesPerSec;
787 *out = closest;
788 } else
789 CoTaskMemFree(closest);
791 TRACE("returning: %08x\n", ret);
792 return ret;
795 static void session_init_vols(AudioSession *session, UINT channels)
797 if(session->channel_count < channels){
798 UINT i;
800 if(session->channel_vols)
801 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
802 session->channel_vols, sizeof(float) * channels);
803 else
804 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
805 sizeof(float) * channels);
806 if(!session->channel_vols)
807 return;
809 for(i = session->channel_count; i < channels; ++i)
810 session->channel_vols[i] = 1.f;
812 session->channel_count = channels;
816 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
817 UINT num_channels)
819 AudioSession *ret;
821 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
822 if(!ret)
823 return NULL;
825 memcpy(&ret->guid, guid, sizeof(GUID));
827 ret->device = device;
829 list_init(&ret->clients);
831 list_add_head(&g_sessions, &ret->entry);
833 InitializeCriticalSection(&ret->lock);
834 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
836 session_init_vols(ret, num_channels);
838 ret->master_vol = 1.f;
840 return ret;
843 /* if channels == 0, then this will return or create a session with
844 * matching dataflow and GUID. otherwise, channels must also match */
845 static HRESULT get_audio_session(const GUID *sessionguid,
846 IMMDevice *device, UINT channels, AudioSession **out)
848 AudioSession *session;
850 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
851 *out = create_session(&GUID_NULL, device, channels);
852 if(!*out)
853 return E_OUTOFMEMORY;
855 return S_OK;
858 *out = NULL;
859 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
860 if(session->device == device &&
861 IsEqualGUID(sessionguid, &session->guid)){
862 session_init_vols(session, channels);
863 *out = session;
864 break;
868 if(!*out){
869 *out = create_session(sessionguid, device, channels);
870 if(!*out)
871 return E_OUTOFMEMORY;
874 return S_OK;
877 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
878 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
879 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
880 const GUID *sessionguid)
882 ACImpl *This = impl_from_IAudioClient(iface);
883 int mask, i;
884 HRESULT hr;
886 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
887 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
889 if(!fmt)
890 return E_POINTER;
892 dump_fmt(fmt);
894 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
895 return AUDCLNT_E_NOT_INITIALIZED;
897 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
898 AUDCLNT_STREAMFLAGS_LOOPBACK |
899 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
900 AUDCLNT_STREAMFLAGS_NOPERSIST |
901 AUDCLNT_STREAMFLAGS_RATEADJUST |
902 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
903 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
904 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
905 TRACE("Unknown flags: %08x\n", flags);
906 return E_INVALIDARG;
909 if(mode == AUDCLNT_SHAREMODE_SHARED){
910 period = DefaultPeriod;
911 if( duration < 3 * period)
912 duration = 3 * period;
913 }else{
914 if(!period)
915 period = DefaultPeriod; /* not minimum */
916 if(period < MinimumPeriod || period > 5000000)
917 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
918 if(duration > 20000000) /* the smaller the period, the lower this limit */
919 return AUDCLNT_E_BUFFER_SIZE_ERROR;
920 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
921 if(duration != period)
922 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
923 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
924 return AUDCLNT_E_DEVICE_IN_USE;
925 }else{
926 if( duration < 8 * period)
927 duration = 8 * period; /* may grow above 2s */
931 EnterCriticalSection(&This->lock);
933 if(This->initted){
934 LeaveCriticalSection(&This->lock);
935 return AUDCLNT_E_ALREADY_INITIALIZED;
938 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
939 if(FAILED(hr)){
940 LeaveCriticalSection(&This->lock);
941 return hr;
944 mask = 0;
945 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
946 LeaveCriticalSection(&This->lock);
947 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
948 return E_FAIL;
951 This->fmt = clone_format(fmt);
952 if(!This->fmt){
953 LeaveCriticalSection(&This->lock);
954 return E_OUTOFMEMORY;
957 This->period_us = period / 10;
959 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
960 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
961 This->bufsize_frames * fmt->nBlockAlign);
962 if(!This->local_buffer){
963 CoTaskMemFree(This->fmt);
964 This->fmt = NULL;
965 LeaveCriticalSection(&This->lock);
966 return E_OUTOFMEMORY;
969 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
970 if(!This->vols){
971 CoTaskMemFree(This->fmt);
972 This->fmt = NULL;
973 LeaveCriticalSection(&This->lock);
974 return E_OUTOFMEMORY;
977 for(i = 0; i < fmt->nChannels; ++i)
978 This->vols[i] = 1.f;
980 This->share = mode;
981 This->flags = flags;
983 EnterCriticalSection(&g_sessions_lock);
985 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
986 &This->session);
987 if(FAILED(hr)){
988 LeaveCriticalSection(&g_sessions_lock);
989 HeapFree(GetProcessHeap(), 0, This->vols);
990 This->vols = NULL;
991 CoTaskMemFree(This->fmt);
992 This->fmt = NULL;
993 LeaveCriticalSection(&This->lock);
994 return hr;
997 list_add_tail(&This->session->clients, &This->entry);
999 LeaveCriticalSection(&g_sessions_lock);
1001 This->initted = TRUE;
1003 LeaveCriticalSection(&This->lock);
1005 return S_OK;
1008 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1009 UINT32 *frames)
1011 ACImpl *This = impl_from_IAudioClient(iface);
1013 TRACE("(%p)->(%p)\n", This, frames);
1015 if(!frames)
1016 return E_POINTER;
1018 EnterCriticalSection(&This->lock);
1020 if(!This->initted){
1021 LeaveCriticalSection(&This->lock);
1022 return AUDCLNT_E_NOT_INITIALIZED;
1025 *frames = This->bufsize_frames;
1027 LeaveCriticalSection(&This->lock);
1029 return S_OK;
1032 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1033 REFERENCE_TIME *latency)
1035 ACImpl *This = impl_from_IAudioClient(iface);
1037 TRACE("(%p)->(%p)\n", This, latency);
1039 if(!latency)
1040 return E_POINTER;
1042 EnterCriticalSection(&This->lock);
1044 if(!This->initted){
1045 LeaveCriticalSection(&This->lock);
1046 return AUDCLNT_E_NOT_INITIALIZED;
1049 /* pretend we process audio in Period chunks, so max latency includes
1050 * the period time. Some native machines add .6666ms in shared mode. */
1051 *latency = This->period_us * 10 + 6666;
1053 LeaveCriticalSection(&This->lock);
1055 return S_OK;
1058 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1059 UINT32 *numpad)
1061 ACImpl *This = impl_from_IAudioClient(iface);
1062 audio_buf_info bi;
1064 TRACE("(%p)->(%p)\n", This, numpad);
1066 if(!numpad)
1067 return E_POINTER;
1069 EnterCriticalSection(&This->lock);
1071 if(!This->initted){
1072 LeaveCriticalSection(&This->lock);
1073 return AUDCLNT_E_NOT_INITIALIZED;
1076 if(This->dataflow == eRender){
1077 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1078 LeaveCriticalSection(&This->lock);
1079 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1080 return E_FAIL;
1083 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1084 This->fmt->nBlockAlign;
1086 /* when the OSS buffer has less than one fragment of data, including
1087 * no data, it often reports it as some non-zero portion of a
1088 * fragment. when it has more than one fragment of data, it reports
1089 * it as some multiple of that portion of the fragment size.
1091 * so, we have to do some ugly workarounds to report the timing
1092 * as accurately as possible */
1093 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1094 *numpad = This->inbuf_frames;
1095 This->inbuf_frames = 0;
1096 }else{
1097 if(*numpad < This->inbuf_frames)
1098 This->inbuf_frames = *numpad;
1099 else
1100 *numpad = This->inbuf_frames;
1102 }else if(This->dataflow == eCapture){
1103 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1104 LeaveCriticalSection(&This->lock);
1105 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1106 return E_FAIL;
1109 if(bi.bytes <= bi.fragsize)
1110 *numpad = 0;
1111 else
1112 *numpad = bi.bytes / This->fmt->nBlockAlign;
1113 }else{
1114 LeaveCriticalSection(&This->lock);
1115 return E_UNEXPECTED;
1118 *numpad += This->held_frames;
1120 LeaveCriticalSection(&This->lock);
1122 return S_OK;
1125 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1126 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1127 WAVEFORMATEX **outpwfx)
1129 ACImpl *This = impl_from_IAudioClient(iface);
1130 int fd = -1;
1131 HRESULT ret;
1133 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1135 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1136 return E_POINTER;
1138 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1139 return E_INVALIDARG;
1141 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1142 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1143 return E_INVALIDARG;
1145 dump_fmt(pwfx);
1147 if(outpwfx){
1148 *outpwfx = NULL;
1149 if(mode != AUDCLNT_SHAREMODE_SHARED)
1150 outpwfx = NULL;
1153 if(This->dataflow == eRender)
1154 fd = open(This->devnode, O_WRONLY, 0);
1155 else if(This->dataflow == eCapture)
1156 fd = open(This->devnode, O_RDONLY, 0);
1158 if(fd < 0){
1159 ERR("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1160 strerror(errno));
1161 return AUDCLNT_E_DEVICE_INVALIDATED;
1164 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1166 close(fd);
1168 return ret;
1171 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1172 WAVEFORMATEX **pwfx)
1174 ACImpl *This = impl_from_IAudioClient(iface);
1175 WAVEFORMATEXTENSIBLE *fmt;
1176 int formats;
1178 TRACE("(%p)->(%p)\n", This, pwfx);
1180 if(!pwfx)
1181 return E_POINTER;
1182 *pwfx = NULL;
1184 if(This->dataflow == eRender)
1185 formats = This->ai.oformats;
1186 else if(This->dataflow == eCapture)
1187 formats = This->ai.iformats;
1188 else
1189 return E_UNEXPECTED;
1191 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1192 if(!fmt)
1193 return E_OUTOFMEMORY;
1195 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1196 if(formats & AFMT_S16_LE){
1197 fmt->Format.wBitsPerSample = 16;
1198 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1199 #ifdef AFMT_FLOAT
1200 }else if(formats & AFMT_FLOAT){
1201 fmt->Format.wBitsPerSample = 32;
1202 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1203 #endif
1204 }else if(formats & AFMT_U8){
1205 fmt->Format.wBitsPerSample = 8;
1206 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1207 }else if(formats & AFMT_S32_LE){
1208 fmt->Format.wBitsPerSample = 32;
1209 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1210 }else if(formats & AFMT_S24_LE){
1211 fmt->Format.wBitsPerSample = 24;
1212 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1213 }else{
1214 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1215 CoTaskMemFree(fmt);
1216 return E_FAIL;
1219 fmt->Format.nChannels = This->ai.max_channels;
1220 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1221 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1223 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1224 fmt->Format.nChannels) / 8;
1225 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1226 fmt->Format.nBlockAlign;
1228 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1229 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1231 *pwfx = (WAVEFORMATEX*)fmt;
1232 dump_fmt(*pwfx);
1234 return S_OK;
1237 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1238 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1240 ACImpl *This = impl_from_IAudioClient(iface);
1242 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1244 if(!defperiod && !minperiod)
1245 return E_POINTER;
1247 if(defperiod)
1248 *defperiod = DefaultPeriod;
1249 if(minperiod)
1250 *minperiod = MinimumPeriod;
1252 return S_OK;
1255 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1257 if(This->fmt->wBitsPerSample == 8)
1258 memset(buf, 128, frames * This->fmt->nBlockAlign);
1259 else
1260 memset(buf, 0, frames * This->fmt->nBlockAlign);
1263 static void oss_write_data(ACImpl *This)
1265 ssize_t written_bytes;
1266 UINT32 written_frames;
1267 size_t to_write_frames, to_write_bytes;
1268 audio_buf_info bi;
1269 BYTE *buf =
1270 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1272 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1273 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1274 else
1275 to_write_frames = This->held_frames;
1276 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1278 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1279 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1280 return;
1283 if(bi.bytes < to_write_bytes){
1284 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1285 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1288 if(This->session->mute)
1289 oss_silence_buffer(This, buf, to_write_frames);
1291 written_bytes = write(This->fd, buf, to_write_bytes);
1292 if(written_bytes < 0){
1293 /* EAGAIN is OSS buffer full, log that too */
1294 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1295 return;
1297 written_frames = written_bytes / This->fmt->nBlockAlign;
1299 This->lcl_offs_frames += written_frames;
1300 This->lcl_offs_frames %= This->bufsize_frames;
1301 This->held_frames -= written_frames;
1302 This->inbuf_frames += written_frames;
1304 if(written_frames < to_write_frames){
1305 /* OSS buffer probably full */
1306 return;
1309 bi.bytes -= written_bytes;
1310 if(This->held_frames && bi.bytes >= This->fmt->nBlockAlign){
1311 /* wrapped and have some data back at the start to write */
1313 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1314 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1316 if(This->session->mute)
1317 oss_silence_buffer(This, This->local_buffer, to_write_frames);
1319 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1320 if(written_bytes < 0){
1321 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1322 return;
1324 written_frames = written_bytes / This->fmt->nBlockAlign;
1326 This->lcl_offs_frames += written_frames;
1327 This->lcl_offs_frames %= This->bufsize_frames;
1328 This->held_frames -= written_frames;
1329 This->inbuf_frames += written_frames;
1333 static void oss_read_data(ACImpl *This)
1335 UINT64 pos, readable;
1336 audio_buf_info bi;
1337 ssize_t nread;
1339 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1340 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1341 return;
1344 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1345 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1347 if(bi.bytes < readable)
1348 readable = bi.bytes;
1350 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1351 readable);
1352 if(nread < 0){
1353 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1354 return;
1357 This->held_frames += nread / This->fmt->nBlockAlign;
1359 if(This->held_frames > This->bufsize_frames){
1360 WARN("Overflow of unread data\n");
1361 This->lcl_offs_frames += This->held_frames;
1362 This->lcl_offs_frames %= This->bufsize_frames;
1363 This->held_frames = This->bufsize_frames;
1367 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1369 ACImpl *This = user;
1371 EnterCriticalSection(&This->lock);
1373 if(This->dataflow == eRender && This->held_frames)
1374 oss_write_data(This);
1375 else if(This->dataflow == eCapture)
1376 oss_read_data(This);
1378 if(This->event)
1379 SetEvent(This->event);
1381 LeaveCriticalSection(&This->lock);
1384 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1386 ACImpl *This = impl_from_IAudioClient(iface);
1387 int mask;
1389 TRACE("(%p)\n", This);
1391 EnterCriticalSection(&This->lock);
1393 if(!This->initted){
1394 LeaveCriticalSection(&This->lock);
1395 return AUDCLNT_E_NOT_INITIALIZED;
1398 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1399 LeaveCriticalSection(&This->lock);
1400 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1403 if(This->playing){
1404 LeaveCriticalSection(&This->lock);
1405 return AUDCLNT_E_NOT_STOPPED;
1408 if(This->dataflow == eRender)
1409 mask = PCM_ENABLE_OUTPUT;
1410 else if(This->dataflow == eCapture)
1411 mask = PCM_ENABLE_INPUT;
1412 else{
1413 LeaveCriticalSection(&This->lock);
1414 return E_UNEXPECTED;
1417 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1418 LeaveCriticalSection(&This->lock);
1419 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1420 return E_FAIL;
1423 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1424 oss_period_callback, This, 0, This->period_us / 1000,
1425 WT_EXECUTEINTIMERTHREAD))
1426 ERR("Unable to create period timer: %u\n", GetLastError());
1428 This->playing = TRUE;
1430 LeaveCriticalSection(&This->lock);
1432 return S_OK;
1435 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1437 ACImpl *This = impl_from_IAudioClient(iface);
1438 int mask;
1440 TRACE("(%p)\n", This);
1442 EnterCriticalSection(&This->lock);
1444 if(!This->initted){
1445 LeaveCriticalSection(&This->lock);
1446 return AUDCLNT_E_NOT_INITIALIZED;
1449 if(!This->playing){
1450 LeaveCriticalSection(&This->lock);
1451 return S_FALSE;
1454 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1455 DeleteTimerQueueTimer(g_timer_q, This->timer,
1456 INVALID_HANDLE_VALUE);
1457 This->timer = NULL;
1460 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1461 LeaveCriticalSection(&This->lock);
1462 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1463 return E_FAIL;
1466 mask = 0;
1467 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1468 LeaveCriticalSection(&This->lock);
1469 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1470 return E_FAIL;
1473 This->playing = FALSE;
1475 LeaveCriticalSection(&This->lock);
1477 return S_OK;
1480 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1482 ACImpl *This = impl_from_IAudioClient(iface);
1484 TRACE("(%p)\n", This);
1486 EnterCriticalSection(&This->lock);
1488 if(!This->initted){
1489 LeaveCriticalSection(&This->lock);
1490 return AUDCLNT_E_NOT_INITIALIZED;
1493 if(This->playing){
1494 LeaveCriticalSection(&This->lock);
1495 return AUDCLNT_E_NOT_STOPPED;
1498 if(This->buf_state != NOT_LOCKED){
1499 LeaveCriticalSection(&This->lock);
1500 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1503 This->written_frames = 0;
1504 This->inbuf_frames = 0;
1505 This->held_frames = 0;
1507 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1508 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1510 LeaveCriticalSection(&This->lock);
1512 return S_OK;
1515 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1516 HANDLE event)
1518 ACImpl *This = impl_from_IAudioClient(iface);
1520 TRACE("(%p)->(%p)\n", This, event);
1522 if(!event)
1523 return E_INVALIDARG;
1525 EnterCriticalSection(&This->lock);
1527 if(!This->initted){
1528 LeaveCriticalSection(&This->lock);
1529 return AUDCLNT_E_NOT_INITIALIZED;
1532 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1533 LeaveCriticalSection(&This->lock);
1534 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1537 This->event = event;
1539 LeaveCriticalSection(&This->lock);
1541 return S_OK;
1544 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1545 void **ppv)
1547 ACImpl *This = impl_from_IAudioClient(iface);
1549 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1551 if(!ppv)
1552 return E_POINTER;
1553 *ppv = NULL;
1555 EnterCriticalSection(&This->lock);
1557 if(!This->initted){
1558 LeaveCriticalSection(&This->lock);
1559 return AUDCLNT_E_NOT_INITIALIZED;
1562 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1563 if(This->dataflow != eRender){
1564 LeaveCriticalSection(&This->lock);
1565 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1567 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1568 *ppv = &This->IAudioRenderClient_iface;
1569 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1570 if(This->dataflow != eCapture){
1571 LeaveCriticalSection(&This->lock);
1572 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1574 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1575 *ppv = &This->IAudioCaptureClient_iface;
1576 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1577 IAudioClock_AddRef(&This->IAudioClock_iface);
1578 *ppv = &This->IAudioClock_iface;
1579 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1580 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1581 *ppv = &This->IAudioStreamVolume_iface;
1582 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1583 if(!This->session_wrapper){
1584 This->session_wrapper = AudioSessionWrapper_Create(This);
1585 if(!This->session_wrapper){
1586 LeaveCriticalSection(&This->lock);
1587 return E_OUTOFMEMORY;
1589 }else
1590 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1592 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1593 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1594 if(!This->session_wrapper){
1595 This->session_wrapper = AudioSessionWrapper_Create(This);
1596 if(!This->session_wrapper){
1597 LeaveCriticalSection(&This->lock);
1598 return E_OUTOFMEMORY;
1600 }else
1601 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1603 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1604 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1605 if(!This->session_wrapper){
1606 This->session_wrapper = AudioSessionWrapper_Create(This);
1607 if(!This->session_wrapper){
1608 LeaveCriticalSection(&This->lock);
1609 return E_OUTOFMEMORY;
1611 }else
1612 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1614 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1617 if(*ppv){
1618 LeaveCriticalSection(&This->lock);
1619 return S_OK;
1622 LeaveCriticalSection(&This->lock);
1624 FIXME("stub %s\n", debugstr_guid(riid));
1625 return E_NOINTERFACE;
1628 static const IAudioClientVtbl AudioClient_Vtbl =
1630 AudioClient_QueryInterface,
1631 AudioClient_AddRef,
1632 AudioClient_Release,
1633 AudioClient_Initialize,
1634 AudioClient_GetBufferSize,
1635 AudioClient_GetStreamLatency,
1636 AudioClient_GetCurrentPadding,
1637 AudioClient_IsFormatSupported,
1638 AudioClient_GetMixFormat,
1639 AudioClient_GetDevicePeriod,
1640 AudioClient_Start,
1641 AudioClient_Stop,
1642 AudioClient_Reset,
1643 AudioClient_SetEventHandle,
1644 AudioClient_GetService
1647 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1648 IAudioRenderClient *iface, REFIID riid, void **ppv)
1650 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1652 if(!ppv)
1653 return E_POINTER;
1654 *ppv = NULL;
1656 if(IsEqualIID(riid, &IID_IUnknown) ||
1657 IsEqualIID(riid, &IID_IAudioRenderClient))
1658 *ppv = iface;
1659 if(*ppv){
1660 IUnknown_AddRef((IUnknown*)*ppv);
1661 return S_OK;
1664 WARN("Unknown interface %s\n", debugstr_guid(riid));
1665 return E_NOINTERFACE;
1668 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1670 ACImpl *This = impl_from_IAudioRenderClient(iface);
1671 return AudioClient_AddRef(&This->IAudioClient_iface);
1674 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1676 ACImpl *This = impl_from_IAudioRenderClient(iface);
1677 return AudioClient_Release(&This->IAudioClient_iface);
1680 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1681 UINT32 frames, BYTE **data)
1683 ACImpl *This = impl_from_IAudioRenderClient(iface);
1684 UINT32 pad, write_pos;
1685 HRESULT hr;
1687 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1689 if(!data)
1690 return E_POINTER;
1692 EnterCriticalSection(&This->lock);
1694 if(This->buf_state != NOT_LOCKED){
1695 LeaveCriticalSection(&This->lock);
1696 return AUDCLNT_E_OUT_OF_ORDER;
1699 if(!frames){
1700 This->buf_state = LOCKED_NORMAL;
1701 LeaveCriticalSection(&This->lock);
1702 return S_OK;
1705 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1706 if(FAILED(hr)){
1707 LeaveCriticalSection(&This->lock);
1708 return hr;
1711 if(pad + frames > This->bufsize_frames){
1712 LeaveCriticalSection(&This->lock);
1713 return AUDCLNT_E_BUFFER_TOO_LARGE;
1716 write_pos =
1717 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1718 if(write_pos + frames > This->bufsize_frames){
1719 if(This->tmp_buffer_frames < frames){
1720 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1721 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1722 frames * This->fmt->nBlockAlign);
1723 if(!This->tmp_buffer){
1724 LeaveCriticalSection(&This->lock);
1725 return E_OUTOFMEMORY;
1727 This->tmp_buffer_frames = frames;
1729 *data = This->tmp_buffer;
1730 This->buf_state = LOCKED_WRAPPED;
1731 }else{
1732 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1733 This->buf_state = LOCKED_NORMAL;
1736 LeaveCriticalSection(&This->lock);
1738 return S_OK;
1741 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1743 UINT32 write_offs_frames =
1744 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1745 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1746 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1747 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1748 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1750 if(written_bytes <= chunk_bytes){
1751 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1752 }else{
1753 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1754 memcpy(This->local_buffer, buffer + chunk_bytes,
1755 written_bytes - chunk_bytes);
1759 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1760 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1762 ACImpl *This = impl_from_IAudioRenderClient(iface);
1763 BYTE *buffer;
1765 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1767 EnterCriticalSection(&This->lock);
1769 if(This->buf_state == NOT_LOCKED || !written_frames){
1770 This->buf_state = NOT_LOCKED;
1771 LeaveCriticalSection(&This->lock);
1772 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1775 if(This->buf_state == LOCKED_NORMAL)
1776 buffer = This->local_buffer + This->fmt->nBlockAlign *
1777 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1778 else
1779 buffer = This->tmp_buffer;
1781 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1782 oss_silence_buffer(This, buffer, written_frames);
1784 if(This->held_frames){
1785 if(This->buf_state == LOCKED_WRAPPED)
1786 oss_wrap_buffer(This, buffer, written_frames);
1788 This->held_frames += written_frames;
1789 }else{
1790 ssize_t w_bytes;
1791 UINT32 w_frames;
1793 if(This->session->mute)
1794 oss_silence_buffer(This, buffer, written_frames);
1796 w_bytes = write(This->fd, buffer,
1797 written_frames * This->fmt->nBlockAlign);
1798 if(w_bytes < 0){
1799 if(errno != EAGAIN){
1800 This->buf_state = NOT_LOCKED;
1801 LeaveCriticalSection(&This->lock);
1802 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1803 return E_FAIL;
1804 }else /* OSS buffer full */
1805 w_bytes = 0;
1807 w_frames = w_bytes / This->fmt->nBlockAlign;
1808 This->inbuf_frames += w_frames;
1810 if(w_frames < written_frames){
1811 if(This->buf_state == LOCKED_WRAPPED)
1812 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1813 written_frames - w_frames);
1814 else
1815 This->lcl_offs_frames += w_frames;
1816 This->held_frames = written_frames - w_frames;
1820 This->written_frames += written_frames;
1821 This->buf_state = NOT_LOCKED;
1823 LeaveCriticalSection(&This->lock);
1825 return S_OK;
1828 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1829 AudioRenderClient_QueryInterface,
1830 AudioRenderClient_AddRef,
1831 AudioRenderClient_Release,
1832 AudioRenderClient_GetBuffer,
1833 AudioRenderClient_ReleaseBuffer
1836 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1837 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1839 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1841 if(!ppv)
1842 return E_POINTER;
1843 *ppv = NULL;
1845 if(IsEqualIID(riid, &IID_IUnknown) ||
1846 IsEqualIID(riid, &IID_IAudioCaptureClient))
1847 *ppv = iface;
1848 if(*ppv){
1849 IUnknown_AddRef((IUnknown*)*ppv);
1850 return S_OK;
1853 WARN("Unknown interface %s\n", debugstr_guid(riid));
1854 return E_NOINTERFACE;
1857 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1859 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1860 return IAudioClient_AddRef(&This->IAudioClient_iface);
1863 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1865 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1866 return IAudioClient_Release(&This->IAudioClient_iface);
1869 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1870 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1871 UINT64 *qpcpos)
1873 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1874 HRESULT hr;
1876 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1877 devpos, qpcpos);
1879 if(!data || !frames || !flags)
1880 return E_POINTER;
1882 EnterCriticalSection(&This->lock);
1884 if(This->buf_state != NOT_LOCKED){
1885 LeaveCriticalSection(&This->lock);
1886 return AUDCLNT_E_OUT_OF_ORDER;
1889 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1890 if(FAILED(hr)){
1891 LeaveCriticalSection(&This->lock);
1892 return hr;
1895 *flags = 0;
1897 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1898 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1899 if(This->tmp_buffer_frames < *frames){
1900 if(This->tmp_buffer)
1901 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
1902 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1903 *frames * This->fmt->nBlockAlign);
1904 if(!This->tmp_buffer){
1905 LeaveCriticalSection(&This->lock);
1906 return E_OUTOFMEMORY;
1908 This->tmp_buffer_frames = *frames;
1911 *data = This->tmp_buffer;
1912 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1913 This->fmt->nBlockAlign;
1914 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1915 frames_bytes = *frames * This->fmt->nBlockAlign;
1916 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1917 memcpy(This->tmp_buffer, This->local_buffer,
1918 frames_bytes - chunk_bytes);
1919 }else
1920 *data = This->local_buffer +
1921 This->lcl_offs_frames * This->fmt->nBlockAlign;
1923 This->buf_state = LOCKED_NORMAL;
1925 if(devpos || qpcpos)
1926 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1928 LeaveCriticalSection(&This->lock);
1930 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1933 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1934 IAudioCaptureClient *iface, UINT32 done)
1936 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1938 TRACE("(%p)->(%u)\n", This, done);
1940 EnterCriticalSection(&This->lock);
1942 if(This->buf_state == NOT_LOCKED){
1943 LeaveCriticalSection(&This->lock);
1944 return AUDCLNT_E_OUT_OF_ORDER;
1947 This->held_frames -= done;
1948 This->lcl_offs_frames += done;
1949 This->lcl_offs_frames %= This->bufsize_frames;
1951 This->buf_state = NOT_LOCKED;
1953 LeaveCriticalSection(&This->lock);
1955 return S_OK;
1958 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1959 IAudioCaptureClient *iface, UINT32 *frames)
1961 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1963 TRACE("(%p)->(%p)\n", This, frames);
1965 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1968 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1970 AudioCaptureClient_QueryInterface,
1971 AudioCaptureClient_AddRef,
1972 AudioCaptureClient_Release,
1973 AudioCaptureClient_GetBuffer,
1974 AudioCaptureClient_ReleaseBuffer,
1975 AudioCaptureClient_GetNextPacketSize
1978 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1979 REFIID riid, void **ppv)
1981 ACImpl *This = impl_from_IAudioClock(iface);
1983 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1985 if(!ppv)
1986 return E_POINTER;
1987 *ppv = NULL;
1989 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1990 *ppv = iface;
1991 else if(IsEqualIID(riid, &IID_IAudioClock2))
1992 *ppv = &This->IAudioClock2_iface;
1993 if(*ppv){
1994 IUnknown_AddRef((IUnknown*)*ppv);
1995 return S_OK;
1998 WARN("Unknown interface %s\n", debugstr_guid(riid));
1999 return E_NOINTERFACE;
2002 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2004 ACImpl *This = impl_from_IAudioClock(iface);
2005 return IAudioClient_AddRef(&This->IAudioClient_iface);
2008 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2010 ACImpl *This = impl_from_IAudioClock(iface);
2011 return IAudioClient_Release(&This->IAudioClient_iface);
2014 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2016 ACImpl *This = impl_from_IAudioClock(iface);
2018 TRACE("(%p)->(%p)\n", This, freq);
2020 *freq = This->fmt->nSamplesPerSec;
2022 return S_OK;
2025 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2026 UINT64 *qpctime)
2028 ACImpl *This = impl_from_IAudioClock(iface);
2029 UINT32 pad;
2030 HRESULT hr;
2032 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2034 if(!pos)
2035 return E_POINTER;
2037 EnterCriticalSection(&This->lock);
2039 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2040 if(FAILED(hr)){
2041 LeaveCriticalSection(&This->lock);
2042 return hr;
2045 if(This->dataflow == eRender)
2046 *pos = This->written_frames - pad;
2047 else if(This->dataflow == eCapture)
2048 *pos = This->written_frames + pad;
2050 LeaveCriticalSection(&This->lock);
2052 if(qpctime){
2053 LARGE_INTEGER stamp, freq;
2054 QueryPerformanceCounter(&stamp);
2055 QueryPerformanceFrequency(&freq);
2056 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2059 return S_OK;
2062 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2063 DWORD *chars)
2065 ACImpl *This = impl_from_IAudioClock(iface);
2067 TRACE("(%p)->(%p)\n", This, chars);
2069 if(!chars)
2070 return E_POINTER;
2072 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2074 return S_OK;
2077 static const IAudioClockVtbl AudioClock_Vtbl =
2079 AudioClock_QueryInterface,
2080 AudioClock_AddRef,
2081 AudioClock_Release,
2082 AudioClock_GetFrequency,
2083 AudioClock_GetPosition,
2084 AudioClock_GetCharacteristics
2087 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2088 REFIID riid, void **ppv)
2090 ACImpl *This = impl_from_IAudioClock2(iface);
2091 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2094 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2096 ACImpl *This = impl_from_IAudioClock2(iface);
2097 return IAudioClient_AddRef(&This->IAudioClient_iface);
2100 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2102 ACImpl *This = impl_from_IAudioClock2(iface);
2103 return IAudioClient_Release(&This->IAudioClient_iface);
2106 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2107 UINT64 *pos, UINT64 *qpctime)
2109 ACImpl *This = impl_from_IAudioClock2(iface);
2111 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2113 return E_NOTIMPL;
2116 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2118 AudioClock2_QueryInterface,
2119 AudioClock2_AddRef,
2120 AudioClock2_Release,
2121 AudioClock2_GetDevicePosition
2124 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2126 AudioSessionWrapper *ret;
2128 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2129 sizeof(AudioSessionWrapper));
2130 if(!ret)
2131 return NULL;
2133 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2134 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2135 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2137 ret->ref = 1;
2139 ret->client = client;
2140 if(client){
2141 ret->session = client->session;
2142 AudioClient_AddRef(&client->IAudioClient_iface);
2145 return ret;
2148 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2149 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2151 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2153 if(!ppv)
2154 return E_POINTER;
2155 *ppv = NULL;
2157 if(IsEqualIID(riid, &IID_IUnknown) ||
2158 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2159 IsEqualIID(riid, &IID_IAudioSessionControl2))
2160 *ppv = iface;
2161 if(*ppv){
2162 IUnknown_AddRef((IUnknown*)*ppv);
2163 return S_OK;
2166 WARN("Unknown interface %s\n", debugstr_guid(riid));
2167 return E_NOINTERFACE;
2170 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2172 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2173 ULONG ref;
2174 ref = InterlockedIncrement(&This->ref);
2175 TRACE("(%p) Refcount now %u\n", This, ref);
2176 return ref;
2179 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2181 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2182 ULONG ref;
2183 ref = InterlockedDecrement(&This->ref);
2184 TRACE("(%p) Refcount now %u\n", This, ref);
2185 if(!ref){
2186 if(This->client){
2187 EnterCriticalSection(&This->client->lock);
2188 This->client->session_wrapper = NULL;
2189 LeaveCriticalSection(&This->client->lock);
2190 AudioClient_Release(&This->client->IAudioClient_iface);
2192 HeapFree(GetProcessHeap(), 0, This);
2194 return ref;
2197 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2198 AudioSessionState *state)
2200 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2201 ACImpl *client;
2203 TRACE("(%p)->(%p)\n", This, state);
2205 if(!state)
2206 return NULL_PTR_ERR;
2208 EnterCriticalSection(&g_sessions_lock);
2210 if(list_empty(&This->session->clients)){
2211 *state = AudioSessionStateExpired;
2212 LeaveCriticalSection(&g_sessions_lock);
2213 return S_OK;
2216 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2217 EnterCriticalSection(&client->lock);
2218 if(client->playing){
2219 *state = AudioSessionStateActive;
2220 LeaveCriticalSection(&client->lock);
2221 LeaveCriticalSection(&g_sessions_lock);
2222 return S_OK;
2224 LeaveCriticalSection(&client->lock);
2227 LeaveCriticalSection(&g_sessions_lock);
2229 *state = AudioSessionStateInactive;
2231 return S_OK;
2234 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2235 IAudioSessionControl2 *iface, WCHAR **name)
2237 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2239 FIXME("(%p)->(%p) - stub\n", This, name);
2241 return E_NOTIMPL;
2244 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2245 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2247 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2249 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2251 return E_NOTIMPL;
2254 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2255 IAudioSessionControl2 *iface, WCHAR **path)
2257 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2259 FIXME("(%p)->(%p) - stub\n", This, path);
2261 return E_NOTIMPL;
2264 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2265 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2267 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2269 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2271 return E_NOTIMPL;
2274 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2275 IAudioSessionControl2 *iface, GUID *group)
2277 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2279 FIXME("(%p)->(%p) - stub\n", This, group);
2281 return E_NOTIMPL;
2284 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2285 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2287 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2289 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2290 debugstr_guid(session));
2292 return E_NOTIMPL;
2295 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2296 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2298 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2300 FIXME("(%p)->(%p) - stub\n", This, events);
2302 return S_OK;
2305 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2306 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2308 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2310 FIXME("(%p)->(%p) - stub\n", This, events);
2312 return S_OK;
2315 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2316 IAudioSessionControl2 *iface, WCHAR **id)
2318 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2320 FIXME("(%p)->(%p) - stub\n", This, id);
2322 return E_NOTIMPL;
2325 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2326 IAudioSessionControl2 *iface, WCHAR **id)
2328 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2330 FIXME("(%p)->(%p) - stub\n", This, id);
2332 return E_NOTIMPL;
2335 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2336 IAudioSessionControl2 *iface, DWORD *pid)
2338 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2340 TRACE("(%p)->(%p)\n", This, pid);
2342 if(!pid)
2343 return E_POINTER;
2345 *pid = GetCurrentProcessId();
2347 return S_OK;
2350 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2351 IAudioSessionControl2 *iface)
2353 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2355 TRACE("(%p)\n", This);
2357 return S_FALSE;
2360 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2361 IAudioSessionControl2 *iface, BOOL optout)
2363 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2365 TRACE("(%p)->(%d)\n", This, optout);
2367 return S_OK;
2370 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2372 AudioSessionControl_QueryInterface,
2373 AudioSessionControl_AddRef,
2374 AudioSessionControl_Release,
2375 AudioSessionControl_GetState,
2376 AudioSessionControl_GetDisplayName,
2377 AudioSessionControl_SetDisplayName,
2378 AudioSessionControl_GetIconPath,
2379 AudioSessionControl_SetIconPath,
2380 AudioSessionControl_GetGroupingParam,
2381 AudioSessionControl_SetGroupingParam,
2382 AudioSessionControl_RegisterAudioSessionNotification,
2383 AudioSessionControl_UnregisterAudioSessionNotification,
2384 AudioSessionControl_GetSessionIdentifier,
2385 AudioSessionControl_GetSessionInstanceIdentifier,
2386 AudioSessionControl_GetProcessId,
2387 AudioSessionControl_IsSystemSoundsSession,
2388 AudioSessionControl_SetDuckingPreference
2391 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2392 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2394 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2396 if(!ppv)
2397 return E_POINTER;
2398 *ppv = NULL;
2400 if(IsEqualIID(riid, &IID_IUnknown) ||
2401 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2402 *ppv = iface;
2403 if(*ppv){
2404 IUnknown_AddRef((IUnknown*)*ppv);
2405 return S_OK;
2408 WARN("Unknown interface %s\n", debugstr_guid(riid));
2409 return E_NOINTERFACE;
2412 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2414 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2415 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2418 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2420 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2421 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2424 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2425 ISimpleAudioVolume *iface, float level, const GUID *context)
2427 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2428 AudioSession *session = This->session;
2430 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2432 if(level < 0.f || level > 1.f)
2433 return E_INVALIDARG;
2435 if(context)
2436 FIXME("Notifications not supported yet\n");
2438 EnterCriticalSection(&session->lock);
2440 session->master_vol = level;
2442 TRACE("OSS doesn't support setting volume\n");
2444 LeaveCriticalSection(&session->lock);
2446 return S_OK;
2449 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2450 ISimpleAudioVolume *iface, float *level)
2452 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2453 AudioSession *session = This->session;
2455 TRACE("(%p)->(%p)\n", session, level);
2457 if(!level)
2458 return NULL_PTR_ERR;
2460 *level = session->master_vol;
2462 return S_OK;
2465 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2466 BOOL mute, const GUID *context)
2468 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2469 AudioSession *session = This->session;
2471 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2473 EnterCriticalSection(&session->lock);
2475 if(!mute && session->mute){
2476 ACImpl *client;
2478 session->mute = mute;
2480 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2481 EnterCriticalSection(&client->lock);
2482 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2483 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2484 strerror(errno));
2485 oss_write_data(client);
2486 LeaveCriticalSection(&client->lock);
2488 }else
2489 session->mute = mute;
2491 LeaveCriticalSection(&session->lock);
2493 return S_OK;
2496 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2497 BOOL *mute)
2499 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2500 AudioSession *session = This->session;
2502 TRACE("(%p)->(%p)\n", session, mute);
2504 if(!mute)
2505 return NULL_PTR_ERR;
2507 *mute = This->session->mute;
2509 return S_OK;
2512 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2514 SimpleAudioVolume_QueryInterface,
2515 SimpleAudioVolume_AddRef,
2516 SimpleAudioVolume_Release,
2517 SimpleAudioVolume_SetMasterVolume,
2518 SimpleAudioVolume_GetMasterVolume,
2519 SimpleAudioVolume_SetMute,
2520 SimpleAudioVolume_GetMute
2523 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2524 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2526 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2528 if(!ppv)
2529 return E_POINTER;
2530 *ppv = NULL;
2532 if(IsEqualIID(riid, &IID_IUnknown) ||
2533 IsEqualIID(riid, &IID_IAudioStreamVolume))
2534 *ppv = iface;
2535 if(*ppv){
2536 IUnknown_AddRef((IUnknown*)*ppv);
2537 return S_OK;
2540 WARN("Unknown interface %s\n", debugstr_guid(riid));
2541 return E_NOINTERFACE;
2544 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2546 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2547 return IAudioClient_AddRef(&This->IAudioClient_iface);
2550 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2552 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2553 return IAudioClient_Release(&This->IAudioClient_iface);
2556 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2557 IAudioStreamVolume *iface, UINT32 *out)
2559 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2561 TRACE("(%p)->(%p)\n", This, out);
2563 if(!out)
2564 return E_POINTER;
2566 *out = This->fmt->nChannels;
2568 return S_OK;
2571 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2572 IAudioStreamVolume *iface, UINT32 index, float level)
2574 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2576 TRACE("(%p)->(%d, %f)\n", This, index, level);
2578 if(level < 0.f || level > 1.f)
2579 return E_INVALIDARG;
2581 if(index >= This->fmt->nChannels)
2582 return E_INVALIDARG;
2584 EnterCriticalSection(&This->lock);
2586 This->vols[index] = level;
2588 TRACE("OSS doesn't support setting volume\n");
2590 LeaveCriticalSection(&This->lock);
2592 return S_OK;
2595 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2596 IAudioStreamVolume *iface, UINT32 index, float *level)
2598 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2600 TRACE("(%p)->(%d, %p)\n", This, index, level);
2602 if(!level)
2603 return E_POINTER;
2605 if(index >= This->fmt->nChannels)
2606 return E_INVALIDARG;
2608 *level = This->vols[index];
2610 return S_OK;
2613 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2614 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2616 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2617 int i;
2619 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2621 if(!levels)
2622 return E_POINTER;
2624 if(count != This->fmt->nChannels)
2625 return E_INVALIDARG;
2627 EnterCriticalSection(&This->lock);
2629 for(i = 0; i < count; ++i)
2630 This->vols[i] = levels[i];
2632 TRACE("OSS doesn't support setting volume\n");
2634 LeaveCriticalSection(&This->lock);
2636 return S_OK;
2639 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2640 IAudioStreamVolume *iface, UINT32 count, float *levels)
2642 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2643 int i;
2645 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2647 if(!levels)
2648 return E_POINTER;
2650 if(count != This->fmt->nChannels)
2651 return E_INVALIDARG;
2653 EnterCriticalSection(&This->lock);
2655 for(i = 0; i < count; ++i)
2656 levels[i] = This->vols[i];
2658 LeaveCriticalSection(&This->lock);
2660 return S_OK;
2663 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2665 AudioStreamVolume_QueryInterface,
2666 AudioStreamVolume_AddRef,
2667 AudioStreamVolume_Release,
2668 AudioStreamVolume_GetChannelCount,
2669 AudioStreamVolume_SetChannelVolume,
2670 AudioStreamVolume_GetChannelVolume,
2671 AudioStreamVolume_SetAllVolumes,
2672 AudioStreamVolume_GetAllVolumes
2675 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2676 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2678 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2680 if(!ppv)
2681 return E_POINTER;
2682 *ppv = NULL;
2684 if(IsEqualIID(riid, &IID_IUnknown) ||
2685 IsEqualIID(riid, &IID_IChannelAudioVolume))
2686 *ppv = iface;
2687 if(*ppv){
2688 IUnknown_AddRef((IUnknown*)*ppv);
2689 return S_OK;
2692 WARN("Unknown interface %s\n", debugstr_guid(riid));
2693 return E_NOINTERFACE;
2696 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2698 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2699 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2702 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2704 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2705 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2708 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2709 IChannelAudioVolume *iface, UINT32 *out)
2711 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2712 AudioSession *session = This->session;
2714 TRACE("(%p)->(%p)\n", session, out);
2716 if(!out)
2717 return NULL_PTR_ERR;
2719 *out = session->channel_count;
2721 return S_OK;
2724 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2725 IChannelAudioVolume *iface, UINT32 index, float level,
2726 const GUID *context)
2728 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2729 AudioSession *session = This->session;
2731 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2732 wine_dbgstr_guid(context));
2734 if(level < 0.f || level > 1.f)
2735 return E_INVALIDARG;
2737 if(index >= session->channel_count)
2738 return E_INVALIDARG;
2740 if(context)
2741 FIXME("Notifications not supported yet\n");
2743 EnterCriticalSection(&session->lock);
2745 session->channel_vols[index] = level;
2747 TRACE("OSS doesn't support setting volume\n");
2749 LeaveCriticalSection(&session->lock);
2751 return S_OK;
2754 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2755 IChannelAudioVolume *iface, UINT32 index, float *level)
2757 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2758 AudioSession *session = This->session;
2760 TRACE("(%p)->(%d, %p)\n", session, index, level);
2762 if(!level)
2763 return NULL_PTR_ERR;
2765 if(index >= session->channel_count)
2766 return E_INVALIDARG;
2768 *level = session->channel_vols[index];
2770 return S_OK;
2773 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2774 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2775 const GUID *context)
2777 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2778 AudioSession *session = This->session;
2779 int i;
2781 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2782 wine_dbgstr_guid(context));
2784 if(!levels)
2785 return NULL_PTR_ERR;
2787 if(count != session->channel_count)
2788 return E_INVALIDARG;
2790 if(context)
2791 FIXME("Notifications not supported yet\n");
2793 EnterCriticalSection(&session->lock);
2795 for(i = 0; i < count; ++i)
2796 session->channel_vols[i] = levels[i];
2798 TRACE("OSS doesn't support setting volume\n");
2800 LeaveCriticalSection(&session->lock);
2802 return S_OK;
2805 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2806 IChannelAudioVolume *iface, UINT32 count, float *levels)
2808 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2809 AudioSession *session = This->session;
2810 int i;
2812 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2814 if(!levels)
2815 return NULL_PTR_ERR;
2817 if(count != session->channel_count)
2818 return E_INVALIDARG;
2820 for(i = 0; i < count; ++i)
2821 levels[i] = session->channel_vols[i];
2823 return S_OK;
2826 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2828 ChannelAudioVolume_QueryInterface,
2829 ChannelAudioVolume_AddRef,
2830 ChannelAudioVolume_Release,
2831 ChannelAudioVolume_GetChannelCount,
2832 ChannelAudioVolume_SetChannelVolume,
2833 ChannelAudioVolume_GetChannelVolume,
2834 ChannelAudioVolume_SetAllVolumes,
2835 ChannelAudioVolume_GetAllVolumes
2838 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2839 REFIID riid, void **ppv)
2841 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2843 if(!ppv)
2844 return E_POINTER;
2845 *ppv = NULL;
2847 if(IsEqualIID(riid, &IID_IUnknown) ||
2848 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2849 IsEqualIID(riid, &IID_IAudioSessionManager2))
2850 *ppv = iface;
2851 if(*ppv){
2852 IUnknown_AddRef((IUnknown*)*ppv);
2853 return S_OK;
2856 WARN("Unknown interface %s\n", debugstr_guid(riid));
2857 return E_NOINTERFACE;
2860 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2862 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2863 ULONG ref;
2864 ref = InterlockedIncrement(&This->ref);
2865 TRACE("(%p) Refcount now %u\n", This, ref);
2866 return ref;
2869 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2871 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2872 ULONG ref;
2873 ref = InterlockedDecrement(&This->ref);
2874 TRACE("(%p) Refcount now %u\n", This, ref);
2875 if(!ref)
2876 HeapFree(GetProcessHeap(), 0, This);
2877 return ref;
2880 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2881 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2882 IAudioSessionControl **out)
2884 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2885 AudioSession *session;
2886 AudioSessionWrapper *wrapper;
2887 HRESULT hr;
2889 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2890 flags, out);
2892 hr = get_audio_session(session_guid, This->device, 0, &session);
2893 if(FAILED(hr))
2894 return hr;
2896 wrapper = AudioSessionWrapper_Create(NULL);
2897 if(!wrapper)
2898 return E_OUTOFMEMORY;
2900 wrapper->session = session;
2902 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2904 return S_OK;
2907 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2908 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2909 ISimpleAudioVolume **out)
2911 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2912 AudioSession *session;
2913 AudioSessionWrapper *wrapper;
2914 HRESULT hr;
2916 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2917 flags, out);
2919 hr = get_audio_session(session_guid, This->device, 0, &session);
2920 if(FAILED(hr))
2921 return hr;
2923 wrapper = AudioSessionWrapper_Create(NULL);
2924 if(!wrapper)
2925 return E_OUTOFMEMORY;
2927 wrapper->session = session;
2929 *out = &wrapper->ISimpleAudioVolume_iface;
2931 return S_OK;
2934 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2935 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2937 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2938 FIXME("(%p)->(%p) - stub\n", This, out);
2939 return E_NOTIMPL;
2942 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2943 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2945 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2946 FIXME("(%p)->(%p) - stub\n", This, notification);
2947 return E_NOTIMPL;
2950 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2951 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2953 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2954 FIXME("(%p)->(%p) - stub\n", This, notification);
2955 return E_NOTIMPL;
2958 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2959 IAudioSessionManager2 *iface, const WCHAR *session_id,
2960 IAudioVolumeDuckNotification *notification)
2962 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2963 FIXME("(%p)->(%p) - stub\n", This, notification);
2964 return E_NOTIMPL;
2967 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2968 IAudioSessionManager2 *iface,
2969 IAudioVolumeDuckNotification *notification)
2971 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2972 FIXME("(%p)->(%p) - stub\n", This, notification);
2973 return E_NOTIMPL;
2976 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2978 AudioSessionManager_QueryInterface,
2979 AudioSessionManager_AddRef,
2980 AudioSessionManager_Release,
2981 AudioSessionManager_GetAudioSessionControl,
2982 AudioSessionManager_GetSimpleAudioVolume,
2983 AudioSessionManager_GetSessionEnumerator,
2984 AudioSessionManager_RegisterSessionNotification,
2985 AudioSessionManager_UnregisterSessionNotification,
2986 AudioSessionManager_RegisterDuckNotification,
2987 AudioSessionManager_UnregisterDuckNotification
2990 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
2991 IAudioSessionManager2 **out)
2993 SessionMgr *This;
2995 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
2996 if(!This)
2997 return E_OUTOFMEMORY;
2999 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3000 This->device = device;
3001 This->ref = 1;
3003 *out = &This->IAudioSessionManager2_iface;
3005 return S_OK;