mmdevapi: Fix stuttering upon start and underrun.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob562a67498fb4700b10324e2598a500b809aee538
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"
50 #include "endpointvolume.h"
52 #include "initguid.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;
123 BOOL initted, playing;
124 UINT64 written_frames;
125 UINT32 period_us, bufsize_frames, held_frames, tmp_buffer_frames, inbuf_frames;
126 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
128 BYTE *local_buffer, *tmp_buffer;
129 int buf_state;
130 HANDLE timer;
132 CRITICAL_SECTION lock;
134 AudioSession *session;
135 AudioSessionWrapper *session_wrapper;
137 struct list entry;
140 enum BufferStates {
141 NOT_LOCKED = 0,
142 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
143 LOCKED_WRAPPED /* public buffer piece is in tmp_buffer */
146 typedef struct _SessionMgr {
147 IAudioSessionManager2 IAudioSessionManager2_iface;
149 LONG ref;
151 IMMDevice *device;
152 } SessionMgr;
154 static HANDLE g_timer_q;
156 static CRITICAL_SECTION g_sessions_lock;
157 static struct list g_sessions = LIST_INIT(g_sessions);
159 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
160 static HRESULT oss_setvol(ACImpl *This, UINT32 index);
162 static const IAudioClientVtbl AudioClient_Vtbl;
163 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
164 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
165 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
166 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
167 static const IAudioClockVtbl AudioClock_Vtbl;
168 static const IAudioClock2Vtbl AudioClock2_Vtbl;
169 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
170 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
171 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
173 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
175 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
178 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
180 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
183 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
185 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
188 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
190 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
193 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
195 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
198 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
200 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
203 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
205 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
208 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
210 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
213 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
215 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
218 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
220 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
223 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
225 if(reason == DLL_PROCESS_ATTACH){
226 g_timer_q = CreateTimerQueue();
227 if(!g_timer_q)
228 return FALSE;
230 InitializeCriticalSection(&g_sessions_lock);
233 return TRUE;
236 /* From <dlls/mmdevapi/mmdevapi.h> */
237 enum DriverPriority {
238 Priority_Unavailable = 0,
239 Priority_Low,
240 Priority_Neutral,
241 Priority_Preferred
244 int WINAPI AUDDRV_GetPriority(void)
246 int mixer_fd;
247 oss_sysinfo sysinfo;
249 /* Attempt to determine if we are running on OSS or ALSA's OSS
250 * compatibility layer. There is no official way to do that, so just check
251 * for validity as best as possible, without rejecting valid OSS
252 * implementations. */
254 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
255 if(mixer_fd < 0){
256 TRACE("Priority_Unavailable: open failed\n");
257 return Priority_Unavailable;
260 sysinfo.version[0] = 0xFF;
261 sysinfo.versionnum = ~0;
262 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
263 TRACE("Priority_Unavailable: ioctl failed\n");
264 close(mixer_fd);
265 return Priority_Unavailable;
268 close(mixer_fd);
270 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
271 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
272 return Priority_Low;
274 if(sysinfo.versionnum & 0x80000000){
275 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
276 return Priority_Low;
279 TRACE("Priority_Preferred: Seems like valid OSS!\n");
281 return Priority_Preferred;
284 static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
286 int fd = -1, err, i;
287 oss_audioinfo ai;
289 if(flow == eRender)
290 fd = open("/dev/dsp", O_WRONLY);
291 else
292 fd = open("/dev/dsp", O_RDONLY);
294 if(fd < 0){
295 WARN("Couldn't open default device!\n");
296 return 0;
299 ai.dev = -1;
300 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
301 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
302 close(fd);
303 return 0;
306 close(fd);
308 TRACE("Default devnode: %s\n", ai.devnode);
309 for(i = 0; i < num; ++i)
310 if(!strcmp(ai.devnode, keys[i]))
311 return i;
313 WARN("Couldn't find default device! Choosing first.\n");
314 return 0;
317 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
318 UINT *num, UINT *def_index)
320 int i, mixer_fd;
321 oss_sysinfo sysinfo;
322 static int print_once = 0;
324 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
326 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
327 if(mixer_fd < 0){
328 ERR("OSS /dev/mixer doesn't seem to exist\n");
329 return AUDCLNT_E_SERVICE_NOT_RUNNING;
332 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
333 close(mixer_fd);
335 if(errno == EINVAL){
336 ERR("OSS version too old, need at least OSSv4\n");
337 return AUDCLNT_E_SERVICE_NOT_RUNNING;
340 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
341 return E_FAIL;
344 if(!print_once){
345 TRACE("OSS sysinfo:\n");
346 TRACE("product: %s\n", sysinfo.product);
347 TRACE("version: %s\n", sysinfo.version);
348 TRACE("versionnum: %x\n", sysinfo.versionnum);
349 TRACE("numaudios: %d\n", sysinfo.numaudios);
350 TRACE("nummixers: %d\n", sysinfo.nummixers);
351 TRACE("numcards: %d\n", sysinfo.numcards);
352 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
353 print_once = 1;
356 if(sysinfo.numaudios <= 0){
357 WARN("No audio devices!\n");
358 close(mixer_fd);
359 return AUDCLNT_E_SERVICE_NOT_RUNNING;
362 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
363 *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
365 *num = 0;
366 for(i = 0; i < sysinfo.numaudios; ++i){
367 oss_audioinfo ai = {0};
368 int fd;
370 ai.dev = i;
371 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
372 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
373 strerror(errno));
374 continue;
377 if(flow == eRender)
378 fd = open(ai.devnode, O_WRONLY, 0);
379 else
380 fd = open(ai.devnode, O_RDONLY, 0);
381 if(fd < 0){
382 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
383 ai.devnode, errno, strerror(errno));
384 continue;
386 close(fd);
388 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
389 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
390 size_t len;
392 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
393 strlen(ai.devnode) + 1);
394 if(!(*keys)[*num]){
395 for(i = 0; i < *num; ++i){
396 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
397 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
399 HeapFree(GetProcessHeap(), 0, *ids);
400 HeapFree(GetProcessHeap(), 0, *keys);
401 close(mixer_fd);
402 return E_OUTOFMEMORY;
404 strcpy((*keys)[*num], ai.devnode);
406 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
407 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
408 len * sizeof(WCHAR));
409 if(!(*ids)[*num]){
410 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
411 for(i = 0; i < *num; ++i){
412 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
413 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
415 HeapFree(GetProcessHeap(), 0, *ids);
416 HeapFree(GetProcessHeap(), 0, *keys);
417 close(mixer_fd);
418 return E_OUTOFMEMORY;
420 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
421 (*ids)[*num], len);
423 (*num)++;
427 close(mixer_fd);
429 *def_index = get_default_index(flow, *keys, *num);
431 return S_OK;
434 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
435 EDataFlow dataflow, IAudioClient **out)
437 ACImpl *This;
439 TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
441 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
442 if(!This)
443 return E_OUTOFMEMORY;
445 if(dataflow == eRender)
446 This->fd = open(devnode, O_WRONLY, 0);
447 else if(dataflow == eCapture)
448 This->fd = open(devnode, O_RDONLY, 0);
449 else{
450 HeapFree(GetProcessHeap(), 0, This);
451 return E_INVALIDARG;
453 if(This->fd < 0){
454 ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
455 strerror(errno));
456 HeapFree(GetProcessHeap(), 0, This);
457 return AUDCLNT_E_DEVICE_INVALIDATED;
460 This->dataflow = dataflow;
462 This->ai.dev = -1;
463 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
464 ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
465 errno, strerror(errno));
466 close(This->fd);
467 HeapFree(GetProcessHeap(), 0, This);
468 return E_FAIL;
471 TRACE("OSS audioinfo:\n");
472 TRACE("devnode: %s\n", This->ai.devnode);
473 TRACE("name: %s\n", This->ai.name);
474 TRACE("busy: %x\n", This->ai.busy);
475 TRACE("caps: %x\n", This->ai.caps);
476 TRACE("iformats: %x\n", This->ai.iformats);
477 TRACE("oformats: %x\n", This->ai.oformats);
478 TRACE("enabled: %d\n", This->ai.enabled);
479 TRACE("min_rate: %d\n", This->ai.min_rate);
480 TRACE("max_rate: %d\n", This->ai.max_rate);
481 TRACE("min_channels: %d\n", This->ai.min_channels);
482 TRACE("max_channels: %d\n", This->ai.max_channels);
484 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
485 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
486 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
487 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
488 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
489 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
491 InitializeCriticalSection(&This->lock);
493 This->parent = dev;
494 IMMDevice_AddRef(This->parent);
496 IAudioClient_AddRef(&This->IAudioClient_iface);
498 *out = &This->IAudioClient_iface;
500 return S_OK;
503 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
504 REFIID riid, void **ppv)
506 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
508 if(!ppv)
509 return E_POINTER;
510 *ppv = NULL;
511 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
512 *ppv = iface;
513 if(*ppv){
514 IUnknown_AddRef((IUnknown*)*ppv);
515 return S_OK;
517 WARN("Unknown interface %s\n", debugstr_guid(riid));
518 return E_NOINTERFACE;
521 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
523 ACImpl *This = impl_from_IAudioClient(iface);
524 ULONG ref;
525 ref = InterlockedIncrement(&This->ref);
526 TRACE("(%p) Refcount now %u\n", This, ref);
527 return ref;
530 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
532 ACImpl *This = impl_from_IAudioClient(iface);
533 ULONG ref;
534 ref = InterlockedDecrement(&This->ref);
535 TRACE("(%p) Refcount now %u\n", This, ref);
536 if(!ref){
537 IAudioClient_Stop(iface);
538 IMMDevice_Release(This->parent);
539 DeleteCriticalSection(&This->lock);
540 close(This->fd);
541 if(This->initted){
542 EnterCriticalSection(&g_sessions_lock);
543 list_remove(&This->entry);
544 LeaveCriticalSection(&g_sessions_lock);
546 HeapFree(GetProcessHeap(), 0, This->vols);
547 HeapFree(GetProcessHeap(), 0, This->local_buffer);
548 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
549 CoTaskMemFree(This->fmt);
550 HeapFree(GetProcessHeap(), 0, This);
552 return ref;
555 static void dump_fmt(const WAVEFORMATEX *fmt)
557 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
558 switch(fmt->wFormatTag){
559 case WAVE_FORMAT_PCM:
560 TRACE("WAVE_FORMAT_PCM");
561 break;
562 case WAVE_FORMAT_IEEE_FLOAT:
563 TRACE("WAVE_FORMAT_IEEE_FLOAT");
564 break;
565 case WAVE_FORMAT_EXTENSIBLE:
566 TRACE("WAVE_FORMAT_EXTENSIBLE");
567 break;
568 default:
569 TRACE("Unknown");
570 break;
572 TRACE(")\n");
574 TRACE("nChannels: %u\n", fmt->nChannels);
575 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
576 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
577 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
578 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
579 TRACE("cbSize: %u\n", fmt->cbSize);
581 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
582 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
583 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
584 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
585 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
589 static DWORD get_channel_mask(unsigned int channels)
591 switch(channels){
592 case 0:
593 return 0;
594 case 1:
595 return KSAUDIO_SPEAKER_MONO;
596 case 2:
597 return KSAUDIO_SPEAKER_STEREO;
598 case 3:
599 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
600 case 4:
601 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
602 case 5:
603 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
604 case 6:
605 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
606 case 7:
607 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
608 case 8:
609 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
611 FIXME("Unknown speaker configuration: %u\n", channels);
612 return 0;
615 static int get_oss_format(const WAVEFORMATEX *fmt)
617 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
619 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
620 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
621 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
622 switch(fmt->wBitsPerSample){
623 case 8:
624 return AFMT_U8;
625 case 16:
626 return AFMT_S16_LE;
627 case 24:
628 return AFMT_S24_LE;
629 case 32:
630 return AFMT_S32_LE;
632 return -1;
635 #ifdef AFMT_FLOAT
636 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
637 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
638 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
639 if(fmt->wBitsPerSample != 32)
640 return -1;
642 return AFMT_FLOAT;
644 #endif
646 return -1;
649 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
651 WAVEFORMATEX *ret;
652 size_t size;
654 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
655 size = sizeof(WAVEFORMATEXTENSIBLE);
656 else
657 size = sizeof(WAVEFORMATEX);
659 ret = CoTaskMemAlloc(size);
660 if(!ret)
661 return NULL;
663 memcpy(ret, fmt, size);
665 ret->cbSize = size - sizeof(WAVEFORMATEX);
667 return ret;
670 static HRESULT setup_oss_device(ACImpl *This, const WAVEFORMATEX *fmt,
671 WAVEFORMATEX **out)
673 int tmp, oss_format;
674 double tenth;
675 HRESULT ret = S_OK;
676 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
677 WAVEFORMATEX *closest = NULL;
679 if(out)
680 *out = NULL;
682 tmp = oss_format = get_oss_format(fmt);
683 if(oss_format < 0)
684 return AUDCLNT_E_UNSUPPORTED_FORMAT;
685 if(ioctl(This->fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
686 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
687 return E_FAIL;
689 if(tmp != oss_format){
690 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
691 return AUDCLNT_E_UNSUPPORTED_FORMAT;
694 closest = clone_format(fmt);
695 if(!closest)
696 return E_OUTOFMEMORY;
698 tmp = fmt->nSamplesPerSec;
699 if(ioctl(This->fd, SNDCTL_DSP_SPEED, &tmp) < 0){
700 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
701 CoTaskMemFree(closest);
702 return E_FAIL;
704 tenth = fmt->nSamplesPerSec * 0.1;
705 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
706 ret = S_FALSE;
707 closest->nSamplesPerSec = tmp;
710 tmp = fmt->nChannels;
711 if(ioctl(This->fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
712 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
713 CoTaskMemFree(closest);
714 return E_FAIL;
716 if(tmp != fmt->nChannels){
717 ret = S_FALSE;
718 closest->nChannels = tmp;
721 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
722 DWORD mask = get_channel_mask(closest->nChannels);
724 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
726 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
727 fmtex->dwChannelMask != mask)
728 ret = S_FALSE;
731 if(ret == S_FALSE && out){
732 closest->nBlockAlign =
733 closest->nChannels * closest->wBitsPerSample / 8;
734 closest->nAvgBytesPerSec =
735 closest->nBlockAlign * closest->nSamplesPerSec;
736 *out = closest;
737 } else
738 CoTaskMemFree(closest);
740 TRACE("returning: %08x\n", ret);
741 return ret;
744 static void session_init_vols(AudioSession *session, UINT channels)
746 if(session->channel_count < channels){
747 UINT i;
749 if(session->channel_vols)
750 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
751 session->channel_vols, sizeof(float) * channels);
752 else
753 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
754 sizeof(float) * channels);
755 if(!session->channel_vols)
756 return;
758 for(i = session->channel_count; i < channels; ++i)
759 session->channel_vols[i] = 1.f;
761 session->channel_count = channels;
765 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
766 UINT num_channels)
768 AudioSession *ret;
770 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
771 if(!ret)
772 return NULL;
774 memcpy(&ret->guid, guid, sizeof(GUID));
776 ret->device = device;
778 list_init(&ret->clients);
780 list_add_head(&g_sessions, &ret->entry);
782 InitializeCriticalSection(&ret->lock);
784 session_init_vols(ret, num_channels);
786 ret->master_vol = 1.f;
788 return ret;
791 /* if channels == 0, then this will return or create a session with
792 * matching dataflow and GUID. otherwise, channels must also match */
793 static HRESULT get_audio_session(const GUID *sessionguid,
794 IMMDevice *device, UINT channels, AudioSession **out)
796 AudioSession *session;
798 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
799 *out = create_session(&GUID_NULL, device, channels);
800 if(!*out)
801 return E_OUTOFMEMORY;
803 return S_OK;
806 *out = NULL;
807 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
808 if(session->device == device &&
809 IsEqualGUID(sessionguid, &session->guid)){
810 session_init_vols(session, channels);
811 *out = session;
812 break;
816 if(!*out){
817 *out = create_session(sessionguid, device, channels);
818 if(!*out)
819 return E_OUTOFMEMORY;
822 return S_OK;
825 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
826 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
827 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
828 const GUID *sessionguid)
830 ACImpl *This = impl_from_IAudioClient(iface);
831 int mask, i;
832 HRESULT hr;
834 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
835 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
837 if(!fmt)
838 return E_POINTER;
840 dump_fmt(fmt);
842 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
843 return AUDCLNT_E_NOT_INITIALIZED;
845 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
846 AUDCLNT_STREAMFLAGS_LOOPBACK |
847 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
848 AUDCLNT_STREAMFLAGS_NOPERSIST |
849 AUDCLNT_STREAMFLAGS_RATEADJUST |
850 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
851 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
852 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
853 TRACE("Unknown flags: %08x\n", flags);
854 return E_INVALIDARG;
857 EnterCriticalSection(&This->lock);
859 if(This->initted){
860 LeaveCriticalSection(&This->lock);
861 return AUDCLNT_E_ALREADY_INITIALIZED;
864 hr = setup_oss_device(This, fmt, NULL);
865 if(hr == S_FALSE){
866 LeaveCriticalSection(&This->lock);
867 return AUDCLNT_E_UNSUPPORTED_FORMAT;
869 if(FAILED(hr)){
870 LeaveCriticalSection(&This->lock);
871 return hr;
874 mask = 0;
875 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
876 LeaveCriticalSection(&This->lock);
877 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
878 return E_FAIL;
881 mask = (100 << 8) | 100;
882 if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
883 WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
885 This->fmt = clone_format(fmt);
886 if(!This->fmt){
887 LeaveCriticalSection(&This->lock);
888 return E_OUTOFMEMORY;
891 if(period)
892 This->period_us = period / 10;
893 else
894 This->period_us = DefaultPeriod / 10;
896 if(!duration)
897 duration = 300000; /* 0.03s */
898 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
899 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
900 This->bufsize_frames * fmt->nBlockAlign);
901 if(!This->local_buffer){
902 CoTaskMemFree(This->fmt);
903 This->fmt = NULL;
904 LeaveCriticalSection(&This->lock);
905 return E_OUTOFMEMORY;
908 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
909 if(!This->vols){
910 CoTaskMemFree(This->fmt);
911 This->fmt = NULL;
912 LeaveCriticalSection(&This->lock);
913 return E_OUTOFMEMORY;
916 for(i = 0; i < fmt->nChannels; ++i)
917 This->vols[i] = 1.f;
919 This->share = mode;
920 This->flags = flags;
922 EnterCriticalSection(&g_sessions_lock);
924 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
925 &This->session);
926 if(FAILED(hr)){
927 LeaveCriticalSection(&g_sessions_lock);
928 HeapFree(GetProcessHeap(), 0, This->vols);
929 This->vols = NULL;
930 CoTaskMemFree(This->fmt);
931 This->fmt = NULL;
932 LeaveCriticalSection(&This->lock);
933 return hr;
936 list_add_tail(&This->session->clients, &This->entry);
938 LeaveCriticalSection(&g_sessions_lock);
940 This->initted = TRUE;
942 oss_setvol(This, -1);
944 LeaveCriticalSection(&This->lock);
946 return S_OK;
949 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
950 UINT32 *frames)
952 ACImpl *This = impl_from_IAudioClient(iface);
954 TRACE("(%p)->(%p)\n", This, frames);
956 if(!frames)
957 return E_POINTER;
959 EnterCriticalSection(&This->lock);
961 if(!This->initted){
962 LeaveCriticalSection(&This->lock);
963 return AUDCLNT_E_NOT_INITIALIZED;
966 *frames = This->bufsize_frames;
968 LeaveCriticalSection(&This->lock);
970 return S_OK;
973 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
974 REFERENCE_TIME *latency)
976 ACImpl *This = impl_from_IAudioClient(iface);
978 TRACE("(%p)->(%p)\n", This, latency);
980 if(!latency)
981 return E_POINTER;
983 EnterCriticalSection(&This->lock);
985 if(!This->initted){
986 LeaveCriticalSection(&This->lock);
987 return AUDCLNT_E_NOT_INITIALIZED;
990 if(This->dataflow == eRender){
991 int delay_bytes;
992 double delay_s;
994 if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
995 LeaveCriticalSection(&This->lock);
996 WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
997 return E_FAIL;
1000 delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
1001 This->fmt->nBlockAlign);
1003 *latency = delay_s * 10000000;
1004 }else
1005 *latency = 10000; /* OSS doesn't provide input latency */
1007 LeaveCriticalSection(&This->lock);
1009 return S_OK;
1012 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1013 UINT32 *numpad)
1015 ACImpl *This = impl_from_IAudioClient(iface);
1016 audio_buf_info bi;
1018 TRACE("(%p)->(%p)\n", This, numpad);
1020 if(!numpad)
1021 return E_POINTER;
1023 EnterCriticalSection(&This->lock);
1025 if(!This->initted){
1026 LeaveCriticalSection(&This->lock);
1027 return AUDCLNT_E_NOT_INITIALIZED;
1030 if(This->dataflow == eRender){
1031 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1032 LeaveCriticalSection(&This->lock);
1033 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1034 return E_FAIL;
1037 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1038 This->fmt->nBlockAlign;
1040 /* when the OSS buffer has less than one fragment of data, including
1041 * no data, it often reports it as some non-zero portion of a
1042 * fragment. when it has more than one fragment of data, it reports
1043 * it as some multiple of that portion of the fragment size.
1045 * so, we have to do some ugly workarounds to report the timing
1046 * as accurately as possible */
1047 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1048 *numpad = This->inbuf_frames;
1049 This->inbuf_frames = 0;
1050 }else{
1051 if(*numpad < This->inbuf_frames)
1052 This->inbuf_frames = *numpad;
1053 else
1054 *numpad = This->inbuf_frames;
1056 }else if(This->dataflow == eCapture){
1057 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1058 LeaveCriticalSection(&This->lock);
1059 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1060 return E_FAIL;
1063 if(bi.bytes <= bi.fragsize)
1064 *numpad = 0;
1065 else
1066 *numpad = bi.bytes / This->fmt->nBlockAlign;
1067 }else{
1068 LeaveCriticalSection(&This->lock);
1069 return E_UNEXPECTED;
1072 *numpad += This->held_frames;
1074 LeaveCriticalSection(&This->lock);
1076 return S_OK;
1079 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1080 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1081 WAVEFORMATEX **outpwfx)
1083 ACImpl *This = impl_from_IAudioClient(iface);
1084 HRESULT ret;
1086 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1088 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1089 return E_POINTER;
1091 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1092 return E_INVALIDARG;
1094 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1095 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1096 return E_INVALIDARG;
1098 dump_fmt(pwfx);
1100 EnterCriticalSection(&This->lock);
1102 ret = setup_oss_device(This, pwfx, outpwfx);
1104 LeaveCriticalSection(&This->lock);
1106 return ret;
1109 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1110 WAVEFORMATEX **pwfx)
1112 ACImpl *This = impl_from_IAudioClient(iface);
1113 WAVEFORMATEXTENSIBLE *fmt;
1114 int formats;
1116 TRACE("(%p)->(%p)\n", This, pwfx);
1118 if(!pwfx)
1119 return E_POINTER;
1120 *pwfx = NULL;
1122 if(This->dataflow == eRender)
1123 formats = This->ai.oformats;
1124 else if(This->dataflow == eCapture)
1125 formats = This->ai.iformats;
1126 else
1127 return E_UNEXPECTED;
1129 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1130 if(!fmt)
1131 return E_OUTOFMEMORY;
1133 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1134 if(formats & AFMT_S16_LE){
1135 fmt->Format.wBitsPerSample = 16;
1136 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1137 #ifdef AFMT_FLOAT
1138 }else if(formats & AFMT_FLOAT){
1139 fmt->Format.wBitsPerSample = 32;
1140 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1141 #endif
1142 }else if(formats & AFMT_U8){
1143 fmt->Format.wBitsPerSample = 8;
1144 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1145 }else if(formats & AFMT_S32_LE){
1146 fmt->Format.wBitsPerSample = 32;
1147 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1148 }else if(formats & AFMT_S24_LE){
1149 fmt->Format.wBitsPerSample = 24;
1150 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1151 }else{
1152 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1153 CoTaskMemFree(fmt);
1154 return E_FAIL;
1157 fmt->Format.nChannels = This->ai.max_channels;
1158 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1159 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1161 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1162 fmt->Format.nChannels) / 8;
1163 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1164 fmt->Format.nBlockAlign;
1166 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1167 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1169 *pwfx = (WAVEFORMATEX*)fmt;
1170 dump_fmt(*pwfx);
1172 return S_OK;
1175 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1176 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1178 ACImpl *This = impl_from_IAudioClient(iface);
1180 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1182 if(!defperiod && !minperiod)
1183 return E_POINTER;
1185 EnterCriticalSection(&This->lock);
1187 if(defperiod)
1188 *defperiod = DefaultPeriod;
1189 if(minperiod)
1190 *minperiod = MinimumPeriod;
1192 LeaveCriticalSection(&This->lock);
1194 return S_OK;
1197 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1199 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1200 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1201 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1202 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1203 This->fmt->wBitsPerSample == 8)
1204 memset(buf, 128, frames * This->fmt->nBlockAlign);
1205 else
1206 memset(buf, 0, frames * This->fmt->nBlockAlign);
1209 static void oss_write_data(ACImpl *This)
1211 ssize_t written;
1212 UINT32 written_frames;
1213 size_t to_write;
1214 BYTE *buf =
1215 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1217 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1218 to_write = This->bufsize_frames - This->lcl_offs_frames;
1219 else
1220 to_write = This->held_frames;
1222 if(This->session->mute)
1223 oss_silence_buffer(This, buf, to_write);
1225 written = write(This->fd, buf, to_write * This->fmt->nBlockAlign);
1226 if(written < 0){
1227 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1228 return;
1230 written_frames = written / This->fmt->nBlockAlign;
1232 This->lcl_offs_frames += written_frames;
1233 This->lcl_offs_frames %= This->bufsize_frames;
1234 This->held_frames -= written_frames;
1235 This->inbuf_frames += written_frames;
1237 if(written_frames < to_write){
1238 /* OSS buffer probably full */
1239 return;
1242 if(This->held_frames){
1243 /* wrapped and have some data back at the start to write */
1245 if(This->session->mute)
1246 oss_silence_buffer(This, This->local_buffer, This->held_frames);
1248 written = write(This->fd, This->local_buffer,
1249 This->held_frames * This->fmt->nBlockAlign);
1250 if(written < 0){
1251 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1252 return;
1254 written_frames = written / This->fmt->nBlockAlign;
1256 This->lcl_offs_frames += written_frames;
1257 This->lcl_offs_frames %= This->bufsize_frames;
1258 This->held_frames -= written_frames;
1259 This->inbuf_frames += written_frames;
1263 static void oss_read_data(ACImpl *This)
1265 UINT64 pos, readable;
1266 audio_buf_info bi;
1267 ssize_t nread;
1269 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1270 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1271 return;
1274 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1275 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1277 if(bi.bytes < readable)
1278 readable = bi.bytes;
1280 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1281 readable);
1282 if(nread < 0){
1283 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1284 return;
1287 This->held_frames += nread / This->fmt->nBlockAlign;
1289 if(This->held_frames > This->bufsize_frames){
1290 WARN("Overflow of unread data\n");
1291 This->lcl_offs_frames += This->held_frames;
1292 This->lcl_offs_frames %= This->bufsize_frames;
1293 This->held_frames = This->bufsize_frames;
1297 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1299 ACImpl *This = user;
1301 EnterCriticalSection(&This->lock);
1303 if(This->dataflow == eRender && This->held_frames)
1304 oss_write_data(This);
1305 else if(This->dataflow == eCapture)
1306 oss_read_data(This);
1308 if(This->event)
1309 SetEvent(This->event);
1311 LeaveCriticalSection(&This->lock);
1314 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1316 ACImpl *This = impl_from_IAudioClient(iface);
1317 int mask;
1319 TRACE("(%p)\n", This);
1321 EnterCriticalSection(&This->lock);
1323 if(!This->initted){
1324 LeaveCriticalSection(&This->lock);
1325 return AUDCLNT_E_NOT_INITIALIZED;
1328 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1329 LeaveCriticalSection(&This->lock);
1330 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1333 if(This->playing){
1334 LeaveCriticalSection(&This->lock);
1335 return AUDCLNT_E_NOT_STOPPED;
1338 if(This->dataflow == eRender)
1339 mask = PCM_ENABLE_OUTPUT;
1340 else if(This->dataflow == eCapture)
1341 mask = PCM_ENABLE_INPUT;
1342 else{
1343 LeaveCriticalSection(&This->lock);
1344 return E_UNEXPECTED;
1347 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1348 LeaveCriticalSection(&This->lock);
1349 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1350 return E_FAIL;
1353 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1354 oss_period_callback, This, 0, This->period_us / 1000,
1355 WT_EXECUTEINTIMERTHREAD))
1356 ERR("Unable to create period timer: %u\n", GetLastError());
1358 This->playing = TRUE;
1360 LeaveCriticalSection(&This->lock);
1362 return S_OK;
1365 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1367 ACImpl *This = impl_from_IAudioClient(iface);
1368 int mask;
1370 TRACE("(%p)\n", This);
1372 EnterCriticalSection(&This->lock);
1374 if(!This->initted){
1375 LeaveCriticalSection(&This->lock);
1376 return AUDCLNT_E_NOT_INITIALIZED;
1379 if(!This->playing){
1380 LeaveCriticalSection(&This->lock);
1381 return S_FALSE;
1384 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1385 DeleteTimerQueueTimer(g_timer_q, This->timer,
1386 INVALID_HANDLE_VALUE);
1387 This->timer = NULL;
1390 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1391 LeaveCriticalSection(&This->lock);
1392 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1393 return E_FAIL;
1396 mask = 0;
1397 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1398 LeaveCriticalSection(&This->lock);
1399 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1400 return E_FAIL;
1403 This->playing = FALSE;
1405 LeaveCriticalSection(&This->lock);
1407 return S_OK;
1410 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1412 ACImpl *This = impl_from_IAudioClient(iface);
1414 TRACE("(%p)\n", This);
1416 EnterCriticalSection(&This->lock);
1418 if(!This->initted){
1419 LeaveCriticalSection(&This->lock);
1420 return AUDCLNT_E_NOT_INITIALIZED;
1423 if(This->playing){
1424 LeaveCriticalSection(&This->lock);
1425 return AUDCLNT_E_NOT_STOPPED;
1428 if(This->buf_state != NOT_LOCKED){
1429 LeaveCriticalSection(&This->lock);
1430 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1433 This->written_frames = 0;
1434 This->inbuf_frames = 0;
1435 This->held_frames = 0;
1437 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1438 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1440 LeaveCriticalSection(&This->lock);
1442 return S_OK;
1445 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1446 HANDLE event)
1448 ACImpl *This = impl_from_IAudioClient(iface);
1450 TRACE("(%p)->(%p)\n", This, event);
1452 if(!event)
1453 return E_INVALIDARG;
1455 EnterCriticalSection(&This->lock);
1457 if(!This->initted){
1458 LeaveCriticalSection(&This->lock);
1459 return AUDCLNT_E_NOT_INITIALIZED;
1462 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1463 LeaveCriticalSection(&This->lock);
1464 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1467 This->event = event;
1469 LeaveCriticalSection(&This->lock);
1471 return S_OK;
1474 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1475 void **ppv)
1477 ACImpl *This = impl_from_IAudioClient(iface);
1479 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1481 if(!ppv)
1482 return E_POINTER;
1483 *ppv = NULL;
1485 EnterCriticalSection(&This->lock);
1487 if(!This->initted){
1488 LeaveCriticalSection(&This->lock);
1489 return AUDCLNT_E_NOT_INITIALIZED;
1492 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1493 if(This->dataflow != eRender){
1494 LeaveCriticalSection(&This->lock);
1495 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1497 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1498 *ppv = &This->IAudioRenderClient_iface;
1499 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1500 if(This->dataflow != eCapture){
1501 LeaveCriticalSection(&This->lock);
1502 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1504 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1505 *ppv = &This->IAudioCaptureClient_iface;
1506 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1507 IAudioClock_AddRef(&This->IAudioClock_iface);
1508 *ppv = &This->IAudioClock_iface;
1509 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1510 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1511 *ppv = &This->IAudioStreamVolume_iface;
1512 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1513 if(!This->session_wrapper){
1514 This->session_wrapper = AudioSessionWrapper_Create(This);
1515 if(!This->session_wrapper){
1516 LeaveCriticalSection(&This->lock);
1517 return E_OUTOFMEMORY;
1519 }else
1520 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1522 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1523 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1524 if(!This->session_wrapper){
1525 This->session_wrapper = AudioSessionWrapper_Create(This);
1526 if(!This->session_wrapper){
1527 LeaveCriticalSection(&This->lock);
1528 return E_OUTOFMEMORY;
1530 }else
1531 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1533 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1534 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1535 if(!This->session_wrapper){
1536 This->session_wrapper = AudioSessionWrapper_Create(This);
1537 if(!This->session_wrapper){
1538 LeaveCriticalSection(&This->lock);
1539 return E_OUTOFMEMORY;
1541 }else
1542 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1544 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1547 if(*ppv){
1548 LeaveCriticalSection(&This->lock);
1549 return S_OK;
1552 LeaveCriticalSection(&This->lock);
1554 FIXME("stub %s\n", debugstr_guid(riid));
1555 return E_NOINTERFACE;
1558 static const IAudioClientVtbl AudioClient_Vtbl =
1560 AudioClient_QueryInterface,
1561 AudioClient_AddRef,
1562 AudioClient_Release,
1563 AudioClient_Initialize,
1564 AudioClient_GetBufferSize,
1565 AudioClient_GetStreamLatency,
1566 AudioClient_GetCurrentPadding,
1567 AudioClient_IsFormatSupported,
1568 AudioClient_GetMixFormat,
1569 AudioClient_GetDevicePeriod,
1570 AudioClient_Start,
1571 AudioClient_Stop,
1572 AudioClient_Reset,
1573 AudioClient_SetEventHandle,
1574 AudioClient_GetService
1577 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1578 IAudioRenderClient *iface, REFIID riid, void **ppv)
1580 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1582 if(!ppv)
1583 return E_POINTER;
1584 *ppv = NULL;
1586 if(IsEqualIID(riid, &IID_IUnknown) ||
1587 IsEqualIID(riid, &IID_IAudioRenderClient))
1588 *ppv = iface;
1589 if(*ppv){
1590 IUnknown_AddRef((IUnknown*)*ppv);
1591 return S_OK;
1594 WARN("Unknown interface %s\n", debugstr_guid(riid));
1595 return E_NOINTERFACE;
1598 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1600 ACImpl *This = impl_from_IAudioRenderClient(iface);
1601 return AudioClient_AddRef(&This->IAudioClient_iface);
1604 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1606 ACImpl *This = impl_from_IAudioRenderClient(iface);
1607 return AudioClient_Release(&This->IAudioClient_iface);
1610 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1611 UINT32 frames, BYTE **data)
1613 ACImpl *This = impl_from_IAudioRenderClient(iface);
1614 UINT32 pad, write_pos;
1615 HRESULT hr;
1617 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1619 if(!data)
1620 return E_POINTER;
1622 EnterCriticalSection(&This->lock);
1624 if(This->buf_state != NOT_LOCKED){
1625 LeaveCriticalSection(&This->lock);
1626 return AUDCLNT_E_OUT_OF_ORDER;
1629 if(!frames){
1630 This->buf_state = LOCKED_NORMAL;
1631 LeaveCriticalSection(&This->lock);
1632 return S_OK;
1635 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1636 if(FAILED(hr)){
1637 LeaveCriticalSection(&This->lock);
1638 return hr;
1641 if(pad + frames > This->bufsize_frames){
1642 LeaveCriticalSection(&This->lock);
1643 return AUDCLNT_E_BUFFER_TOO_LARGE;
1646 write_pos =
1647 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1648 if(write_pos + frames > This->bufsize_frames){
1649 if(This->tmp_buffer_frames < frames){
1650 if(This->tmp_buffer)
1651 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1652 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1653 else
1654 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1655 frames * This->fmt->nBlockAlign);
1656 if(!This->tmp_buffer){
1657 LeaveCriticalSection(&This->lock);
1658 return E_OUTOFMEMORY;
1660 This->tmp_buffer_frames = frames;
1662 *data = This->tmp_buffer;
1663 This->buf_state = LOCKED_WRAPPED;
1664 }else{
1665 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1666 This->buf_state = LOCKED_NORMAL;
1669 LeaveCriticalSection(&This->lock);
1671 return S_OK;
1674 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1676 UINT32 write_offs_frames =
1677 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1678 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1679 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1680 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1681 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1683 if(written_bytes <= chunk_bytes){
1684 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1685 }else{
1686 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1687 memcpy(This->local_buffer, buffer + chunk_bytes,
1688 written_bytes - chunk_bytes);
1692 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1693 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1695 ACImpl *This = impl_from_IAudioRenderClient(iface);
1696 BYTE *buffer;
1698 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1700 EnterCriticalSection(&This->lock);
1702 if(This->buf_state == NOT_LOCKED || !written_frames){
1703 This->buf_state = NOT_LOCKED;
1704 LeaveCriticalSection(&This->lock);
1705 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1708 if(This->buf_state == LOCKED_NORMAL)
1709 buffer = This->local_buffer + This->fmt->nBlockAlign *
1710 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1711 else
1712 buffer = This->tmp_buffer;
1714 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1715 oss_silence_buffer(This, buffer, written_frames);
1717 if(This->held_frames){
1718 if(This->buf_state == LOCKED_WRAPPED)
1719 oss_wrap_buffer(This, buffer, written_frames);
1721 This->held_frames += written_frames;
1722 }else{
1723 ssize_t w_bytes;
1724 UINT32 w_frames;
1726 if(This->session->mute)
1727 oss_silence_buffer(This, buffer, written_frames);
1729 w_bytes = write(This->fd, buffer,
1730 written_frames * This->fmt->nBlockAlign);
1731 if(w_bytes < 0){
1732 LeaveCriticalSection(&This->lock);
1733 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1734 return E_FAIL;
1736 w_frames = w_bytes / This->fmt->nBlockAlign;
1737 This->inbuf_frames += w_frames;
1739 if(w_frames < written_frames){
1740 if(This->buf_state == LOCKED_WRAPPED)
1741 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1742 written_frames - w_frames);
1743 else
1744 This->lcl_offs_frames += w_frames;
1745 This->held_frames = written_frames - w_frames;
1749 This->written_frames += written_frames;
1750 This->buf_state = NOT_LOCKED;
1752 LeaveCriticalSection(&This->lock);
1754 return S_OK;
1757 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1758 AudioRenderClient_QueryInterface,
1759 AudioRenderClient_AddRef,
1760 AudioRenderClient_Release,
1761 AudioRenderClient_GetBuffer,
1762 AudioRenderClient_ReleaseBuffer
1765 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1766 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1768 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1770 if(!ppv)
1771 return E_POINTER;
1772 *ppv = NULL;
1774 if(IsEqualIID(riid, &IID_IUnknown) ||
1775 IsEqualIID(riid, &IID_IAudioCaptureClient))
1776 *ppv = iface;
1777 if(*ppv){
1778 IUnknown_AddRef((IUnknown*)*ppv);
1779 return S_OK;
1782 WARN("Unknown interface %s\n", debugstr_guid(riid));
1783 return E_NOINTERFACE;
1786 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1788 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1789 return IAudioClient_AddRef(&This->IAudioClient_iface);
1792 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1794 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1795 return IAudioClient_Release(&This->IAudioClient_iface);
1798 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1799 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1800 UINT64 *qpcpos)
1802 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1803 HRESULT hr;
1805 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1806 devpos, qpcpos);
1808 if(!data || !frames || !flags)
1809 return E_POINTER;
1811 EnterCriticalSection(&This->lock);
1813 if(This->buf_state != NOT_LOCKED){
1814 LeaveCriticalSection(&This->lock);
1815 return AUDCLNT_E_OUT_OF_ORDER;
1818 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1819 if(FAILED(hr)){
1820 LeaveCriticalSection(&This->lock);
1821 return hr;
1824 *flags = 0;
1826 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1827 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1828 if(This->tmp_buffer_frames < *frames){
1829 if(This->tmp_buffer)
1830 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1831 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1832 else
1833 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1834 *frames * This->fmt->nBlockAlign);
1835 if(!This->tmp_buffer){
1836 LeaveCriticalSection(&This->lock);
1837 return E_OUTOFMEMORY;
1839 This->tmp_buffer_frames = *frames;
1842 *data = This->tmp_buffer;
1843 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1844 This->fmt->nBlockAlign;
1845 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1846 frames_bytes = *frames * This->fmt->nBlockAlign;
1847 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1848 memcpy(This->tmp_buffer, This->local_buffer,
1849 frames_bytes - chunk_bytes);
1850 }else
1851 *data = This->local_buffer +
1852 This->lcl_offs_frames * This->fmt->nBlockAlign;
1854 This->buf_state = LOCKED_NORMAL;
1856 if(devpos || qpcpos)
1857 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1859 LeaveCriticalSection(&This->lock);
1861 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1864 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1865 IAudioCaptureClient *iface, UINT32 done)
1867 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1869 TRACE("(%p)->(%u)\n", This, done);
1871 EnterCriticalSection(&This->lock);
1873 if(This->buf_state == NOT_LOCKED){
1874 LeaveCriticalSection(&This->lock);
1875 return AUDCLNT_E_OUT_OF_ORDER;
1878 This->held_frames -= done;
1879 This->lcl_offs_frames += done;
1880 This->lcl_offs_frames %= This->bufsize_frames;
1882 This->buf_state = NOT_LOCKED;
1884 LeaveCriticalSection(&This->lock);
1886 return S_OK;
1889 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1890 IAudioCaptureClient *iface, UINT32 *frames)
1892 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1894 TRACE("(%p)->(%p)\n", This, frames);
1896 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1899 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1901 AudioCaptureClient_QueryInterface,
1902 AudioCaptureClient_AddRef,
1903 AudioCaptureClient_Release,
1904 AudioCaptureClient_GetBuffer,
1905 AudioCaptureClient_ReleaseBuffer,
1906 AudioCaptureClient_GetNextPacketSize
1909 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1910 REFIID riid, void **ppv)
1912 ACImpl *This = impl_from_IAudioClock(iface);
1914 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1916 if(!ppv)
1917 return E_POINTER;
1918 *ppv = NULL;
1920 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1921 *ppv = iface;
1922 else if(IsEqualIID(riid, &IID_IAudioClock2))
1923 *ppv = &This->IAudioClock2_iface;
1924 if(*ppv){
1925 IUnknown_AddRef((IUnknown*)*ppv);
1926 return S_OK;
1929 WARN("Unknown interface %s\n", debugstr_guid(riid));
1930 return E_NOINTERFACE;
1933 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1935 ACImpl *This = impl_from_IAudioClock(iface);
1936 return IAudioClient_AddRef(&This->IAudioClient_iface);
1939 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1941 ACImpl *This = impl_from_IAudioClock(iface);
1942 return IAudioClient_Release(&This->IAudioClient_iface);
1945 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1947 ACImpl *This = impl_from_IAudioClock(iface);
1949 TRACE("(%p)->(%p)\n", This, freq);
1951 *freq = This->fmt->nSamplesPerSec;
1953 return S_OK;
1956 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1957 UINT64 *qpctime)
1959 ACImpl *This = impl_from_IAudioClock(iface);
1960 UINT32 pad;
1961 HRESULT hr;
1963 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1965 if(!pos)
1966 return E_POINTER;
1968 EnterCriticalSection(&This->lock);
1970 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1971 if(FAILED(hr)){
1972 LeaveCriticalSection(&This->lock);
1973 return hr;
1976 if(This->dataflow == eRender)
1977 *pos = This->written_frames - pad;
1978 else if(This->dataflow == eCapture)
1979 *pos = This->written_frames + pad;
1981 LeaveCriticalSection(&This->lock);
1983 if(qpctime){
1984 LARGE_INTEGER stamp, freq;
1985 QueryPerformanceCounter(&stamp);
1986 QueryPerformanceFrequency(&freq);
1987 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1990 return S_OK;
1993 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1994 DWORD *chars)
1996 ACImpl *This = impl_from_IAudioClock(iface);
1998 TRACE("(%p)->(%p)\n", This, chars);
2000 if(!chars)
2001 return E_POINTER;
2003 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2005 return S_OK;
2008 static const IAudioClockVtbl AudioClock_Vtbl =
2010 AudioClock_QueryInterface,
2011 AudioClock_AddRef,
2012 AudioClock_Release,
2013 AudioClock_GetFrequency,
2014 AudioClock_GetPosition,
2015 AudioClock_GetCharacteristics
2018 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2019 REFIID riid, void **ppv)
2021 ACImpl *This = impl_from_IAudioClock2(iface);
2022 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2025 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2027 ACImpl *This = impl_from_IAudioClock2(iface);
2028 return IAudioClient_AddRef(&This->IAudioClient_iface);
2031 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2033 ACImpl *This = impl_from_IAudioClock2(iface);
2034 return IAudioClient_Release(&This->IAudioClient_iface);
2037 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2038 UINT64 *pos, UINT64 *qpctime)
2040 ACImpl *This = impl_from_IAudioClock2(iface);
2042 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2044 return E_NOTIMPL;
2047 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2049 AudioClock2_QueryInterface,
2050 AudioClock2_AddRef,
2051 AudioClock2_Release,
2052 AudioClock2_GetDevicePosition
2055 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2057 AudioSessionWrapper *ret;
2059 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2060 sizeof(AudioSessionWrapper));
2061 if(!ret)
2062 return NULL;
2064 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2065 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2066 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2068 ret->ref = 1;
2070 ret->client = client;
2071 if(client){
2072 ret->session = client->session;
2073 AudioClient_AddRef(&client->IAudioClient_iface);
2076 return ret;
2079 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2080 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2082 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2084 if(!ppv)
2085 return E_POINTER;
2086 *ppv = NULL;
2088 if(IsEqualIID(riid, &IID_IUnknown) ||
2089 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2090 IsEqualIID(riid, &IID_IAudioSessionControl2))
2091 *ppv = iface;
2092 if(*ppv){
2093 IUnknown_AddRef((IUnknown*)*ppv);
2094 return S_OK;
2097 WARN("Unknown interface %s\n", debugstr_guid(riid));
2098 return E_NOINTERFACE;
2101 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2103 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2104 ULONG ref;
2105 ref = InterlockedIncrement(&This->ref);
2106 TRACE("(%p) Refcount now %u\n", This, ref);
2107 return ref;
2110 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2112 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2113 ULONG ref;
2114 ref = InterlockedDecrement(&This->ref);
2115 TRACE("(%p) Refcount now %u\n", This, ref);
2116 if(!ref){
2117 if(This->client){
2118 EnterCriticalSection(&This->client->lock);
2119 This->client->session_wrapper = NULL;
2120 LeaveCriticalSection(&This->client->lock);
2121 AudioClient_Release(&This->client->IAudioClient_iface);
2123 HeapFree(GetProcessHeap(), 0, This);
2125 return ref;
2128 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2129 AudioSessionState *state)
2131 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2132 ACImpl *client;
2134 TRACE("(%p)->(%p)\n", This, state);
2136 if(!state)
2137 return NULL_PTR_ERR;
2139 EnterCriticalSection(&g_sessions_lock);
2141 if(list_empty(&This->session->clients)){
2142 *state = AudioSessionStateExpired;
2143 LeaveCriticalSection(&g_sessions_lock);
2144 return S_OK;
2147 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2148 EnterCriticalSection(&client->lock);
2149 if(client->playing){
2150 *state = AudioSessionStateActive;
2151 LeaveCriticalSection(&client->lock);
2152 LeaveCriticalSection(&g_sessions_lock);
2153 return S_OK;
2155 LeaveCriticalSection(&client->lock);
2158 LeaveCriticalSection(&g_sessions_lock);
2160 *state = AudioSessionStateInactive;
2162 return S_OK;
2165 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2166 IAudioSessionControl2 *iface, WCHAR **name)
2168 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2170 FIXME("(%p)->(%p) - stub\n", This, name);
2172 return E_NOTIMPL;
2175 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2176 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2178 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2180 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2182 return E_NOTIMPL;
2185 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2186 IAudioSessionControl2 *iface, WCHAR **path)
2188 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2190 FIXME("(%p)->(%p) - stub\n", This, path);
2192 return E_NOTIMPL;
2195 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2196 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2198 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2200 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2202 return E_NOTIMPL;
2205 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2206 IAudioSessionControl2 *iface, GUID *group)
2208 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2210 FIXME("(%p)->(%p) - stub\n", This, group);
2212 return E_NOTIMPL;
2215 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2216 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2218 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2220 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2221 debugstr_guid(session));
2223 return E_NOTIMPL;
2226 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2227 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2229 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2231 FIXME("(%p)->(%p) - stub\n", This, events);
2233 return S_OK;
2236 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2237 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2239 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2241 FIXME("(%p)->(%p) - stub\n", This, events);
2243 return S_OK;
2246 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2247 IAudioSessionControl2 *iface, WCHAR **id)
2249 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2251 FIXME("(%p)->(%p) - stub\n", This, id);
2253 return E_NOTIMPL;
2256 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2257 IAudioSessionControl2 *iface, WCHAR **id)
2259 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2261 FIXME("(%p)->(%p) - stub\n", This, id);
2263 return E_NOTIMPL;
2266 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2267 IAudioSessionControl2 *iface, DWORD *pid)
2269 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2271 TRACE("(%p)->(%p)\n", This, pid);
2273 if(!pid)
2274 return E_POINTER;
2276 *pid = GetCurrentProcessId();
2278 return S_OK;
2281 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2282 IAudioSessionControl2 *iface)
2284 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2286 TRACE("(%p)\n", This);
2288 return S_FALSE;
2291 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2292 IAudioSessionControl2 *iface, BOOL optout)
2294 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2296 TRACE("(%p)->(%d)\n", This, optout);
2298 return S_OK;
2301 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2303 AudioSessionControl_QueryInterface,
2304 AudioSessionControl_AddRef,
2305 AudioSessionControl_Release,
2306 AudioSessionControl_GetState,
2307 AudioSessionControl_GetDisplayName,
2308 AudioSessionControl_SetDisplayName,
2309 AudioSessionControl_GetIconPath,
2310 AudioSessionControl_SetIconPath,
2311 AudioSessionControl_GetGroupingParam,
2312 AudioSessionControl_SetGroupingParam,
2313 AudioSessionControl_RegisterAudioSessionNotification,
2314 AudioSessionControl_UnregisterAudioSessionNotification,
2315 AudioSessionControl_GetSessionIdentifier,
2316 AudioSessionControl_GetSessionInstanceIdentifier,
2317 AudioSessionControl_GetProcessId,
2318 AudioSessionControl_IsSystemSoundsSession,
2319 AudioSessionControl_SetDuckingPreference
2322 /* index == -1 means set all channels, otherwise sets only the given channel */
2323 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2325 int setreq, getreq;
2326 unsigned int vol;
2327 unsigned short l;
2328 float level;
2330 if(index == (UINT32)-1){
2331 HRESULT ret = S_OK;
2332 UINT32 i;
2333 for(i = 0; i < This->fmt->nChannels; ++i){
2334 HRESULT hr;
2335 hr = oss_setvol(This, i);
2336 if(FAILED(hr))
2337 ret = hr;
2339 return ret;
2342 if(index > 1)
2343 /* OSS doesn't support volume control past the first two channels */
2344 return S_OK;
2346 if(This->dataflow == eRender){
2347 setreq = SNDCTL_DSP_SETPLAYVOL;
2348 getreq = SNDCTL_DSP_GETPLAYVOL;
2349 }else if(This->dataflow == eCapture){
2350 setreq = SNDCTL_DSP_SETRECVOL;
2351 getreq = SNDCTL_DSP_GETRECVOL;
2352 }else
2353 return E_UNEXPECTED;
2355 if(ioctl(This->fd, getreq, &vol) < 0){
2356 if(errno == EINVAL)
2357 /* device doesn't support this call */
2358 return S_OK;
2360 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2361 return E_FAIL;
2364 level = This->session->master_vol * This->session->channel_vols[index] *
2365 This->vols[index];
2366 l = level * 100;
2367 if(index == 0)
2368 vol = l | (vol & 0xFF00);
2369 else
2370 vol = (vol & 0xFF) | (l << 8);
2372 if(ioctl(This->fd, setreq, &vol) < 0){
2373 if(errno == EINVAL)
2374 /* device doesn't support this call */
2375 return S_OK;
2377 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2378 return E_FAIL;
2381 return S_OK;
2384 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2386 HRESULT ret = S_OK;
2387 ACImpl *client;
2389 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2390 HRESULT hr;
2391 hr = oss_setvol(client, index);
2392 if(FAILED(hr))
2393 ret = hr;
2396 return ret;
2399 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2400 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2402 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2404 if(!ppv)
2405 return E_POINTER;
2406 *ppv = NULL;
2408 if(IsEqualIID(riid, &IID_IUnknown) ||
2409 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2410 *ppv = iface;
2411 if(*ppv){
2412 IUnknown_AddRef((IUnknown*)*ppv);
2413 return S_OK;
2416 WARN("Unknown interface %s\n", debugstr_guid(riid));
2417 return E_NOINTERFACE;
2420 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2422 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2423 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2426 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2428 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2429 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2432 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2433 ISimpleAudioVolume *iface, float level, const GUID *context)
2435 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2436 AudioSession *session = This->session;
2437 HRESULT ret;
2439 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2441 if(level < 0.f || level > 1.f)
2442 return E_INVALIDARG;
2444 if(context)
2445 FIXME("Notifications not supported yet\n");
2447 EnterCriticalSection(&session->lock);
2449 session->master_vol = level;
2451 ret = oss_session_setvol(session, -1);
2453 LeaveCriticalSection(&session->lock);
2455 return ret;
2458 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2459 ISimpleAudioVolume *iface, float *level)
2461 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2462 AudioSession *session = This->session;
2464 TRACE("(%p)->(%p)\n", session, level);
2466 if(!level)
2467 return NULL_PTR_ERR;
2469 *level = session->master_vol;
2471 return S_OK;
2474 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2475 BOOL mute, const GUID *context)
2477 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2478 AudioSession *session = This->session;
2480 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2482 EnterCriticalSection(&session->lock);
2484 if(!mute && session->mute){
2485 ACImpl *client;
2487 session->mute = mute;
2489 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2490 EnterCriticalSection(&client->lock);
2491 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2492 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2493 strerror(errno));
2494 oss_write_data(client);
2495 LeaveCriticalSection(&client->lock);
2497 }else
2498 session->mute = mute;
2500 LeaveCriticalSection(&session->lock);
2502 return S_OK;
2505 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2506 BOOL *mute)
2508 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2509 AudioSession *session = This->session;
2511 TRACE("(%p)->(%p)\n", session, mute);
2513 if(!mute)
2514 return NULL_PTR_ERR;
2516 *mute = This->session->mute;
2518 return S_OK;
2521 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2523 SimpleAudioVolume_QueryInterface,
2524 SimpleAudioVolume_AddRef,
2525 SimpleAudioVolume_Release,
2526 SimpleAudioVolume_SetMasterVolume,
2527 SimpleAudioVolume_GetMasterVolume,
2528 SimpleAudioVolume_SetMute,
2529 SimpleAudioVolume_GetMute
2532 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2533 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2535 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2537 if(!ppv)
2538 return E_POINTER;
2539 *ppv = NULL;
2541 if(IsEqualIID(riid, &IID_IUnknown) ||
2542 IsEqualIID(riid, &IID_IAudioStreamVolume))
2543 *ppv = iface;
2544 if(*ppv){
2545 IUnknown_AddRef((IUnknown*)*ppv);
2546 return S_OK;
2549 WARN("Unknown interface %s\n", debugstr_guid(riid));
2550 return E_NOINTERFACE;
2553 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2555 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2556 return IAudioClient_AddRef(&This->IAudioClient_iface);
2559 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2561 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2562 return IAudioClient_Release(&This->IAudioClient_iface);
2565 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2566 IAudioStreamVolume *iface, UINT32 *out)
2568 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2570 TRACE("(%p)->(%p)\n", This, out);
2572 if(!out)
2573 return E_POINTER;
2575 *out = This->fmt->nChannels;
2577 return S_OK;
2580 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2581 IAudioStreamVolume *iface, UINT32 index, float level)
2583 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2584 HRESULT ret;
2586 TRACE("(%p)->(%d, %f)\n", This, index, level);
2588 if(level < 0.f || level > 1.f)
2589 return E_INVALIDARG;
2591 if(index >= This->fmt->nChannels)
2592 return E_INVALIDARG;
2594 EnterCriticalSection(&This->lock);
2596 This->vols[index] = level;
2598 ret = oss_setvol(This, index);
2600 LeaveCriticalSection(&This->lock);
2602 return ret;
2605 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2606 IAudioStreamVolume *iface, UINT32 index, float *level)
2608 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2610 TRACE("(%p)->(%d, %p)\n", This, index, level);
2612 if(!level)
2613 return E_POINTER;
2615 if(index >= This->fmt->nChannels)
2616 return E_INVALIDARG;
2618 *level = This->vols[index];
2620 return S_OK;
2623 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2624 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2626 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2627 int i;
2628 HRESULT ret;
2630 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2632 if(!levels)
2633 return E_POINTER;
2635 if(count != This->fmt->nChannels)
2636 return E_INVALIDARG;
2638 EnterCriticalSection(&This->lock);
2640 for(i = 0; i < count; ++i)
2641 This->vols[i] = levels[i];
2643 ret = oss_setvol(This, -1);
2645 LeaveCriticalSection(&This->lock);
2647 return ret;
2650 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2651 IAudioStreamVolume *iface, UINT32 count, float *levels)
2653 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2654 int i;
2656 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2658 if(!levels)
2659 return E_POINTER;
2661 if(count != This->fmt->nChannels)
2662 return E_INVALIDARG;
2664 EnterCriticalSection(&This->lock);
2666 for(i = 0; i < count; ++i)
2667 levels[i] = This->vols[i];
2669 LeaveCriticalSection(&This->lock);
2671 return S_OK;
2674 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2676 AudioStreamVolume_QueryInterface,
2677 AudioStreamVolume_AddRef,
2678 AudioStreamVolume_Release,
2679 AudioStreamVolume_GetChannelCount,
2680 AudioStreamVolume_SetChannelVolume,
2681 AudioStreamVolume_GetChannelVolume,
2682 AudioStreamVolume_SetAllVolumes,
2683 AudioStreamVolume_GetAllVolumes
2686 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2687 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2689 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2691 if(!ppv)
2692 return E_POINTER;
2693 *ppv = NULL;
2695 if(IsEqualIID(riid, &IID_IUnknown) ||
2696 IsEqualIID(riid, &IID_IChannelAudioVolume))
2697 *ppv = iface;
2698 if(*ppv){
2699 IUnknown_AddRef((IUnknown*)*ppv);
2700 return S_OK;
2703 WARN("Unknown interface %s\n", debugstr_guid(riid));
2704 return E_NOINTERFACE;
2707 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2709 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2710 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2713 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2715 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2716 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2719 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2720 IChannelAudioVolume *iface, UINT32 *out)
2722 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2723 AudioSession *session = This->session;
2725 TRACE("(%p)->(%p)\n", session, out);
2727 if(!out)
2728 return NULL_PTR_ERR;
2730 *out = session->channel_count;
2732 return S_OK;
2735 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2736 IChannelAudioVolume *iface, UINT32 index, float level,
2737 const GUID *context)
2739 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2740 AudioSession *session = This->session;
2741 HRESULT ret;
2743 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2744 wine_dbgstr_guid(context));
2746 if(level < 0.f || level > 1.f)
2747 return E_INVALIDARG;
2749 if(index >= session->channel_count)
2750 return E_INVALIDARG;
2752 if(context)
2753 FIXME("Notifications not supported yet\n");
2755 EnterCriticalSection(&session->lock);
2757 session->channel_vols[index] = level;
2759 ret = oss_session_setvol(session, index);
2761 LeaveCriticalSection(&session->lock);
2763 return ret;
2766 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2767 IChannelAudioVolume *iface, UINT32 index, float *level)
2769 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2770 AudioSession *session = This->session;
2772 TRACE("(%p)->(%d, %p)\n", session, index, level);
2774 if(!level)
2775 return NULL_PTR_ERR;
2777 if(index >= session->channel_count)
2778 return E_INVALIDARG;
2780 *level = session->channel_vols[index];
2782 return S_OK;
2785 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2786 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2787 const GUID *context)
2789 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2790 AudioSession *session = This->session;
2791 int i;
2792 HRESULT ret;
2794 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2795 wine_dbgstr_guid(context));
2797 if(!levels)
2798 return NULL_PTR_ERR;
2800 if(count != session->channel_count)
2801 return E_INVALIDARG;
2803 if(context)
2804 FIXME("Notifications not supported yet\n");
2806 EnterCriticalSection(&session->lock);
2808 for(i = 0; i < count; ++i)
2809 session->channel_vols[i] = levels[i];
2811 ret = oss_session_setvol(session, -1);
2813 LeaveCriticalSection(&session->lock);
2815 return ret;
2818 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2819 IChannelAudioVolume *iface, UINT32 count, float *levels)
2821 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2822 AudioSession *session = This->session;
2823 int i;
2825 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2827 if(!levels)
2828 return NULL_PTR_ERR;
2830 if(count != session->channel_count)
2831 return E_INVALIDARG;
2833 for(i = 0; i < count; ++i)
2834 levels[i] = session->channel_vols[i];
2836 return S_OK;
2839 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2841 ChannelAudioVolume_QueryInterface,
2842 ChannelAudioVolume_AddRef,
2843 ChannelAudioVolume_Release,
2844 ChannelAudioVolume_GetChannelCount,
2845 ChannelAudioVolume_SetChannelVolume,
2846 ChannelAudioVolume_GetChannelVolume,
2847 ChannelAudioVolume_SetAllVolumes,
2848 ChannelAudioVolume_GetAllVolumes
2851 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2852 REFIID riid, void **ppv)
2854 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2856 if(!ppv)
2857 return E_POINTER;
2858 *ppv = NULL;
2860 if(IsEqualIID(riid, &IID_IUnknown) ||
2861 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2862 IsEqualIID(riid, &IID_IAudioSessionManager2))
2863 *ppv = iface;
2864 if(*ppv){
2865 IUnknown_AddRef((IUnknown*)*ppv);
2866 return S_OK;
2869 WARN("Unknown interface %s\n", debugstr_guid(riid));
2870 return E_NOINTERFACE;
2873 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2875 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2876 ULONG ref;
2877 ref = InterlockedIncrement(&This->ref);
2878 TRACE("(%p) Refcount now %u\n", This, ref);
2879 return ref;
2882 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2884 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2885 ULONG ref;
2886 ref = InterlockedDecrement(&This->ref);
2887 TRACE("(%p) Refcount now %u\n", This, ref);
2888 if(!ref)
2889 HeapFree(GetProcessHeap(), 0, This);
2890 return ref;
2893 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2894 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2895 IAudioSessionControl **out)
2897 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2898 AudioSession *session;
2899 AudioSessionWrapper *wrapper;
2900 HRESULT hr;
2902 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2903 flags, out);
2905 hr = get_audio_session(session_guid, This->device, 0, &session);
2906 if(FAILED(hr))
2907 return hr;
2909 wrapper = AudioSessionWrapper_Create(NULL);
2910 if(!wrapper)
2911 return E_OUTOFMEMORY;
2913 wrapper->session = session;
2915 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2917 return S_OK;
2920 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2921 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2922 ISimpleAudioVolume **out)
2924 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2925 AudioSession *session;
2926 AudioSessionWrapper *wrapper;
2927 HRESULT hr;
2929 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2930 flags, out);
2932 hr = get_audio_session(session_guid, This->device, 0, &session);
2933 if(FAILED(hr))
2934 return hr;
2936 wrapper = AudioSessionWrapper_Create(NULL);
2937 if(!wrapper)
2938 return E_OUTOFMEMORY;
2940 wrapper->session = session;
2942 *out = &wrapper->ISimpleAudioVolume_iface;
2944 return S_OK;
2947 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2948 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2950 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2951 FIXME("(%p)->(%p) - stub\n", This, out);
2952 return E_NOTIMPL;
2955 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2956 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2958 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2959 FIXME("(%p)->(%p) - stub\n", This, notification);
2960 return E_NOTIMPL;
2963 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2964 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2966 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2967 FIXME("(%p)->(%p) - stub\n", This, notification);
2968 return E_NOTIMPL;
2971 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2972 IAudioSessionManager2 *iface, const WCHAR *session_id,
2973 IAudioVolumeDuckNotification *notification)
2975 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2976 FIXME("(%p)->(%p) - stub\n", This, notification);
2977 return E_NOTIMPL;
2980 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2981 IAudioSessionManager2 *iface,
2982 IAudioVolumeDuckNotification *notification)
2984 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2985 FIXME("(%p)->(%p) - stub\n", This, notification);
2986 return E_NOTIMPL;
2989 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2991 AudioSessionManager_QueryInterface,
2992 AudioSessionManager_AddRef,
2993 AudioSessionManager_Release,
2994 AudioSessionManager_GetAudioSessionControl,
2995 AudioSessionManager_GetSimpleAudioVolume,
2996 AudioSessionManager_GetSessionEnumerator,
2997 AudioSessionManager_RegisterSessionNotification,
2998 AudioSessionManager_UnregisterSessionNotification,
2999 AudioSessionManager_RegisterDuckNotification,
3000 AudioSessionManager_UnregisterDuckNotification
3003 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3004 IAudioSessionManager2 **out)
3006 SessionMgr *This;
3008 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3009 if(!This)
3010 return E_OUTOFMEMORY;
3012 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3013 This->device = device;
3014 This->ref = 1;
3016 *out = &This->IAudioSessionManager2_iface;
3018 return S_OK;