usp10: ScriptShape needs a number of SCRIPT_VISATTR equal to glyphs not chars.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobb7e3bbfec63fd894c3f1d58cab325ccd8e784841
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 /* EAGAIN is OSS buffer full, log that too */
1228 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1229 return;
1231 written_frames = written / This->fmt->nBlockAlign;
1233 This->lcl_offs_frames += written_frames;
1234 This->lcl_offs_frames %= This->bufsize_frames;
1235 This->held_frames -= written_frames;
1236 This->inbuf_frames += written_frames;
1238 if(written_frames < to_write){
1239 /* OSS buffer probably full */
1240 return;
1243 if(This->held_frames){
1244 /* wrapped and have some data back at the start to write */
1246 if(This->session->mute)
1247 oss_silence_buffer(This, This->local_buffer, This->held_frames);
1249 written = write(This->fd, This->local_buffer,
1250 This->held_frames * This->fmt->nBlockAlign);
1251 if(written < 0){
1252 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1253 return;
1255 written_frames = written / This->fmt->nBlockAlign;
1257 This->lcl_offs_frames += written_frames;
1258 This->lcl_offs_frames %= This->bufsize_frames;
1259 This->held_frames -= written_frames;
1260 This->inbuf_frames += written_frames;
1264 static void oss_read_data(ACImpl *This)
1266 UINT64 pos, readable;
1267 audio_buf_info bi;
1268 ssize_t nread;
1270 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1271 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1272 return;
1275 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1276 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1278 if(bi.bytes < readable)
1279 readable = bi.bytes;
1281 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1282 readable);
1283 if(nread < 0){
1284 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1285 return;
1288 This->held_frames += nread / This->fmt->nBlockAlign;
1290 if(This->held_frames > This->bufsize_frames){
1291 WARN("Overflow of unread data\n");
1292 This->lcl_offs_frames += This->held_frames;
1293 This->lcl_offs_frames %= This->bufsize_frames;
1294 This->held_frames = This->bufsize_frames;
1298 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1300 ACImpl *This = user;
1302 EnterCriticalSection(&This->lock);
1304 if(This->dataflow == eRender && This->held_frames)
1305 oss_write_data(This);
1306 else if(This->dataflow == eCapture)
1307 oss_read_data(This);
1309 if(This->event)
1310 SetEvent(This->event);
1312 LeaveCriticalSection(&This->lock);
1315 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1317 ACImpl *This = impl_from_IAudioClient(iface);
1318 int mask;
1320 TRACE("(%p)\n", This);
1322 EnterCriticalSection(&This->lock);
1324 if(!This->initted){
1325 LeaveCriticalSection(&This->lock);
1326 return AUDCLNT_E_NOT_INITIALIZED;
1329 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1330 LeaveCriticalSection(&This->lock);
1331 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1334 if(This->playing){
1335 LeaveCriticalSection(&This->lock);
1336 return AUDCLNT_E_NOT_STOPPED;
1339 if(This->dataflow == eRender)
1340 mask = PCM_ENABLE_OUTPUT;
1341 else if(This->dataflow == eCapture)
1342 mask = PCM_ENABLE_INPUT;
1343 else{
1344 LeaveCriticalSection(&This->lock);
1345 return E_UNEXPECTED;
1348 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1349 LeaveCriticalSection(&This->lock);
1350 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1351 return E_FAIL;
1354 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1355 oss_period_callback, This, 0, This->period_us / 1000,
1356 WT_EXECUTEINTIMERTHREAD))
1357 ERR("Unable to create period timer: %u\n", GetLastError());
1359 This->playing = TRUE;
1361 LeaveCriticalSection(&This->lock);
1363 return S_OK;
1366 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1368 ACImpl *This = impl_from_IAudioClient(iface);
1369 int mask;
1371 TRACE("(%p)\n", This);
1373 EnterCriticalSection(&This->lock);
1375 if(!This->initted){
1376 LeaveCriticalSection(&This->lock);
1377 return AUDCLNT_E_NOT_INITIALIZED;
1380 if(!This->playing){
1381 LeaveCriticalSection(&This->lock);
1382 return S_FALSE;
1385 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1386 DeleteTimerQueueTimer(g_timer_q, This->timer,
1387 INVALID_HANDLE_VALUE);
1388 This->timer = NULL;
1391 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1392 LeaveCriticalSection(&This->lock);
1393 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1394 return E_FAIL;
1397 mask = 0;
1398 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1399 LeaveCriticalSection(&This->lock);
1400 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1401 return E_FAIL;
1404 This->playing = FALSE;
1406 LeaveCriticalSection(&This->lock);
1408 return S_OK;
1411 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1413 ACImpl *This = impl_from_IAudioClient(iface);
1415 TRACE("(%p)\n", This);
1417 EnterCriticalSection(&This->lock);
1419 if(!This->initted){
1420 LeaveCriticalSection(&This->lock);
1421 return AUDCLNT_E_NOT_INITIALIZED;
1424 if(This->playing){
1425 LeaveCriticalSection(&This->lock);
1426 return AUDCLNT_E_NOT_STOPPED;
1429 if(This->buf_state != NOT_LOCKED){
1430 LeaveCriticalSection(&This->lock);
1431 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1434 This->written_frames = 0;
1435 This->inbuf_frames = 0;
1436 This->held_frames = 0;
1438 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1439 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1441 LeaveCriticalSection(&This->lock);
1443 return S_OK;
1446 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1447 HANDLE event)
1449 ACImpl *This = impl_from_IAudioClient(iface);
1451 TRACE("(%p)->(%p)\n", This, event);
1453 if(!event)
1454 return E_INVALIDARG;
1456 EnterCriticalSection(&This->lock);
1458 if(!This->initted){
1459 LeaveCriticalSection(&This->lock);
1460 return AUDCLNT_E_NOT_INITIALIZED;
1463 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1464 LeaveCriticalSection(&This->lock);
1465 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1468 This->event = event;
1470 LeaveCriticalSection(&This->lock);
1472 return S_OK;
1475 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1476 void **ppv)
1478 ACImpl *This = impl_from_IAudioClient(iface);
1480 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1482 if(!ppv)
1483 return E_POINTER;
1484 *ppv = NULL;
1486 EnterCriticalSection(&This->lock);
1488 if(!This->initted){
1489 LeaveCriticalSection(&This->lock);
1490 return AUDCLNT_E_NOT_INITIALIZED;
1493 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1494 if(This->dataflow != eRender){
1495 LeaveCriticalSection(&This->lock);
1496 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1498 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1499 *ppv = &This->IAudioRenderClient_iface;
1500 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1501 if(This->dataflow != eCapture){
1502 LeaveCriticalSection(&This->lock);
1503 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1505 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1506 *ppv = &This->IAudioCaptureClient_iface;
1507 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1508 IAudioClock_AddRef(&This->IAudioClock_iface);
1509 *ppv = &This->IAudioClock_iface;
1510 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1511 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1512 *ppv = &This->IAudioStreamVolume_iface;
1513 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1514 if(!This->session_wrapper){
1515 This->session_wrapper = AudioSessionWrapper_Create(This);
1516 if(!This->session_wrapper){
1517 LeaveCriticalSection(&This->lock);
1518 return E_OUTOFMEMORY;
1520 }else
1521 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1523 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1524 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1525 if(!This->session_wrapper){
1526 This->session_wrapper = AudioSessionWrapper_Create(This);
1527 if(!This->session_wrapper){
1528 LeaveCriticalSection(&This->lock);
1529 return E_OUTOFMEMORY;
1531 }else
1532 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1534 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1535 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1536 if(!This->session_wrapper){
1537 This->session_wrapper = AudioSessionWrapper_Create(This);
1538 if(!This->session_wrapper){
1539 LeaveCriticalSection(&This->lock);
1540 return E_OUTOFMEMORY;
1542 }else
1543 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1545 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1548 if(*ppv){
1549 LeaveCriticalSection(&This->lock);
1550 return S_OK;
1553 LeaveCriticalSection(&This->lock);
1555 FIXME("stub %s\n", debugstr_guid(riid));
1556 return E_NOINTERFACE;
1559 static const IAudioClientVtbl AudioClient_Vtbl =
1561 AudioClient_QueryInterface,
1562 AudioClient_AddRef,
1563 AudioClient_Release,
1564 AudioClient_Initialize,
1565 AudioClient_GetBufferSize,
1566 AudioClient_GetStreamLatency,
1567 AudioClient_GetCurrentPadding,
1568 AudioClient_IsFormatSupported,
1569 AudioClient_GetMixFormat,
1570 AudioClient_GetDevicePeriod,
1571 AudioClient_Start,
1572 AudioClient_Stop,
1573 AudioClient_Reset,
1574 AudioClient_SetEventHandle,
1575 AudioClient_GetService
1578 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1579 IAudioRenderClient *iface, REFIID riid, void **ppv)
1581 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1583 if(!ppv)
1584 return E_POINTER;
1585 *ppv = NULL;
1587 if(IsEqualIID(riid, &IID_IUnknown) ||
1588 IsEqualIID(riid, &IID_IAudioRenderClient))
1589 *ppv = iface;
1590 if(*ppv){
1591 IUnknown_AddRef((IUnknown*)*ppv);
1592 return S_OK;
1595 WARN("Unknown interface %s\n", debugstr_guid(riid));
1596 return E_NOINTERFACE;
1599 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1601 ACImpl *This = impl_from_IAudioRenderClient(iface);
1602 return AudioClient_AddRef(&This->IAudioClient_iface);
1605 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1607 ACImpl *This = impl_from_IAudioRenderClient(iface);
1608 return AudioClient_Release(&This->IAudioClient_iface);
1611 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1612 UINT32 frames, BYTE **data)
1614 ACImpl *This = impl_from_IAudioRenderClient(iface);
1615 UINT32 pad, write_pos;
1616 HRESULT hr;
1618 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1620 if(!data)
1621 return E_POINTER;
1623 EnterCriticalSection(&This->lock);
1625 if(This->buf_state != NOT_LOCKED){
1626 LeaveCriticalSection(&This->lock);
1627 return AUDCLNT_E_OUT_OF_ORDER;
1630 if(!frames){
1631 This->buf_state = LOCKED_NORMAL;
1632 LeaveCriticalSection(&This->lock);
1633 return S_OK;
1636 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1637 if(FAILED(hr)){
1638 LeaveCriticalSection(&This->lock);
1639 return hr;
1642 if(pad + frames > This->bufsize_frames){
1643 LeaveCriticalSection(&This->lock);
1644 return AUDCLNT_E_BUFFER_TOO_LARGE;
1647 write_pos =
1648 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1649 if(write_pos + frames > This->bufsize_frames){
1650 if(This->tmp_buffer_frames < frames){
1651 if(This->tmp_buffer)
1652 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1653 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1654 else
1655 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1656 frames * This->fmt->nBlockAlign);
1657 if(!This->tmp_buffer){
1658 LeaveCriticalSection(&This->lock);
1659 return E_OUTOFMEMORY;
1661 This->tmp_buffer_frames = frames;
1663 *data = This->tmp_buffer;
1664 This->buf_state = LOCKED_WRAPPED;
1665 }else{
1666 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1667 This->buf_state = LOCKED_NORMAL;
1670 LeaveCriticalSection(&This->lock);
1672 return S_OK;
1675 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1677 UINT32 write_offs_frames =
1678 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1679 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1680 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1681 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1682 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1684 if(written_bytes <= chunk_bytes){
1685 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1686 }else{
1687 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1688 memcpy(This->local_buffer, buffer + chunk_bytes,
1689 written_bytes - chunk_bytes);
1693 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1694 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1696 ACImpl *This = impl_from_IAudioRenderClient(iface);
1697 BYTE *buffer;
1699 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1701 EnterCriticalSection(&This->lock);
1703 if(This->buf_state == NOT_LOCKED || !written_frames){
1704 This->buf_state = NOT_LOCKED;
1705 LeaveCriticalSection(&This->lock);
1706 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1709 if(This->buf_state == LOCKED_NORMAL)
1710 buffer = This->local_buffer + This->fmt->nBlockAlign *
1711 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1712 else
1713 buffer = This->tmp_buffer;
1715 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1716 oss_silence_buffer(This, buffer, written_frames);
1718 if(This->held_frames){
1719 if(This->buf_state == LOCKED_WRAPPED)
1720 oss_wrap_buffer(This, buffer, written_frames);
1722 This->held_frames += written_frames;
1723 }else{
1724 ssize_t w_bytes;
1725 UINT32 w_frames;
1727 if(This->session->mute)
1728 oss_silence_buffer(This, buffer, written_frames);
1730 w_bytes = write(This->fd, buffer,
1731 written_frames * This->fmt->nBlockAlign);
1732 if(w_bytes < 0){
1733 if(errno != EAGAIN){
1734 This->buf_state = NOT_LOCKED;
1735 LeaveCriticalSection(&This->lock);
1736 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1737 return E_FAIL;
1738 }else /* OSS buffer full */
1739 w_bytes = 0;
1741 w_frames = w_bytes / This->fmt->nBlockAlign;
1742 This->inbuf_frames += w_frames;
1744 if(w_frames < written_frames){
1745 if(This->buf_state == LOCKED_WRAPPED)
1746 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1747 written_frames - w_frames);
1748 else
1749 This->lcl_offs_frames += w_frames;
1750 This->held_frames = written_frames - w_frames;
1754 This->written_frames += written_frames;
1755 This->buf_state = NOT_LOCKED;
1757 LeaveCriticalSection(&This->lock);
1759 return S_OK;
1762 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1763 AudioRenderClient_QueryInterface,
1764 AudioRenderClient_AddRef,
1765 AudioRenderClient_Release,
1766 AudioRenderClient_GetBuffer,
1767 AudioRenderClient_ReleaseBuffer
1770 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1771 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1773 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1775 if(!ppv)
1776 return E_POINTER;
1777 *ppv = NULL;
1779 if(IsEqualIID(riid, &IID_IUnknown) ||
1780 IsEqualIID(riid, &IID_IAudioCaptureClient))
1781 *ppv = iface;
1782 if(*ppv){
1783 IUnknown_AddRef((IUnknown*)*ppv);
1784 return S_OK;
1787 WARN("Unknown interface %s\n", debugstr_guid(riid));
1788 return E_NOINTERFACE;
1791 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1793 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1794 return IAudioClient_AddRef(&This->IAudioClient_iface);
1797 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1799 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1800 return IAudioClient_Release(&This->IAudioClient_iface);
1803 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1804 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1805 UINT64 *qpcpos)
1807 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1808 HRESULT hr;
1810 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1811 devpos, qpcpos);
1813 if(!data || !frames || !flags)
1814 return E_POINTER;
1816 EnterCriticalSection(&This->lock);
1818 if(This->buf_state != NOT_LOCKED){
1819 LeaveCriticalSection(&This->lock);
1820 return AUDCLNT_E_OUT_OF_ORDER;
1823 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1824 if(FAILED(hr)){
1825 LeaveCriticalSection(&This->lock);
1826 return hr;
1829 *flags = 0;
1831 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1832 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1833 if(This->tmp_buffer_frames < *frames){
1834 if(This->tmp_buffer)
1835 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1836 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1837 else
1838 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1839 *frames * This->fmt->nBlockAlign);
1840 if(!This->tmp_buffer){
1841 LeaveCriticalSection(&This->lock);
1842 return E_OUTOFMEMORY;
1844 This->tmp_buffer_frames = *frames;
1847 *data = This->tmp_buffer;
1848 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1849 This->fmt->nBlockAlign;
1850 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1851 frames_bytes = *frames * This->fmt->nBlockAlign;
1852 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1853 memcpy(This->tmp_buffer, This->local_buffer,
1854 frames_bytes - chunk_bytes);
1855 }else
1856 *data = This->local_buffer +
1857 This->lcl_offs_frames * This->fmt->nBlockAlign;
1859 This->buf_state = LOCKED_NORMAL;
1861 if(devpos || qpcpos)
1862 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1864 LeaveCriticalSection(&This->lock);
1866 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1869 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1870 IAudioCaptureClient *iface, UINT32 done)
1872 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1874 TRACE("(%p)->(%u)\n", This, done);
1876 EnterCriticalSection(&This->lock);
1878 if(This->buf_state == NOT_LOCKED){
1879 LeaveCriticalSection(&This->lock);
1880 return AUDCLNT_E_OUT_OF_ORDER;
1883 This->held_frames -= done;
1884 This->lcl_offs_frames += done;
1885 This->lcl_offs_frames %= This->bufsize_frames;
1887 This->buf_state = NOT_LOCKED;
1889 LeaveCriticalSection(&This->lock);
1891 return S_OK;
1894 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1895 IAudioCaptureClient *iface, UINT32 *frames)
1897 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1899 TRACE("(%p)->(%p)\n", This, frames);
1901 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1904 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1906 AudioCaptureClient_QueryInterface,
1907 AudioCaptureClient_AddRef,
1908 AudioCaptureClient_Release,
1909 AudioCaptureClient_GetBuffer,
1910 AudioCaptureClient_ReleaseBuffer,
1911 AudioCaptureClient_GetNextPacketSize
1914 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1915 REFIID riid, void **ppv)
1917 ACImpl *This = impl_from_IAudioClock(iface);
1919 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1921 if(!ppv)
1922 return E_POINTER;
1923 *ppv = NULL;
1925 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1926 *ppv = iface;
1927 else if(IsEqualIID(riid, &IID_IAudioClock2))
1928 *ppv = &This->IAudioClock2_iface;
1929 if(*ppv){
1930 IUnknown_AddRef((IUnknown*)*ppv);
1931 return S_OK;
1934 WARN("Unknown interface %s\n", debugstr_guid(riid));
1935 return E_NOINTERFACE;
1938 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1940 ACImpl *This = impl_from_IAudioClock(iface);
1941 return IAudioClient_AddRef(&This->IAudioClient_iface);
1944 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1946 ACImpl *This = impl_from_IAudioClock(iface);
1947 return IAudioClient_Release(&This->IAudioClient_iface);
1950 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1952 ACImpl *This = impl_from_IAudioClock(iface);
1954 TRACE("(%p)->(%p)\n", This, freq);
1956 *freq = This->fmt->nSamplesPerSec;
1958 return S_OK;
1961 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1962 UINT64 *qpctime)
1964 ACImpl *This = impl_from_IAudioClock(iface);
1965 UINT32 pad;
1966 HRESULT hr;
1968 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1970 if(!pos)
1971 return E_POINTER;
1973 EnterCriticalSection(&This->lock);
1975 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1976 if(FAILED(hr)){
1977 LeaveCriticalSection(&This->lock);
1978 return hr;
1981 if(This->dataflow == eRender)
1982 *pos = This->written_frames - pad;
1983 else if(This->dataflow == eCapture)
1984 *pos = This->written_frames + pad;
1986 LeaveCriticalSection(&This->lock);
1988 if(qpctime){
1989 LARGE_INTEGER stamp, freq;
1990 QueryPerformanceCounter(&stamp);
1991 QueryPerformanceFrequency(&freq);
1992 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1995 return S_OK;
1998 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1999 DWORD *chars)
2001 ACImpl *This = impl_from_IAudioClock(iface);
2003 TRACE("(%p)->(%p)\n", This, chars);
2005 if(!chars)
2006 return E_POINTER;
2008 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2010 return S_OK;
2013 static const IAudioClockVtbl AudioClock_Vtbl =
2015 AudioClock_QueryInterface,
2016 AudioClock_AddRef,
2017 AudioClock_Release,
2018 AudioClock_GetFrequency,
2019 AudioClock_GetPosition,
2020 AudioClock_GetCharacteristics
2023 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2024 REFIID riid, void **ppv)
2026 ACImpl *This = impl_from_IAudioClock2(iface);
2027 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2030 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2032 ACImpl *This = impl_from_IAudioClock2(iface);
2033 return IAudioClient_AddRef(&This->IAudioClient_iface);
2036 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2038 ACImpl *This = impl_from_IAudioClock2(iface);
2039 return IAudioClient_Release(&This->IAudioClient_iface);
2042 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2043 UINT64 *pos, UINT64 *qpctime)
2045 ACImpl *This = impl_from_IAudioClock2(iface);
2047 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2049 return E_NOTIMPL;
2052 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2054 AudioClock2_QueryInterface,
2055 AudioClock2_AddRef,
2056 AudioClock2_Release,
2057 AudioClock2_GetDevicePosition
2060 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2062 AudioSessionWrapper *ret;
2064 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2065 sizeof(AudioSessionWrapper));
2066 if(!ret)
2067 return NULL;
2069 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2070 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2071 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2073 ret->ref = 1;
2075 ret->client = client;
2076 if(client){
2077 ret->session = client->session;
2078 AudioClient_AddRef(&client->IAudioClient_iface);
2081 return ret;
2084 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2085 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2087 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2089 if(!ppv)
2090 return E_POINTER;
2091 *ppv = NULL;
2093 if(IsEqualIID(riid, &IID_IUnknown) ||
2094 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2095 IsEqualIID(riid, &IID_IAudioSessionControl2))
2096 *ppv = iface;
2097 if(*ppv){
2098 IUnknown_AddRef((IUnknown*)*ppv);
2099 return S_OK;
2102 WARN("Unknown interface %s\n", debugstr_guid(riid));
2103 return E_NOINTERFACE;
2106 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2108 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2109 ULONG ref;
2110 ref = InterlockedIncrement(&This->ref);
2111 TRACE("(%p) Refcount now %u\n", This, ref);
2112 return ref;
2115 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2117 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2118 ULONG ref;
2119 ref = InterlockedDecrement(&This->ref);
2120 TRACE("(%p) Refcount now %u\n", This, ref);
2121 if(!ref){
2122 if(This->client){
2123 EnterCriticalSection(&This->client->lock);
2124 This->client->session_wrapper = NULL;
2125 LeaveCriticalSection(&This->client->lock);
2126 AudioClient_Release(&This->client->IAudioClient_iface);
2128 HeapFree(GetProcessHeap(), 0, This);
2130 return ref;
2133 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2134 AudioSessionState *state)
2136 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2137 ACImpl *client;
2139 TRACE("(%p)->(%p)\n", This, state);
2141 if(!state)
2142 return NULL_PTR_ERR;
2144 EnterCriticalSection(&g_sessions_lock);
2146 if(list_empty(&This->session->clients)){
2147 *state = AudioSessionStateExpired;
2148 LeaveCriticalSection(&g_sessions_lock);
2149 return S_OK;
2152 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2153 EnterCriticalSection(&client->lock);
2154 if(client->playing){
2155 *state = AudioSessionStateActive;
2156 LeaveCriticalSection(&client->lock);
2157 LeaveCriticalSection(&g_sessions_lock);
2158 return S_OK;
2160 LeaveCriticalSection(&client->lock);
2163 LeaveCriticalSection(&g_sessions_lock);
2165 *state = AudioSessionStateInactive;
2167 return S_OK;
2170 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2171 IAudioSessionControl2 *iface, WCHAR **name)
2173 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2175 FIXME("(%p)->(%p) - stub\n", This, name);
2177 return E_NOTIMPL;
2180 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2181 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2183 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2185 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2187 return E_NOTIMPL;
2190 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2191 IAudioSessionControl2 *iface, WCHAR **path)
2193 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2195 FIXME("(%p)->(%p) - stub\n", This, path);
2197 return E_NOTIMPL;
2200 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2201 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2203 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2205 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2207 return E_NOTIMPL;
2210 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2211 IAudioSessionControl2 *iface, GUID *group)
2213 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2215 FIXME("(%p)->(%p) - stub\n", This, group);
2217 return E_NOTIMPL;
2220 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2221 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2223 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2225 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2226 debugstr_guid(session));
2228 return E_NOTIMPL;
2231 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2232 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2234 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2236 FIXME("(%p)->(%p) - stub\n", This, events);
2238 return S_OK;
2241 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2242 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2244 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2246 FIXME("(%p)->(%p) - stub\n", This, events);
2248 return S_OK;
2251 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2252 IAudioSessionControl2 *iface, WCHAR **id)
2254 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2256 FIXME("(%p)->(%p) - stub\n", This, id);
2258 return E_NOTIMPL;
2261 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2262 IAudioSessionControl2 *iface, WCHAR **id)
2264 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2266 FIXME("(%p)->(%p) - stub\n", This, id);
2268 return E_NOTIMPL;
2271 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2272 IAudioSessionControl2 *iface, DWORD *pid)
2274 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2276 TRACE("(%p)->(%p)\n", This, pid);
2278 if(!pid)
2279 return E_POINTER;
2281 *pid = GetCurrentProcessId();
2283 return S_OK;
2286 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2287 IAudioSessionControl2 *iface)
2289 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2291 TRACE("(%p)\n", This);
2293 return S_FALSE;
2296 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2297 IAudioSessionControl2 *iface, BOOL optout)
2299 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2301 TRACE("(%p)->(%d)\n", This, optout);
2303 return S_OK;
2306 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2308 AudioSessionControl_QueryInterface,
2309 AudioSessionControl_AddRef,
2310 AudioSessionControl_Release,
2311 AudioSessionControl_GetState,
2312 AudioSessionControl_GetDisplayName,
2313 AudioSessionControl_SetDisplayName,
2314 AudioSessionControl_GetIconPath,
2315 AudioSessionControl_SetIconPath,
2316 AudioSessionControl_GetGroupingParam,
2317 AudioSessionControl_SetGroupingParam,
2318 AudioSessionControl_RegisterAudioSessionNotification,
2319 AudioSessionControl_UnregisterAudioSessionNotification,
2320 AudioSessionControl_GetSessionIdentifier,
2321 AudioSessionControl_GetSessionInstanceIdentifier,
2322 AudioSessionControl_GetProcessId,
2323 AudioSessionControl_IsSystemSoundsSession,
2324 AudioSessionControl_SetDuckingPreference
2327 /* index == -1 means set all channels, otherwise sets only the given channel */
2328 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2330 int setreq, getreq;
2331 unsigned int vol;
2332 unsigned short l;
2333 float level;
2335 if(index == (UINT32)-1){
2336 HRESULT ret = S_OK;
2337 UINT32 i;
2338 for(i = 0; i < This->fmt->nChannels; ++i){
2339 HRESULT hr;
2340 hr = oss_setvol(This, i);
2341 if(FAILED(hr))
2342 ret = hr;
2344 return ret;
2347 if(index > 1)
2348 /* OSS doesn't support volume control past the first two channels */
2349 return S_OK;
2351 if(This->dataflow == eRender){
2352 setreq = SNDCTL_DSP_SETPLAYVOL;
2353 getreq = SNDCTL_DSP_GETPLAYVOL;
2354 }else if(This->dataflow == eCapture){
2355 setreq = SNDCTL_DSP_SETRECVOL;
2356 getreq = SNDCTL_DSP_GETRECVOL;
2357 }else
2358 return E_UNEXPECTED;
2360 if(ioctl(This->fd, getreq, &vol) < 0){
2361 if(errno == EINVAL)
2362 /* device doesn't support this call */
2363 return S_OK;
2365 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2366 return E_FAIL;
2369 level = This->session->master_vol * This->session->channel_vols[index] *
2370 This->vols[index];
2371 l = level * 100;
2372 if(index == 0)
2373 vol = l | (vol & 0xFF00);
2374 else
2375 vol = (vol & 0xFF) | (l << 8);
2377 if(ioctl(This->fd, setreq, &vol) < 0){
2378 if(errno == EINVAL)
2379 /* device doesn't support this call */
2380 return S_OK;
2382 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2383 return E_FAIL;
2386 return S_OK;
2389 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2391 HRESULT ret = S_OK;
2392 ACImpl *client;
2394 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2395 HRESULT hr;
2396 hr = oss_setvol(client, index);
2397 if(FAILED(hr))
2398 ret = hr;
2401 return ret;
2404 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2405 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2407 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2409 if(!ppv)
2410 return E_POINTER;
2411 *ppv = NULL;
2413 if(IsEqualIID(riid, &IID_IUnknown) ||
2414 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2415 *ppv = iface;
2416 if(*ppv){
2417 IUnknown_AddRef((IUnknown*)*ppv);
2418 return S_OK;
2421 WARN("Unknown interface %s\n", debugstr_guid(riid));
2422 return E_NOINTERFACE;
2425 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2427 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2428 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2431 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2433 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2434 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2437 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2438 ISimpleAudioVolume *iface, float level, const GUID *context)
2440 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2441 AudioSession *session = This->session;
2442 HRESULT ret;
2444 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2446 if(level < 0.f || level > 1.f)
2447 return E_INVALIDARG;
2449 if(context)
2450 FIXME("Notifications not supported yet\n");
2452 EnterCriticalSection(&session->lock);
2454 session->master_vol = level;
2456 ret = oss_session_setvol(session, -1);
2458 LeaveCriticalSection(&session->lock);
2460 return ret;
2463 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2464 ISimpleAudioVolume *iface, float *level)
2466 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2467 AudioSession *session = This->session;
2469 TRACE("(%p)->(%p)\n", session, level);
2471 if(!level)
2472 return NULL_PTR_ERR;
2474 *level = session->master_vol;
2476 return S_OK;
2479 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2480 BOOL mute, const GUID *context)
2482 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2483 AudioSession *session = This->session;
2485 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2487 EnterCriticalSection(&session->lock);
2489 if(!mute && session->mute){
2490 ACImpl *client;
2492 session->mute = mute;
2494 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2495 EnterCriticalSection(&client->lock);
2496 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2497 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2498 strerror(errno));
2499 oss_write_data(client);
2500 LeaveCriticalSection(&client->lock);
2502 }else
2503 session->mute = mute;
2505 LeaveCriticalSection(&session->lock);
2507 return S_OK;
2510 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2511 BOOL *mute)
2513 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2514 AudioSession *session = This->session;
2516 TRACE("(%p)->(%p)\n", session, mute);
2518 if(!mute)
2519 return NULL_PTR_ERR;
2521 *mute = This->session->mute;
2523 return S_OK;
2526 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2528 SimpleAudioVolume_QueryInterface,
2529 SimpleAudioVolume_AddRef,
2530 SimpleAudioVolume_Release,
2531 SimpleAudioVolume_SetMasterVolume,
2532 SimpleAudioVolume_GetMasterVolume,
2533 SimpleAudioVolume_SetMute,
2534 SimpleAudioVolume_GetMute
2537 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2538 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2540 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2542 if(!ppv)
2543 return E_POINTER;
2544 *ppv = NULL;
2546 if(IsEqualIID(riid, &IID_IUnknown) ||
2547 IsEqualIID(riid, &IID_IAudioStreamVolume))
2548 *ppv = iface;
2549 if(*ppv){
2550 IUnknown_AddRef((IUnknown*)*ppv);
2551 return S_OK;
2554 WARN("Unknown interface %s\n", debugstr_guid(riid));
2555 return E_NOINTERFACE;
2558 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2560 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2561 return IAudioClient_AddRef(&This->IAudioClient_iface);
2564 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2566 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2567 return IAudioClient_Release(&This->IAudioClient_iface);
2570 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2571 IAudioStreamVolume *iface, UINT32 *out)
2573 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2575 TRACE("(%p)->(%p)\n", This, out);
2577 if(!out)
2578 return E_POINTER;
2580 *out = This->fmt->nChannels;
2582 return S_OK;
2585 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2586 IAudioStreamVolume *iface, UINT32 index, float level)
2588 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2589 HRESULT ret;
2591 TRACE("(%p)->(%d, %f)\n", This, index, level);
2593 if(level < 0.f || level > 1.f)
2594 return E_INVALIDARG;
2596 if(index >= This->fmt->nChannels)
2597 return E_INVALIDARG;
2599 EnterCriticalSection(&This->lock);
2601 This->vols[index] = level;
2603 ret = oss_setvol(This, index);
2605 LeaveCriticalSection(&This->lock);
2607 return ret;
2610 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2611 IAudioStreamVolume *iface, UINT32 index, float *level)
2613 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2615 TRACE("(%p)->(%d, %p)\n", This, index, level);
2617 if(!level)
2618 return E_POINTER;
2620 if(index >= This->fmt->nChannels)
2621 return E_INVALIDARG;
2623 *level = This->vols[index];
2625 return S_OK;
2628 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2629 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2631 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2632 int i;
2633 HRESULT ret;
2635 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2637 if(!levels)
2638 return E_POINTER;
2640 if(count != This->fmt->nChannels)
2641 return E_INVALIDARG;
2643 EnterCriticalSection(&This->lock);
2645 for(i = 0; i < count; ++i)
2646 This->vols[i] = levels[i];
2648 ret = oss_setvol(This, -1);
2650 LeaveCriticalSection(&This->lock);
2652 return ret;
2655 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2656 IAudioStreamVolume *iface, UINT32 count, float *levels)
2658 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2659 int i;
2661 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2663 if(!levels)
2664 return E_POINTER;
2666 if(count != This->fmt->nChannels)
2667 return E_INVALIDARG;
2669 EnterCriticalSection(&This->lock);
2671 for(i = 0; i < count; ++i)
2672 levels[i] = This->vols[i];
2674 LeaveCriticalSection(&This->lock);
2676 return S_OK;
2679 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2681 AudioStreamVolume_QueryInterface,
2682 AudioStreamVolume_AddRef,
2683 AudioStreamVolume_Release,
2684 AudioStreamVolume_GetChannelCount,
2685 AudioStreamVolume_SetChannelVolume,
2686 AudioStreamVolume_GetChannelVolume,
2687 AudioStreamVolume_SetAllVolumes,
2688 AudioStreamVolume_GetAllVolumes
2691 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2692 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2694 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2696 if(!ppv)
2697 return E_POINTER;
2698 *ppv = NULL;
2700 if(IsEqualIID(riid, &IID_IUnknown) ||
2701 IsEqualIID(riid, &IID_IChannelAudioVolume))
2702 *ppv = iface;
2703 if(*ppv){
2704 IUnknown_AddRef((IUnknown*)*ppv);
2705 return S_OK;
2708 WARN("Unknown interface %s\n", debugstr_guid(riid));
2709 return E_NOINTERFACE;
2712 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2714 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2715 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2718 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2720 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2721 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2724 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2725 IChannelAudioVolume *iface, UINT32 *out)
2727 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2728 AudioSession *session = This->session;
2730 TRACE("(%p)->(%p)\n", session, out);
2732 if(!out)
2733 return NULL_PTR_ERR;
2735 *out = session->channel_count;
2737 return S_OK;
2740 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2741 IChannelAudioVolume *iface, UINT32 index, float level,
2742 const GUID *context)
2744 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2745 AudioSession *session = This->session;
2746 HRESULT ret;
2748 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2749 wine_dbgstr_guid(context));
2751 if(level < 0.f || level > 1.f)
2752 return E_INVALIDARG;
2754 if(index >= session->channel_count)
2755 return E_INVALIDARG;
2757 if(context)
2758 FIXME("Notifications not supported yet\n");
2760 EnterCriticalSection(&session->lock);
2762 session->channel_vols[index] = level;
2764 ret = oss_session_setvol(session, index);
2766 LeaveCriticalSection(&session->lock);
2768 return ret;
2771 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2772 IChannelAudioVolume *iface, UINT32 index, float *level)
2774 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2775 AudioSession *session = This->session;
2777 TRACE("(%p)->(%d, %p)\n", session, index, level);
2779 if(!level)
2780 return NULL_PTR_ERR;
2782 if(index >= session->channel_count)
2783 return E_INVALIDARG;
2785 *level = session->channel_vols[index];
2787 return S_OK;
2790 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2791 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2792 const GUID *context)
2794 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2795 AudioSession *session = This->session;
2796 int i;
2797 HRESULT ret;
2799 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2800 wine_dbgstr_guid(context));
2802 if(!levels)
2803 return NULL_PTR_ERR;
2805 if(count != session->channel_count)
2806 return E_INVALIDARG;
2808 if(context)
2809 FIXME("Notifications not supported yet\n");
2811 EnterCriticalSection(&session->lock);
2813 for(i = 0; i < count; ++i)
2814 session->channel_vols[i] = levels[i];
2816 ret = oss_session_setvol(session, -1);
2818 LeaveCriticalSection(&session->lock);
2820 return ret;
2823 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2824 IChannelAudioVolume *iface, UINT32 count, float *levels)
2826 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2827 AudioSession *session = This->session;
2828 int i;
2830 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2832 if(!levels)
2833 return NULL_PTR_ERR;
2835 if(count != session->channel_count)
2836 return E_INVALIDARG;
2838 for(i = 0; i < count; ++i)
2839 levels[i] = session->channel_vols[i];
2841 return S_OK;
2844 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2846 ChannelAudioVolume_QueryInterface,
2847 ChannelAudioVolume_AddRef,
2848 ChannelAudioVolume_Release,
2849 ChannelAudioVolume_GetChannelCount,
2850 ChannelAudioVolume_SetChannelVolume,
2851 ChannelAudioVolume_GetChannelVolume,
2852 ChannelAudioVolume_SetAllVolumes,
2853 ChannelAudioVolume_GetAllVolumes
2856 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2857 REFIID riid, void **ppv)
2859 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2861 if(!ppv)
2862 return E_POINTER;
2863 *ppv = NULL;
2865 if(IsEqualIID(riid, &IID_IUnknown) ||
2866 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2867 IsEqualIID(riid, &IID_IAudioSessionManager2))
2868 *ppv = iface;
2869 if(*ppv){
2870 IUnknown_AddRef((IUnknown*)*ppv);
2871 return S_OK;
2874 WARN("Unknown interface %s\n", debugstr_guid(riid));
2875 return E_NOINTERFACE;
2878 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2880 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2881 ULONG ref;
2882 ref = InterlockedIncrement(&This->ref);
2883 TRACE("(%p) Refcount now %u\n", This, ref);
2884 return ref;
2887 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2889 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2890 ULONG ref;
2891 ref = InterlockedDecrement(&This->ref);
2892 TRACE("(%p) Refcount now %u\n", This, ref);
2893 if(!ref)
2894 HeapFree(GetProcessHeap(), 0, This);
2895 return ref;
2898 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2899 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2900 IAudioSessionControl **out)
2902 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2903 AudioSession *session;
2904 AudioSessionWrapper *wrapper;
2905 HRESULT hr;
2907 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2908 flags, out);
2910 hr = get_audio_session(session_guid, This->device, 0, &session);
2911 if(FAILED(hr))
2912 return hr;
2914 wrapper = AudioSessionWrapper_Create(NULL);
2915 if(!wrapper)
2916 return E_OUTOFMEMORY;
2918 wrapper->session = session;
2920 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2922 return S_OK;
2925 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2926 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2927 ISimpleAudioVolume **out)
2929 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2930 AudioSession *session;
2931 AudioSessionWrapper *wrapper;
2932 HRESULT hr;
2934 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2935 flags, out);
2937 hr = get_audio_session(session_guid, This->device, 0, &session);
2938 if(FAILED(hr))
2939 return hr;
2941 wrapper = AudioSessionWrapper_Create(NULL);
2942 if(!wrapper)
2943 return E_OUTOFMEMORY;
2945 wrapper->session = session;
2947 *out = &wrapper->ISimpleAudioVolume_iface;
2949 return S_OK;
2952 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2953 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2955 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2956 FIXME("(%p)->(%p) - stub\n", This, out);
2957 return E_NOTIMPL;
2960 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2961 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2963 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2964 FIXME("(%p)->(%p) - stub\n", This, notification);
2965 return E_NOTIMPL;
2968 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2969 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2971 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2972 FIXME("(%p)->(%p) - stub\n", This, notification);
2973 return E_NOTIMPL;
2976 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2977 IAudioSessionManager2 *iface, const WCHAR *session_id,
2978 IAudioVolumeDuckNotification *notification)
2980 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2981 FIXME("(%p)->(%p) - stub\n", This, notification);
2982 return E_NOTIMPL;
2985 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2986 IAudioSessionManager2 *iface,
2987 IAudioVolumeDuckNotification *notification)
2989 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2990 FIXME("(%p)->(%p) - stub\n", This, notification);
2991 return E_NOTIMPL;
2994 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2996 AudioSessionManager_QueryInterface,
2997 AudioSessionManager_AddRef,
2998 AudioSessionManager_Release,
2999 AudioSessionManager_GetAudioSessionControl,
3000 AudioSessionManager_GetSimpleAudioVolume,
3001 AudioSessionManager_GetSessionEnumerator,
3002 AudioSessionManager_RegisterSessionNotification,
3003 AudioSessionManager_UnregisterSessionNotification,
3004 AudioSessionManager_RegisterDuckNotification,
3005 AudioSessionManager_UnregisterDuckNotification
3008 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3009 IAudioSessionManager2 **out)
3011 SessionMgr *This;
3013 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3014 if(!This)
3015 return E_OUTOFMEMORY;
3017 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3018 This->device = device;
3019 This->ref = 1;
3021 *out = &This->IAudioSessionManager2_iface;
3023 return S_OK;