ddraw: Use unsafe_impl_from_IDirect3DViewport for application provided interfaces.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob5de0a156e2074b6f3c0641157a0bcd2250d43eba
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;
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(int fd, const WAVEFORMATEX *fmt,
671 WAVEFORMATEX **out, BOOL query)
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(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(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(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(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
727 fmtex->dwChannelMask != 0 &&
728 fmtex->dwChannelMask != mask)
729 ret = S_FALSE;
732 if(ret == S_FALSE && out){
733 closest->nBlockAlign =
734 closest->nChannels * closest->wBitsPerSample / 8;
735 closest->nAvgBytesPerSec =
736 closest->nBlockAlign * closest->nSamplesPerSec;
737 *out = closest;
738 } else
739 CoTaskMemFree(closest);
741 TRACE("returning: %08x\n", ret);
742 return ret;
745 static void session_init_vols(AudioSession *session, UINT channels)
747 if(session->channel_count < channels){
748 UINT i;
750 if(session->channel_vols)
751 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
752 session->channel_vols, sizeof(float) * channels);
753 else
754 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
755 sizeof(float) * channels);
756 if(!session->channel_vols)
757 return;
759 for(i = session->channel_count; i < channels; ++i)
760 session->channel_vols[i] = 1.f;
762 session->channel_count = channels;
766 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
767 UINT num_channels)
769 AudioSession *ret;
771 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
772 if(!ret)
773 return NULL;
775 memcpy(&ret->guid, guid, sizeof(GUID));
777 ret->device = device;
779 list_init(&ret->clients);
781 list_add_head(&g_sessions, &ret->entry);
783 InitializeCriticalSection(&ret->lock);
785 session_init_vols(ret, num_channels);
787 ret->master_vol = 1.f;
789 return ret;
792 /* if channels == 0, then this will return or create a session with
793 * matching dataflow and GUID. otherwise, channels must also match */
794 static HRESULT get_audio_session(const GUID *sessionguid,
795 IMMDevice *device, UINT channels, AudioSession **out)
797 AudioSession *session;
799 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
800 *out = create_session(&GUID_NULL, device, channels);
801 if(!*out)
802 return E_OUTOFMEMORY;
804 return S_OK;
807 *out = NULL;
808 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
809 if(session->device == device &&
810 IsEqualGUID(sessionguid, &session->guid)){
811 session_init_vols(session, channels);
812 *out = session;
813 break;
817 if(!*out){
818 *out = create_session(sessionguid, device, channels);
819 if(!*out)
820 return E_OUTOFMEMORY;
823 return S_OK;
826 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
827 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
828 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
829 const GUID *sessionguid)
831 ACImpl *This = impl_from_IAudioClient(iface);
832 int mask, i;
833 HRESULT hr;
835 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
836 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
838 if(!fmt)
839 return E_POINTER;
841 dump_fmt(fmt);
843 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
844 return AUDCLNT_E_NOT_INITIALIZED;
846 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
847 AUDCLNT_STREAMFLAGS_LOOPBACK |
848 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
849 AUDCLNT_STREAMFLAGS_NOPERSIST |
850 AUDCLNT_STREAMFLAGS_RATEADJUST |
851 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
852 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
853 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
854 TRACE("Unknown flags: %08x\n", flags);
855 return E_INVALIDARG;
858 EnterCriticalSection(&This->lock);
860 if(This->initted){
861 LeaveCriticalSection(&This->lock);
862 return AUDCLNT_E_ALREADY_INITIALIZED;
865 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
866 if(hr == S_FALSE){
867 LeaveCriticalSection(&This->lock);
868 return AUDCLNT_E_UNSUPPORTED_FORMAT;
870 if(FAILED(hr)){
871 LeaveCriticalSection(&This->lock);
872 return hr;
875 mask = 0;
876 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
877 LeaveCriticalSection(&This->lock);
878 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
879 return E_FAIL;
882 mask = (100 << 8) | 100;
883 if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
884 WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
886 This->fmt = clone_format(fmt);
887 if(!This->fmt){
888 LeaveCriticalSection(&This->lock);
889 return E_OUTOFMEMORY;
892 if(period)
893 This->period_us = period / 10;
894 else
895 This->period_us = DefaultPeriod / 10;
897 if(!duration)
898 duration = 300000; /* 0.03s */
899 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
900 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
901 This->bufsize_frames * fmt->nBlockAlign);
902 if(!This->local_buffer){
903 CoTaskMemFree(This->fmt);
904 This->fmt = NULL;
905 LeaveCriticalSection(&This->lock);
906 return E_OUTOFMEMORY;
909 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
910 if(!This->vols){
911 CoTaskMemFree(This->fmt);
912 This->fmt = NULL;
913 LeaveCriticalSection(&This->lock);
914 return E_OUTOFMEMORY;
917 for(i = 0; i < fmt->nChannels; ++i)
918 This->vols[i] = 1.f;
920 This->share = mode;
921 This->flags = flags;
923 EnterCriticalSection(&g_sessions_lock);
925 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
926 &This->session);
927 if(FAILED(hr)){
928 LeaveCriticalSection(&g_sessions_lock);
929 HeapFree(GetProcessHeap(), 0, This->vols);
930 This->vols = NULL;
931 CoTaskMemFree(This->fmt);
932 This->fmt = NULL;
933 LeaveCriticalSection(&This->lock);
934 return hr;
937 list_add_tail(&This->session->clients, &This->entry);
939 LeaveCriticalSection(&g_sessions_lock);
941 This->initted = TRUE;
943 oss_setvol(This, -1);
945 LeaveCriticalSection(&This->lock);
947 return S_OK;
950 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
951 UINT32 *frames)
953 ACImpl *This = impl_from_IAudioClient(iface);
955 TRACE("(%p)->(%p)\n", This, frames);
957 if(!frames)
958 return E_POINTER;
960 EnterCriticalSection(&This->lock);
962 if(!This->initted){
963 LeaveCriticalSection(&This->lock);
964 return AUDCLNT_E_NOT_INITIALIZED;
967 *frames = This->bufsize_frames;
969 LeaveCriticalSection(&This->lock);
971 return S_OK;
974 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
975 REFERENCE_TIME *latency)
977 ACImpl *This = impl_from_IAudioClient(iface);
979 TRACE("(%p)->(%p)\n", This, latency);
981 if(!latency)
982 return E_POINTER;
984 EnterCriticalSection(&This->lock);
986 if(!This->initted){
987 LeaveCriticalSection(&This->lock);
988 return AUDCLNT_E_NOT_INITIALIZED;
991 if(This->dataflow == eRender){
992 int delay_bytes;
993 double delay_s;
995 if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
996 LeaveCriticalSection(&This->lock);
997 WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
998 return E_FAIL;
1001 delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
1002 This->fmt->nBlockAlign);
1004 *latency = delay_s * 10000000;
1005 }else
1006 *latency = 10000; /* OSS doesn't provide input latency */
1008 LeaveCriticalSection(&This->lock);
1010 return S_OK;
1013 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1014 UINT32 *numpad)
1016 ACImpl *This = impl_from_IAudioClient(iface);
1017 audio_buf_info bi;
1019 TRACE("(%p)->(%p)\n", This, numpad);
1021 if(!numpad)
1022 return E_POINTER;
1024 EnterCriticalSection(&This->lock);
1026 if(!This->initted){
1027 LeaveCriticalSection(&This->lock);
1028 return AUDCLNT_E_NOT_INITIALIZED;
1031 if(This->dataflow == eRender){
1032 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1033 LeaveCriticalSection(&This->lock);
1034 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1035 return E_FAIL;
1038 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1039 This->fmt->nBlockAlign;
1041 /* when the OSS buffer has less than one fragment of data, including
1042 * no data, it often reports it as some non-zero portion of a
1043 * fragment. when it has more than one fragment of data, it reports
1044 * it as some multiple of that portion of the fragment size.
1046 * so, we have to do some ugly workarounds to report the timing
1047 * as accurately as possible */
1048 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1049 *numpad = This->inbuf_frames;
1050 This->inbuf_frames = 0;
1051 }else{
1052 if(*numpad < This->inbuf_frames)
1053 This->inbuf_frames = *numpad;
1054 else
1055 *numpad = This->inbuf_frames;
1057 }else if(This->dataflow == eCapture){
1058 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1059 LeaveCriticalSection(&This->lock);
1060 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1061 return E_FAIL;
1064 if(bi.bytes <= bi.fragsize)
1065 *numpad = 0;
1066 else
1067 *numpad = bi.bytes / This->fmt->nBlockAlign;
1068 }else{
1069 LeaveCriticalSection(&This->lock);
1070 return E_UNEXPECTED;
1073 *numpad += This->held_frames;
1075 LeaveCriticalSection(&This->lock);
1077 return S_OK;
1080 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1081 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1082 WAVEFORMATEX **outpwfx)
1084 ACImpl *This = impl_from_IAudioClient(iface);
1085 int fd = -1;
1086 HRESULT ret;
1088 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1090 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1091 return E_POINTER;
1093 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1094 return E_INVALIDARG;
1096 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1097 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1098 return E_INVALIDARG;
1100 dump_fmt(pwfx);
1102 if(This->dataflow == eRender)
1103 fd = open(This->ai.devnode, O_WRONLY, 0);
1104 else if(This->dataflow == eCapture)
1105 fd = open(This->ai.devnode, O_RDONLY, 0);
1107 if(fd < 0){
1108 ERR("Unable to open device %s: %d (%s)\n", This->ai.devnode, errno,
1109 strerror(errno));
1110 return AUDCLNT_E_DEVICE_INVALIDATED;
1113 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1115 close(fd);
1117 return ret;
1120 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1121 WAVEFORMATEX **pwfx)
1123 ACImpl *This = impl_from_IAudioClient(iface);
1124 WAVEFORMATEXTENSIBLE *fmt;
1125 int formats;
1127 TRACE("(%p)->(%p)\n", This, pwfx);
1129 if(!pwfx)
1130 return E_POINTER;
1131 *pwfx = NULL;
1133 if(This->dataflow == eRender)
1134 formats = This->ai.oformats;
1135 else if(This->dataflow == eCapture)
1136 formats = This->ai.iformats;
1137 else
1138 return E_UNEXPECTED;
1140 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1141 if(!fmt)
1142 return E_OUTOFMEMORY;
1144 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1145 if(formats & AFMT_S16_LE){
1146 fmt->Format.wBitsPerSample = 16;
1147 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1148 #ifdef AFMT_FLOAT
1149 }else if(formats & AFMT_FLOAT){
1150 fmt->Format.wBitsPerSample = 32;
1151 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1152 #endif
1153 }else if(formats & AFMT_U8){
1154 fmt->Format.wBitsPerSample = 8;
1155 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1156 }else if(formats & AFMT_S32_LE){
1157 fmt->Format.wBitsPerSample = 32;
1158 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1159 }else if(formats & AFMT_S24_LE){
1160 fmt->Format.wBitsPerSample = 24;
1161 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1162 }else{
1163 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1164 CoTaskMemFree(fmt);
1165 return E_FAIL;
1168 fmt->Format.nChannels = This->ai.max_channels;
1169 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1170 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1172 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1173 fmt->Format.nChannels) / 8;
1174 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1175 fmt->Format.nBlockAlign;
1177 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1178 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1180 *pwfx = (WAVEFORMATEX*)fmt;
1181 dump_fmt(*pwfx);
1183 return S_OK;
1186 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1187 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1189 ACImpl *This = impl_from_IAudioClient(iface);
1191 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1193 if(!defperiod && !minperiod)
1194 return E_POINTER;
1196 EnterCriticalSection(&This->lock);
1198 if(defperiod)
1199 *defperiod = DefaultPeriod;
1200 if(minperiod)
1201 *minperiod = MinimumPeriod;
1203 LeaveCriticalSection(&This->lock);
1205 return S_OK;
1208 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1210 if(This->fmt->wBitsPerSample == 8)
1211 memset(buf, 128, frames * This->fmt->nBlockAlign);
1212 else
1213 memset(buf, 0, frames * This->fmt->nBlockAlign);
1216 static void oss_write_data(ACImpl *This)
1218 ssize_t written;
1219 UINT32 written_frames;
1220 size_t to_write;
1221 BYTE *buf =
1222 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1224 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1225 to_write = This->bufsize_frames - This->lcl_offs_frames;
1226 else
1227 to_write = This->held_frames;
1229 if(This->session->mute)
1230 oss_silence_buffer(This, buf, to_write);
1232 written = write(This->fd, buf, to_write * This->fmt->nBlockAlign);
1233 if(written < 0){
1234 /* EAGAIN is OSS buffer full, log that too */
1235 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1236 return;
1238 written_frames = written / This->fmt->nBlockAlign;
1240 This->lcl_offs_frames += written_frames;
1241 This->lcl_offs_frames %= This->bufsize_frames;
1242 This->held_frames -= written_frames;
1243 This->inbuf_frames += written_frames;
1245 if(written_frames < to_write){
1246 /* OSS buffer probably full */
1247 return;
1250 if(This->held_frames){
1251 /* wrapped and have some data back at the start to write */
1253 if(This->session->mute)
1254 oss_silence_buffer(This, This->local_buffer, This->held_frames);
1256 written = write(This->fd, This->local_buffer,
1257 This->held_frames * This->fmt->nBlockAlign);
1258 if(written < 0){
1259 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1260 return;
1262 written_frames = written / This->fmt->nBlockAlign;
1264 This->lcl_offs_frames += written_frames;
1265 This->lcl_offs_frames %= This->bufsize_frames;
1266 This->held_frames -= written_frames;
1267 This->inbuf_frames += written_frames;
1271 static void oss_read_data(ACImpl *This)
1273 UINT64 pos, readable;
1274 audio_buf_info bi;
1275 ssize_t nread;
1277 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1278 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1279 return;
1282 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1283 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1285 if(bi.bytes < readable)
1286 readable = bi.bytes;
1288 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1289 readable);
1290 if(nread < 0){
1291 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1292 return;
1295 This->held_frames += nread / This->fmt->nBlockAlign;
1297 if(This->held_frames > This->bufsize_frames){
1298 WARN("Overflow of unread data\n");
1299 This->lcl_offs_frames += This->held_frames;
1300 This->lcl_offs_frames %= This->bufsize_frames;
1301 This->held_frames = This->bufsize_frames;
1305 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1307 ACImpl *This = user;
1309 EnterCriticalSection(&This->lock);
1311 if(This->dataflow == eRender && This->held_frames)
1312 oss_write_data(This);
1313 else if(This->dataflow == eCapture)
1314 oss_read_data(This);
1316 if(This->event)
1317 SetEvent(This->event);
1319 LeaveCriticalSection(&This->lock);
1322 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1324 ACImpl *This = impl_from_IAudioClient(iface);
1325 int mask;
1327 TRACE("(%p)\n", This);
1329 EnterCriticalSection(&This->lock);
1331 if(!This->initted){
1332 LeaveCriticalSection(&This->lock);
1333 return AUDCLNT_E_NOT_INITIALIZED;
1336 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1337 LeaveCriticalSection(&This->lock);
1338 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1341 if(This->playing){
1342 LeaveCriticalSection(&This->lock);
1343 return AUDCLNT_E_NOT_STOPPED;
1346 if(This->dataflow == eRender)
1347 mask = PCM_ENABLE_OUTPUT;
1348 else if(This->dataflow == eCapture)
1349 mask = PCM_ENABLE_INPUT;
1350 else{
1351 LeaveCriticalSection(&This->lock);
1352 return E_UNEXPECTED;
1355 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1356 LeaveCriticalSection(&This->lock);
1357 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1358 return E_FAIL;
1361 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1362 oss_period_callback, This, 0, This->period_us / 1000,
1363 WT_EXECUTEINTIMERTHREAD))
1364 ERR("Unable to create period timer: %u\n", GetLastError());
1366 This->playing = TRUE;
1368 LeaveCriticalSection(&This->lock);
1370 return S_OK;
1373 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1375 ACImpl *This = impl_from_IAudioClient(iface);
1376 int mask;
1378 TRACE("(%p)\n", This);
1380 EnterCriticalSection(&This->lock);
1382 if(!This->initted){
1383 LeaveCriticalSection(&This->lock);
1384 return AUDCLNT_E_NOT_INITIALIZED;
1387 if(!This->playing){
1388 LeaveCriticalSection(&This->lock);
1389 return S_FALSE;
1392 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1393 DeleteTimerQueueTimer(g_timer_q, This->timer,
1394 INVALID_HANDLE_VALUE);
1395 This->timer = NULL;
1398 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1399 LeaveCriticalSection(&This->lock);
1400 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1401 return E_FAIL;
1404 mask = 0;
1405 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1406 LeaveCriticalSection(&This->lock);
1407 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1408 return E_FAIL;
1411 This->playing = FALSE;
1413 LeaveCriticalSection(&This->lock);
1415 return S_OK;
1418 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1420 ACImpl *This = impl_from_IAudioClient(iface);
1422 TRACE("(%p)\n", This);
1424 EnterCriticalSection(&This->lock);
1426 if(!This->initted){
1427 LeaveCriticalSection(&This->lock);
1428 return AUDCLNT_E_NOT_INITIALIZED;
1431 if(This->playing){
1432 LeaveCriticalSection(&This->lock);
1433 return AUDCLNT_E_NOT_STOPPED;
1436 if(This->buf_state != NOT_LOCKED){
1437 LeaveCriticalSection(&This->lock);
1438 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1441 This->written_frames = 0;
1442 This->inbuf_frames = 0;
1443 This->held_frames = 0;
1445 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1446 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1448 LeaveCriticalSection(&This->lock);
1450 return S_OK;
1453 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1454 HANDLE event)
1456 ACImpl *This = impl_from_IAudioClient(iface);
1458 TRACE("(%p)->(%p)\n", This, event);
1460 if(!event)
1461 return E_INVALIDARG;
1463 EnterCriticalSection(&This->lock);
1465 if(!This->initted){
1466 LeaveCriticalSection(&This->lock);
1467 return AUDCLNT_E_NOT_INITIALIZED;
1470 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1471 LeaveCriticalSection(&This->lock);
1472 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1475 This->event = event;
1477 LeaveCriticalSection(&This->lock);
1479 return S_OK;
1482 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1483 void **ppv)
1485 ACImpl *This = impl_from_IAudioClient(iface);
1487 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1489 if(!ppv)
1490 return E_POINTER;
1491 *ppv = NULL;
1493 EnterCriticalSection(&This->lock);
1495 if(!This->initted){
1496 LeaveCriticalSection(&This->lock);
1497 return AUDCLNT_E_NOT_INITIALIZED;
1500 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1501 if(This->dataflow != eRender){
1502 LeaveCriticalSection(&This->lock);
1503 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1505 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1506 *ppv = &This->IAudioRenderClient_iface;
1507 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1508 if(This->dataflow != eCapture){
1509 LeaveCriticalSection(&This->lock);
1510 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1512 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1513 *ppv = &This->IAudioCaptureClient_iface;
1514 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1515 IAudioClock_AddRef(&This->IAudioClock_iface);
1516 *ppv = &This->IAudioClock_iface;
1517 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1518 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1519 *ppv = &This->IAudioStreamVolume_iface;
1520 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1521 if(!This->session_wrapper){
1522 This->session_wrapper = AudioSessionWrapper_Create(This);
1523 if(!This->session_wrapper){
1524 LeaveCriticalSection(&This->lock);
1525 return E_OUTOFMEMORY;
1527 }else
1528 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1530 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1531 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1532 if(!This->session_wrapper){
1533 This->session_wrapper = AudioSessionWrapper_Create(This);
1534 if(!This->session_wrapper){
1535 LeaveCriticalSection(&This->lock);
1536 return E_OUTOFMEMORY;
1538 }else
1539 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1541 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1542 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1543 if(!This->session_wrapper){
1544 This->session_wrapper = AudioSessionWrapper_Create(This);
1545 if(!This->session_wrapper){
1546 LeaveCriticalSection(&This->lock);
1547 return E_OUTOFMEMORY;
1549 }else
1550 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1552 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1555 if(*ppv){
1556 LeaveCriticalSection(&This->lock);
1557 return S_OK;
1560 LeaveCriticalSection(&This->lock);
1562 FIXME("stub %s\n", debugstr_guid(riid));
1563 return E_NOINTERFACE;
1566 static const IAudioClientVtbl AudioClient_Vtbl =
1568 AudioClient_QueryInterface,
1569 AudioClient_AddRef,
1570 AudioClient_Release,
1571 AudioClient_Initialize,
1572 AudioClient_GetBufferSize,
1573 AudioClient_GetStreamLatency,
1574 AudioClient_GetCurrentPadding,
1575 AudioClient_IsFormatSupported,
1576 AudioClient_GetMixFormat,
1577 AudioClient_GetDevicePeriod,
1578 AudioClient_Start,
1579 AudioClient_Stop,
1580 AudioClient_Reset,
1581 AudioClient_SetEventHandle,
1582 AudioClient_GetService
1585 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1586 IAudioRenderClient *iface, REFIID riid, void **ppv)
1588 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1590 if(!ppv)
1591 return E_POINTER;
1592 *ppv = NULL;
1594 if(IsEqualIID(riid, &IID_IUnknown) ||
1595 IsEqualIID(riid, &IID_IAudioRenderClient))
1596 *ppv = iface;
1597 if(*ppv){
1598 IUnknown_AddRef((IUnknown*)*ppv);
1599 return S_OK;
1602 WARN("Unknown interface %s\n", debugstr_guid(riid));
1603 return E_NOINTERFACE;
1606 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1608 ACImpl *This = impl_from_IAudioRenderClient(iface);
1609 return AudioClient_AddRef(&This->IAudioClient_iface);
1612 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1614 ACImpl *This = impl_from_IAudioRenderClient(iface);
1615 return AudioClient_Release(&This->IAudioClient_iface);
1618 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1619 UINT32 frames, BYTE **data)
1621 ACImpl *This = impl_from_IAudioRenderClient(iface);
1622 UINT32 pad, write_pos;
1623 HRESULT hr;
1625 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1627 if(!data)
1628 return E_POINTER;
1630 EnterCriticalSection(&This->lock);
1632 if(This->buf_state != NOT_LOCKED){
1633 LeaveCriticalSection(&This->lock);
1634 return AUDCLNT_E_OUT_OF_ORDER;
1637 if(!frames){
1638 This->buf_state = LOCKED_NORMAL;
1639 LeaveCriticalSection(&This->lock);
1640 return S_OK;
1643 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1644 if(FAILED(hr)){
1645 LeaveCriticalSection(&This->lock);
1646 return hr;
1649 if(pad + frames > This->bufsize_frames){
1650 LeaveCriticalSection(&This->lock);
1651 return AUDCLNT_E_BUFFER_TOO_LARGE;
1654 write_pos =
1655 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1656 if(write_pos + frames > This->bufsize_frames){
1657 if(This->tmp_buffer_frames < frames){
1658 if(This->tmp_buffer)
1659 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1660 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1661 else
1662 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1663 frames * This->fmt->nBlockAlign);
1664 if(!This->tmp_buffer){
1665 LeaveCriticalSection(&This->lock);
1666 return E_OUTOFMEMORY;
1668 This->tmp_buffer_frames = frames;
1670 *data = This->tmp_buffer;
1671 This->buf_state = LOCKED_WRAPPED;
1672 }else{
1673 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1674 This->buf_state = LOCKED_NORMAL;
1677 LeaveCriticalSection(&This->lock);
1679 return S_OK;
1682 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1684 UINT32 write_offs_frames =
1685 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1686 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1687 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1688 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1689 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1691 if(written_bytes <= chunk_bytes){
1692 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1693 }else{
1694 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1695 memcpy(This->local_buffer, buffer + chunk_bytes,
1696 written_bytes - chunk_bytes);
1700 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1701 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1703 ACImpl *This = impl_from_IAudioRenderClient(iface);
1704 BYTE *buffer;
1706 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1708 EnterCriticalSection(&This->lock);
1710 if(This->buf_state == NOT_LOCKED || !written_frames){
1711 This->buf_state = NOT_LOCKED;
1712 LeaveCriticalSection(&This->lock);
1713 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1716 if(This->buf_state == LOCKED_NORMAL)
1717 buffer = This->local_buffer + This->fmt->nBlockAlign *
1718 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1719 else
1720 buffer = This->tmp_buffer;
1722 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1723 oss_silence_buffer(This, buffer, written_frames);
1725 if(This->held_frames){
1726 if(This->buf_state == LOCKED_WRAPPED)
1727 oss_wrap_buffer(This, buffer, written_frames);
1729 This->held_frames += written_frames;
1730 }else{
1731 ssize_t w_bytes;
1732 UINT32 w_frames;
1734 if(This->session->mute)
1735 oss_silence_buffer(This, buffer, written_frames);
1737 w_bytes = write(This->fd, buffer,
1738 written_frames * This->fmt->nBlockAlign);
1739 if(w_bytes < 0){
1740 if(errno != EAGAIN){
1741 This->buf_state = NOT_LOCKED;
1742 LeaveCriticalSection(&This->lock);
1743 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1744 return E_FAIL;
1745 }else /* OSS buffer full */
1746 w_bytes = 0;
1748 w_frames = w_bytes / This->fmt->nBlockAlign;
1749 This->inbuf_frames += w_frames;
1751 if(w_frames < written_frames){
1752 if(This->buf_state == LOCKED_WRAPPED)
1753 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1754 written_frames - w_frames);
1755 else
1756 This->lcl_offs_frames += w_frames;
1757 This->held_frames = written_frames - w_frames;
1761 This->written_frames += written_frames;
1762 This->buf_state = NOT_LOCKED;
1764 LeaveCriticalSection(&This->lock);
1766 return S_OK;
1769 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1770 AudioRenderClient_QueryInterface,
1771 AudioRenderClient_AddRef,
1772 AudioRenderClient_Release,
1773 AudioRenderClient_GetBuffer,
1774 AudioRenderClient_ReleaseBuffer
1777 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1778 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1780 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1782 if(!ppv)
1783 return E_POINTER;
1784 *ppv = NULL;
1786 if(IsEqualIID(riid, &IID_IUnknown) ||
1787 IsEqualIID(riid, &IID_IAudioCaptureClient))
1788 *ppv = iface;
1789 if(*ppv){
1790 IUnknown_AddRef((IUnknown*)*ppv);
1791 return S_OK;
1794 WARN("Unknown interface %s\n", debugstr_guid(riid));
1795 return E_NOINTERFACE;
1798 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1800 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1801 return IAudioClient_AddRef(&This->IAudioClient_iface);
1804 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1806 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1807 return IAudioClient_Release(&This->IAudioClient_iface);
1810 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1811 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1812 UINT64 *qpcpos)
1814 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1815 HRESULT hr;
1817 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1818 devpos, qpcpos);
1820 if(!data || !frames || !flags)
1821 return E_POINTER;
1823 EnterCriticalSection(&This->lock);
1825 if(This->buf_state != NOT_LOCKED){
1826 LeaveCriticalSection(&This->lock);
1827 return AUDCLNT_E_OUT_OF_ORDER;
1830 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1831 if(FAILED(hr)){
1832 LeaveCriticalSection(&This->lock);
1833 return hr;
1836 *flags = 0;
1838 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1839 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1840 if(This->tmp_buffer_frames < *frames){
1841 if(This->tmp_buffer)
1842 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1843 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1844 else
1845 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1846 *frames * This->fmt->nBlockAlign);
1847 if(!This->tmp_buffer){
1848 LeaveCriticalSection(&This->lock);
1849 return E_OUTOFMEMORY;
1851 This->tmp_buffer_frames = *frames;
1854 *data = This->tmp_buffer;
1855 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1856 This->fmt->nBlockAlign;
1857 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1858 frames_bytes = *frames * This->fmt->nBlockAlign;
1859 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1860 memcpy(This->tmp_buffer, This->local_buffer,
1861 frames_bytes - chunk_bytes);
1862 }else
1863 *data = This->local_buffer +
1864 This->lcl_offs_frames * This->fmt->nBlockAlign;
1866 This->buf_state = LOCKED_NORMAL;
1868 if(devpos || qpcpos)
1869 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1871 LeaveCriticalSection(&This->lock);
1873 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1876 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1877 IAudioCaptureClient *iface, UINT32 done)
1879 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1881 TRACE("(%p)->(%u)\n", This, done);
1883 EnterCriticalSection(&This->lock);
1885 if(This->buf_state == NOT_LOCKED){
1886 LeaveCriticalSection(&This->lock);
1887 return AUDCLNT_E_OUT_OF_ORDER;
1890 This->held_frames -= done;
1891 This->lcl_offs_frames += done;
1892 This->lcl_offs_frames %= This->bufsize_frames;
1894 This->buf_state = NOT_LOCKED;
1896 LeaveCriticalSection(&This->lock);
1898 return S_OK;
1901 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1902 IAudioCaptureClient *iface, UINT32 *frames)
1904 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1906 TRACE("(%p)->(%p)\n", This, frames);
1908 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1911 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1913 AudioCaptureClient_QueryInterface,
1914 AudioCaptureClient_AddRef,
1915 AudioCaptureClient_Release,
1916 AudioCaptureClient_GetBuffer,
1917 AudioCaptureClient_ReleaseBuffer,
1918 AudioCaptureClient_GetNextPacketSize
1921 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1922 REFIID riid, void **ppv)
1924 ACImpl *This = impl_from_IAudioClock(iface);
1926 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1928 if(!ppv)
1929 return E_POINTER;
1930 *ppv = NULL;
1932 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1933 *ppv = iface;
1934 else if(IsEqualIID(riid, &IID_IAudioClock2))
1935 *ppv = &This->IAudioClock2_iface;
1936 if(*ppv){
1937 IUnknown_AddRef((IUnknown*)*ppv);
1938 return S_OK;
1941 WARN("Unknown interface %s\n", debugstr_guid(riid));
1942 return E_NOINTERFACE;
1945 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1947 ACImpl *This = impl_from_IAudioClock(iface);
1948 return IAudioClient_AddRef(&This->IAudioClient_iface);
1951 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1953 ACImpl *This = impl_from_IAudioClock(iface);
1954 return IAudioClient_Release(&This->IAudioClient_iface);
1957 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1959 ACImpl *This = impl_from_IAudioClock(iface);
1961 TRACE("(%p)->(%p)\n", This, freq);
1963 *freq = This->fmt->nSamplesPerSec;
1965 return S_OK;
1968 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1969 UINT64 *qpctime)
1971 ACImpl *This = impl_from_IAudioClock(iface);
1972 UINT32 pad;
1973 HRESULT hr;
1975 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1977 if(!pos)
1978 return E_POINTER;
1980 EnterCriticalSection(&This->lock);
1982 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1983 if(FAILED(hr)){
1984 LeaveCriticalSection(&This->lock);
1985 return hr;
1988 if(This->dataflow == eRender)
1989 *pos = This->written_frames - pad;
1990 else if(This->dataflow == eCapture)
1991 *pos = This->written_frames + pad;
1993 LeaveCriticalSection(&This->lock);
1995 if(qpctime){
1996 LARGE_INTEGER stamp, freq;
1997 QueryPerformanceCounter(&stamp);
1998 QueryPerformanceFrequency(&freq);
1999 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2002 return S_OK;
2005 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2006 DWORD *chars)
2008 ACImpl *This = impl_from_IAudioClock(iface);
2010 TRACE("(%p)->(%p)\n", This, chars);
2012 if(!chars)
2013 return E_POINTER;
2015 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2017 return S_OK;
2020 static const IAudioClockVtbl AudioClock_Vtbl =
2022 AudioClock_QueryInterface,
2023 AudioClock_AddRef,
2024 AudioClock_Release,
2025 AudioClock_GetFrequency,
2026 AudioClock_GetPosition,
2027 AudioClock_GetCharacteristics
2030 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2031 REFIID riid, void **ppv)
2033 ACImpl *This = impl_from_IAudioClock2(iface);
2034 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2037 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2039 ACImpl *This = impl_from_IAudioClock2(iface);
2040 return IAudioClient_AddRef(&This->IAudioClient_iface);
2043 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2045 ACImpl *This = impl_from_IAudioClock2(iface);
2046 return IAudioClient_Release(&This->IAudioClient_iface);
2049 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2050 UINT64 *pos, UINT64 *qpctime)
2052 ACImpl *This = impl_from_IAudioClock2(iface);
2054 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2056 return E_NOTIMPL;
2059 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2061 AudioClock2_QueryInterface,
2062 AudioClock2_AddRef,
2063 AudioClock2_Release,
2064 AudioClock2_GetDevicePosition
2067 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2069 AudioSessionWrapper *ret;
2071 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2072 sizeof(AudioSessionWrapper));
2073 if(!ret)
2074 return NULL;
2076 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2077 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2078 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2080 ret->ref = 1;
2082 ret->client = client;
2083 if(client){
2084 ret->session = client->session;
2085 AudioClient_AddRef(&client->IAudioClient_iface);
2088 return ret;
2091 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2092 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2094 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2096 if(!ppv)
2097 return E_POINTER;
2098 *ppv = NULL;
2100 if(IsEqualIID(riid, &IID_IUnknown) ||
2101 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2102 IsEqualIID(riid, &IID_IAudioSessionControl2))
2103 *ppv = iface;
2104 if(*ppv){
2105 IUnknown_AddRef((IUnknown*)*ppv);
2106 return S_OK;
2109 WARN("Unknown interface %s\n", debugstr_guid(riid));
2110 return E_NOINTERFACE;
2113 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2115 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2116 ULONG ref;
2117 ref = InterlockedIncrement(&This->ref);
2118 TRACE("(%p) Refcount now %u\n", This, ref);
2119 return ref;
2122 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2124 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2125 ULONG ref;
2126 ref = InterlockedDecrement(&This->ref);
2127 TRACE("(%p) Refcount now %u\n", This, ref);
2128 if(!ref){
2129 if(This->client){
2130 EnterCriticalSection(&This->client->lock);
2131 This->client->session_wrapper = NULL;
2132 LeaveCriticalSection(&This->client->lock);
2133 AudioClient_Release(&This->client->IAudioClient_iface);
2135 HeapFree(GetProcessHeap(), 0, This);
2137 return ref;
2140 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2141 AudioSessionState *state)
2143 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2144 ACImpl *client;
2146 TRACE("(%p)->(%p)\n", This, state);
2148 if(!state)
2149 return NULL_PTR_ERR;
2151 EnterCriticalSection(&g_sessions_lock);
2153 if(list_empty(&This->session->clients)){
2154 *state = AudioSessionStateExpired;
2155 LeaveCriticalSection(&g_sessions_lock);
2156 return S_OK;
2159 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2160 EnterCriticalSection(&client->lock);
2161 if(client->playing){
2162 *state = AudioSessionStateActive;
2163 LeaveCriticalSection(&client->lock);
2164 LeaveCriticalSection(&g_sessions_lock);
2165 return S_OK;
2167 LeaveCriticalSection(&client->lock);
2170 LeaveCriticalSection(&g_sessions_lock);
2172 *state = AudioSessionStateInactive;
2174 return S_OK;
2177 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2178 IAudioSessionControl2 *iface, WCHAR **name)
2180 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2182 FIXME("(%p)->(%p) - stub\n", This, name);
2184 return E_NOTIMPL;
2187 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2188 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2190 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2192 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2194 return E_NOTIMPL;
2197 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2198 IAudioSessionControl2 *iface, WCHAR **path)
2200 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2202 FIXME("(%p)->(%p) - stub\n", This, path);
2204 return E_NOTIMPL;
2207 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2208 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2210 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2212 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2214 return E_NOTIMPL;
2217 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2218 IAudioSessionControl2 *iface, GUID *group)
2220 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2222 FIXME("(%p)->(%p) - stub\n", This, group);
2224 return E_NOTIMPL;
2227 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2228 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2230 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2232 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2233 debugstr_guid(session));
2235 return E_NOTIMPL;
2238 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2239 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2241 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2243 FIXME("(%p)->(%p) - stub\n", This, events);
2245 return S_OK;
2248 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2249 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2251 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2253 FIXME("(%p)->(%p) - stub\n", This, events);
2255 return S_OK;
2258 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2259 IAudioSessionControl2 *iface, WCHAR **id)
2261 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2263 FIXME("(%p)->(%p) - stub\n", This, id);
2265 return E_NOTIMPL;
2268 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2269 IAudioSessionControl2 *iface, WCHAR **id)
2271 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2273 FIXME("(%p)->(%p) - stub\n", This, id);
2275 return E_NOTIMPL;
2278 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2279 IAudioSessionControl2 *iface, DWORD *pid)
2281 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2283 TRACE("(%p)->(%p)\n", This, pid);
2285 if(!pid)
2286 return E_POINTER;
2288 *pid = GetCurrentProcessId();
2290 return S_OK;
2293 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2294 IAudioSessionControl2 *iface)
2296 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2298 TRACE("(%p)\n", This);
2300 return S_FALSE;
2303 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2304 IAudioSessionControl2 *iface, BOOL optout)
2306 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2308 TRACE("(%p)->(%d)\n", This, optout);
2310 return S_OK;
2313 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2315 AudioSessionControl_QueryInterface,
2316 AudioSessionControl_AddRef,
2317 AudioSessionControl_Release,
2318 AudioSessionControl_GetState,
2319 AudioSessionControl_GetDisplayName,
2320 AudioSessionControl_SetDisplayName,
2321 AudioSessionControl_GetIconPath,
2322 AudioSessionControl_SetIconPath,
2323 AudioSessionControl_GetGroupingParam,
2324 AudioSessionControl_SetGroupingParam,
2325 AudioSessionControl_RegisterAudioSessionNotification,
2326 AudioSessionControl_UnregisterAudioSessionNotification,
2327 AudioSessionControl_GetSessionIdentifier,
2328 AudioSessionControl_GetSessionInstanceIdentifier,
2329 AudioSessionControl_GetProcessId,
2330 AudioSessionControl_IsSystemSoundsSession,
2331 AudioSessionControl_SetDuckingPreference
2334 /* index == -1 means set all channels, otherwise sets only the given channel */
2335 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2337 int setreq, getreq;
2338 unsigned int vol;
2339 unsigned short l;
2340 float level;
2342 if(index == (UINT32)-1){
2343 HRESULT ret = S_OK;
2344 UINT32 i;
2345 for(i = 0; i < This->fmt->nChannels; ++i){
2346 HRESULT hr;
2347 hr = oss_setvol(This, i);
2348 if(FAILED(hr))
2349 ret = hr;
2351 return ret;
2354 if(index > 1)
2355 /* OSS doesn't support volume control past the first two channels */
2356 return S_OK;
2358 if(This->dataflow == eRender){
2359 setreq = SNDCTL_DSP_SETPLAYVOL;
2360 getreq = SNDCTL_DSP_GETPLAYVOL;
2361 }else if(This->dataflow == eCapture){
2362 setreq = SNDCTL_DSP_SETRECVOL;
2363 getreq = SNDCTL_DSP_GETRECVOL;
2364 }else
2365 return E_UNEXPECTED;
2367 if(ioctl(This->fd, getreq, &vol) < 0){
2368 if(errno == EINVAL)
2369 /* device doesn't support this call */
2370 return S_OK;
2372 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2373 return E_FAIL;
2376 level = This->session->master_vol * This->session->channel_vols[index] *
2377 This->vols[index];
2378 l = level * 100;
2379 if(index == 0)
2380 vol = l | (vol & 0xFF00);
2381 else
2382 vol = (vol & 0xFF) | (l << 8);
2384 if(ioctl(This->fd, setreq, &vol) < 0){
2385 if(errno == EINVAL)
2386 /* device doesn't support this call */
2387 return S_OK;
2389 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2390 return E_FAIL;
2393 return S_OK;
2396 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2398 HRESULT ret = S_OK;
2399 ACImpl *client;
2401 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2402 HRESULT hr;
2403 hr = oss_setvol(client, index);
2404 if(FAILED(hr))
2405 ret = hr;
2408 return ret;
2411 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2412 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2414 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2416 if(!ppv)
2417 return E_POINTER;
2418 *ppv = NULL;
2420 if(IsEqualIID(riid, &IID_IUnknown) ||
2421 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2422 *ppv = iface;
2423 if(*ppv){
2424 IUnknown_AddRef((IUnknown*)*ppv);
2425 return S_OK;
2428 WARN("Unknown interface %s\n", debugstr_guid(riid));
2429 return E_NOINTERFACE;
2432 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2434 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2435 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2438 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2440 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2441 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2444 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2445 ISimpleAudioVolume *iface, float level, const GUID *context)
2447 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2448 AudioSession *session = This->session;
2449 HRESULT ret;
2451 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2453 if(level < 0.f || level > 1.f)
2454 return E_INVALIDARG;
2456 if(context)
2457 FIXME("Notifications not supported yet\n");
2459 EnterCriticalSection(&session->lock);
2461 session->master_vol = level;
2463 ret = oss_session_setvol(session, -1);
2465 LeaveCriticalSection(&session->lock);
2467 return ret;
2470 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2471 ISimpleAudioVolume *iface, float *level)
2473 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2474 AudioSession *session = This->session;
2476 TRACE("(%p)->(%p)\n", session, level);
2478 if(!level)
2479 return NULL_PTR_ERR;
2481 *level = session->master_vol;
2483 return S_OK;
2486 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2487 BOOL mute, const GUID *context)
2489 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2490 AudioSession *session = This->session;
2492 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2494 EnterCriticalSection(&session->lock);
2496 if(!mute && session->mute){
2497 ACImpl *client;
2499 session->mute = mute;
2501 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2502 EnterCriticalSection(&client->lock);
2503 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2504 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2505 strerror(errno));
2506 oss_write_data(client);
2507 LeaveCriticalSection(&client->lock);
2509 }else
2510 session->mute = mute;
2512 LeaveCriticalSection(&session->lock);
2514 return S_OK;
2517 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2518 BOOL *mute)
2520 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2521 AudioSession *session = This->session;
2523 TRACE("(%p)->(%p)\n", session, mute);
2525 if(!mute)
2526 return NULL_PTR_ERR;
2528 *mute = This->session->mute;
2530 return S_OK;
2533 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2535 SimpleAudioVolume_QueryInterface,
2536 SimpleAudioVolume_AddRef,
2537 SimpleAudioVolume_Release,
2538 SimpleAudioVolume_SetMasterVolume,
2539 SimpleAudioVolume_GetMasterVolume,
2540 SimpleAudioVolume_SetMute,
2541 SimpleAudioVolume_GetMute
2544 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2545 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2547 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2549 if(!ppv)
2550 return E_POINTER;
2551 *ppv = NULL;
2553 if(IsEqualIID(riid, &IID_IUnknown) ||
2554 IsEqualIID(riid, &IID_IAudioStreamVolume))
2555 *ppv = iface;
2556 if(*ppv){
2557 IUnknown_AddRef((IUnknown*)*ppv);
2558 return S_OK;
2561 WARN("Unknown interface %s\n", debugstr_guid(riid));
2562 return E_NOINTERFACE;
2565 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2567 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2568 return IAudioClient_AddRef(&This->IAudioClient_iface);
2571 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2573 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2574 return IAudioClient_Release(&This->IAudioClient_iface);
2577 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2578 IAudioStreamVolume *iface, UINT32 *out)
2580 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2582 TRACE("(%p)->(%p)\n", This, out);
2584 if(!out)
2585 return E_POINTER;
2587 *out = This->fmt->nChannels;
2589 return S_OK;
2592 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2593 IAudioStreamVolume *iface, UINT32 index, float level)
2595 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2596 HRESULT ret;
2598 TRACE("(%p)->(%d, %f)\n", This, index, level);
2600 if(level < 0.f || level > 1.f)
2601 return E_INVALIDARG;
2603 if(index >= This->fmt->nChannels)
2604 return E_INVALIDARG;
2606 EnterCriticalSection(&This->lock);
2608 This->vols[index] = level;
2610 ret = oss_setvol(This, index);
2612 LeaveCriticalSection(&This->lock);
2614 return ret;
2617 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2618 IAudioStreamVolume *iface, UINT32 index, float *level)
2620 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2622 TRACE("(%p)->(%d, %p)\n", This, index, level);
2624 if(!level)
2625 return E_POINTER;
2627 if(index >= This->fmt->nChannels)
2628 return E_INVALIDARG;
2630 *level = This->vols[index];
2632 return S_OK;
2635 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2636 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2638 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2639 int i;
2640 HRESULT ret;
2642 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2644 if(!levels)
2645 return E_POINTER;
2647 if(count != This->fmt->nChannels)
2648 return E_INVALIDARG;
2650 EnterCriticalSection(&This->lock);
2652 for(i = 0; i < count; ++i)
2653 This->vols[i] = levels[i];
2655 ret = oss_setvol(This, -1);
2657 LeaveCriticalSection(&This->lock);
2659 return ret;
2662 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2663 IAudioStreamVolume *iface, UINT32 count, float *levels)
2665 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2666 int i;
2668 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2670 if(!levels)
2671 return E_POINTER;
2673 if(count != This->fmt->nChannels)
2674 return E_INVALIDARG;
2676 EnterCriticalSection(&This->lock);
2678 for(i = 0; i < count; ++i)
2679 levels[i] = This->vols[i];
2681 LeaveCriticalSection(&This->lock);
2683 return S_OK;
2686 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2688 AudioStreamVolume_QueryInterface,
2689 AudioStreamVolume_AddRef,
2690 AudioStreamVolume_Release,
2691 AudioStreamVolume_GetChannelCount,
2692 AudioStreamVolume_SetChannelVolume,
2693 AudioStreamVolume_GetChannelVolume,
2694 AudioStreamVolume_SetAllVolumes,
2695 AudioStreamVolume_GetAllVolumes
2698 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2699 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2701 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2703 if(!ppv)
2704 return E_POINTER;
2705 *ppv = NULL;
2707 if(IsEqualIID(riid, &IID_IUnknown) ||
2708 IsEqualIID(riid, &IID_IChannelAudioVolume))
2709 *ppv = iface;
2710 if(*ppv){
2711 IUnknown_AddRef((IUnknown*)*ppv);
2712 return S_OK;
2715 WARN("Unknown interface %s\n", debugstr_guid(riid));
2716 return E_NOINTERFACE;
2719 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2721 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2722 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2725 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2727 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2728 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2731 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2732 IChannelAudioVolume *iface, UINT32 *out)
2734 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2735 AudioSession *session = This->session;
2737 TRACE("(%p)->(%p)\n", session, out);
2739 if(!out)
2740 return NULL_PTR_ERR;
2742 *out = session->channel_count;
2744 return S_OK;
2747 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2748 IChannelAudioVolume *iface, UINT32 index, float level,
2749 const GUID *context)
2751 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2752 AudioSession *session = This->session;
2753 HRESULT ret;
2755 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2756 wine_dbgstr_guid(context));
2758 if(level < 0.f || level > 1.f)
2759 return E_INVALIDARG;
2761 if(index >= session->channel_count)
2762 return E_INVALIDARG;
2764 if(context)
2765 FIXME("Notifications not supported yet\n");
2767 EnterCriticalSection(&session->lock);
2769 session->channel_vols[index] = level;
2771 ret = oss_session_setvol(session, index);
2773 LeaveCriticalSection(&session->lock);
2775 return ret;
2778 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2779 IChannelAudioVolume *iface, UINT32 index, float *level)
2781 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2782 AudioSession *session = This->session;
2784 TRACE("(%p)->(%d, %p)\n", session, index, level);
2786 if(!level)
2787 return NULL_PTR_ERR;
2789 if(index >= session->channel_count)
2790 return E_INVALIDARG;
2792 *level = session->channel_vols[index];
2794 return S_OK;
2797 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2798 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2799 const GUID *context)
2801 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2802 AudioSession *session = This->session;
2803 int i;
2804 HRESULT ret;
2806 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2807 wine_dbgstr_guid(context));
2809 if(!levels)
2810 return NULL_PTR_ERR;
2812 if(count != session->channel_count)
2813 return E_INVALIDARG;
2815 if(context)
2816 FIXME("Notifications not supported yet\n");
2818 EnterCriticalSection(&session->lock);
2820 for(i = 0; i < count; ++i)
2821 session->channel_vols[i] = levels[i];
2823 ret = oss_session_setvol(session, -1);
2825 LeaveCriticalSection(&session->lock);
2827 return ret;
2830 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2831 IChannelAudioVolume *iface, UINT32 count, float *levels)
2833 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2834 AudioSession *session = This->session;
2835 int i;
2837 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2839 if(!levels)
2840 return NULL_PTR_ERR;
2842 if(count != session->channel_count)
2843 return E_INVALIDARG;
2845 for(i = 0; i < count; ++i)
2846 levels[i] = session->channel_vols[i];
2848 return S_OK;
2851 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2853 ChannelAudioVolume_QueryInterface,
2854 ChannelAudioVolume_AddRef,
2855 ChannelAudioVolume_Release,
2856 ChannelAudioVolume_GetChannelCount,
2857 ChannelAudioVolume_SetChannelVolume,
2858 ChannelAudioVolume_GetChannelVolume,
2859 ChannelAudioVolume_SetAllVolumes,
2860 ChannelAudioVolume_GetAllVolumes
2863 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2864 REFIID riid, void **ppv)
2866 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2868 if(!ppv)
2869 return E_POINTER;
2870 *ppv = NULL;
2872 if(IsEqualIID(riid, &IID_IUnknown) ||
2873 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2874 IsEqualIID(riid, &IID_IAudioSessionManager2))
2875 *ppv = iface;
2876 if(*ppv){
2877 IUnknown_AddRef((IUnknown*)*ppv);
2878 return S_OK;
2881 WARN("Unknown interface %s\n", debugstr_guid(riid));
2882 return E_NOINTERFACE;
2885 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2887 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2888 ULONG ref;
2889 ref = InterlockedIncrement(&This->ref);
2890 TRACE("(%p) Refcount now %u\n", This, ref);
2891 return ref;
2894 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2896 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2897 ULONG ref;
2898 ref = InterlockedDecrement(&This->ref);
2899 TRACE("(%p) Refcount now %u\n", This, ref);
2900 if(!ref)
2901 HeapFree(GetProcessHeap(), 0, This);
2902 return ref;
2905 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2906 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2907 IAudioSessionControl **out)
2909 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2910 AudioSession *session;
2911 AudioSessionWrapper *wrapper;
2912 HRESULT hr;
2914 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2915 flags, out);
2917 hr = get_audio_session(session_guid, This->device, 0, &session);
2918 if(FAILED(hr))
2919 return hr;
2921 wrapper = AudioSessionWrapper_Create(NULL);
2922 if(!wrapper)
2923 return E_OUTOFMEMORY;
2925 wrapper->session = session;
2927 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2929 return S_OK;
2932 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2933 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2934 ISimpleAudioVolume **out)
2936 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2937 AudioSession *session;
2938 AudioSessionWrapper *wrapper;
2939 HRESULT hr;
2941 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2942 flags, out);
2944 hr = get_audio_session(session_guid, This->device, 0, &session);
2945 if(FAILED(hr))
2946 return hr;
2948 wrapper = AudioSessionWrapper_Create(NULL);
2949 if(!wrapper)
2950 return E_OUTOFMEMORY;
2952 wrapper->session = session;
2954 *out = &wrapper->ISimpleAudioVolume_iface;
2956 return S_OK;
2959 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2960 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2962 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2963 FIXME("(%p)->(%p) - stub\n", This, out);
2964 return E_NOTIMPL;
2967 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2968 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2970 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2971 FIXME("(%p)->(%p) - stub\n", This, notification);
2972 return E_NOTIMPL;
2975 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2976 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2978 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2979 FIXME("(%p)->(%p) - stub\n", This, notification);
2980 return E_NOTIMPL;
2983 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2984 IAudioSessionManager2 *iface, const WCHAR *session_id,
2985 IAudioVolumeDuckNotification *notification)
2987 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2988 FIXME("(%p)->(%p) - stub\n", This, notification);
2989 return E_NOTIMPL;
2992 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2993 IAudioSessionManager2 *iface,
2994 IAudioVolumeDuckNotification *notification)
2996 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2997 FIXME("(%p)->(%p) - stub\n", This, notification);
2998 return E_NOTIMPL;
3001 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3003 AudioSessionManager_QueryInterface,
3004 AudioSessionManager_AddRef,
3005 AudioSessionManager_Release,
3006 AudioSessionManager_GetAudioSessionControl,
3007 AudioSessionManager_GetSimpleAudioVolume,
3008 AudioSessionManager_GetSessionEnumerator,
3009 AudioSessionManager_RegisterSessionNotification,
3010 AudioSessionManager_UnregisterSessionNotification,
3011 AudioSessionManager_RegisterDuckNotification,
3012 AudioSessionManager_UnregisterDuckNotification
3015 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3016 IAudioSessionManager2 **out)
3018 SessionMgr *This;
3020 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3021 if(!This)
3022 return E_OUTOFMEMORY;
3024 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3025 This->device = device;
3026 This->ref = 1;
3028 *out = &This->IAudioSessionManager2_iface;
3030 return S_OK;