wineoss.drv: Trim the sub-device part of the device path.
[wine/multimedia.git] / dlls / wineoss.drv / mmdevdrv.c
blob6f8e2d2364223e61b6bc17a7decb971846fa1115
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <sys/soundcard.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
45 #include "ole2.h"
46 #include "mmdeviceapi.h"
47 #include "devpkey.h"
48 #include "dshow.h"
49 #include "dsound.h"
51 #include "initguid.h"
52 #include "endpointvolume.h"
53 #include "audiopolicy.h"
54 #include "audioclient.h"
57 /* Some implementations of OSS, such as FreeBSD older than 9.0, lack
58 SNDCTL_DSP_HALT which is just a synonym for the older SNDCTL_DSP_RESET. */
59 #ifndef SNDCTL_DSP_HALT
60 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
61 #endif
63 WINE_DEFAULT_DEBUG_CHANNEL(oss);
65 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
67 static const REFERENCE_TIME DefaultPeriod = 200000;
68 static const REFERENCE_TIME MinimumPeriod = 100000;
70 struct ACImpl;
71 typedef struct ACImpl ACImpl;
73 typedef struct _AudioSession {
74 GUID guid;
75 struct list clients;
77 IMMDevice *device;
79 float master_vol;
80 UINT32 channel_count;
81 float *channel_vols;
82 BOOL mute;
84 CRITICAL_SECTION lock;
86 struct list entry;
87 } AudioSession;
89 typedef struct _AudioSessionWrapper {
90 IAudioSessionControl2 IAudioSessionControl2_iface;
91 IChannelAudioVolume IChannelAudioVolume_iface;
92 ISimpleAudioVolume ISimpleAudioVolume_iface;
94 LONG ref;
96 ACImpl *client;
97 AudioSession *session;
98 } AudioSessionWrapper;
100 struct ACImpl {
101 IAudioClient IAudioClient_iface;
102 IAudioRenderClient IAudioRenderClient_iface;
103 IAudioCaptureClient IAudioCaptureClient_iface;
104 IAudioClock IAudioClock_iface;
105 IAudioClock2 IAudioClock2_iface;
106 IAudioStreamVolume IAudioStreamVolume_iface;
108 LONG ref;
110 IMMDevice *parent;
112 WAVEFORMATEX *fmt;
114 EDataFlow dataflow;
115 DWORD flags;
116 AUDCLNT_SHAREMODE share;
117 HANDLE event;
118 float *vols;
120 int fd;
121 oss_audioinfo ai;
122 char devnode[OSS_DEVNODE_SIZE];
124 BOOL initted, playing;
125 UINT64 written_frames;
126 UINT32 period_us, bufsize_frames, held_frames, tmp_buffer_frames, inbuf_frames;
127 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
129 BYTE *local_buffer, *tmp_buffer;
130 int buf_state;
131 HANDLE timer;
133 CRITICAL_SECTION lock;
135 AudioSession *session;
136 AudioSessionWrapper *session_wrapper;
138 struct list entry;
141 enum BufferStates {
142 NOT_LOCKED = 0,
143 LOCKED_NORMAL, /* public buffer piece is from local_buffer */
144 LOCKED_WRAPPED /* public buffer piece is in tmp_buffer */
147 typedef struct _SessionMgr {
148 IAudioSessionManager2 IAudioSessionManager2_iface;
150 LONG ref;
152 IMMDevice *device;
153 } SessionMgr;
155 static HANDLE g_timer_q;
157 static CRITICAL_SECTION g_sessions_lock;
158 static struct list g_sessions = LIST_INIT(g_sessions);
160 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
161 static HRESULT oss_setvol(ACImpl *This, UINT32 index);
163 static const IAudioClientVtbl AudioClient_Vtbl;
164 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
165 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
166 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
167 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
168 static const IAudioClockVtbl AudioClock_Vtbl;
169 static const IAudioClock2Vtbl AudioClock2_Vtbl;
170 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
171 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
172 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
174 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
176 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
179 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
184 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
186 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
189 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
191 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
194 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
199 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
201 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
204 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
209 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
214 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
219 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
221 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
224 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
226 if(reason == DLL_PROCESS_ATTACH){
227 g_timer_q = CreateTimerQueue();
228 if(!g_timer_q)
229 return FALSE;
231 InitializeCriticalSection(&g_sessions_lock);
234 return TRUE;
237 /* From <dlls/mmdevapi/mmdevapi.h> */
238 enum DriverPriority {
239 Priority_Unavailable = 0,
240 Priority_Low,
241 Priority_Neutral,
242 Priority_Preferred
245 int WINAPI AUDDRV_GetPriority(void)
247 int mixer_fd;
248 oss_sysinfo sysinfo;
250 /* Attempt to determine if we are running on OSS or ALSA's OSS
251 * compatibility layer. There is no official way to do that, so just check
252 * for validity as best as possible, without rejecting valid OSS
253 * implementations. */
255 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
256 if(mixer_fd < 0){
257 TRACE("Priority_Unavailable: open failed\n");
258 return Priority_Unavailable;
261 sysinfo.version[0] = 0xFF;
262 sysinfo.versionnum = ~0;
263 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
264 TRACE("Priority_Unavailable: ioctl failed\n");
265 close(mixer_fd);
266 return Priority_Unavailable;
269 close(mixer_fd);
271 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
272 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
273 return Priority_Low;
275 if(sysinfo.versionnum & 0x80000000){
276 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
277 return Priority_Low;
280 TRACE("Priority_Preferred: Seems like valid OSS!\n");
282 return Priority_Preferred;
285 static const char *oss_clean_devnode(const char *devnode)
287 static char ret[OSS_DEVNODE_SIZE];
289 const char *dot, *slash;
290 size_t len;
292 dot = strrchr(devnode, '.');
293 if(!dot)
294 return devnode;
296 slash = strrchr(devnode, '/');
297 if(slash && dot < slash)
298 return devnode;
300 len = dot - devnode;
302 memcpy(ret, devnode, len);
303 ret[len] = '\0';
305 return ret;
308 static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
310 int fd = -1, err, i;
311 oss_audioinfo ai;
312 const char *devnode;
314 if(flow == eRender)
315 fd = open("/dev/dsp", O_WRONLY);
316 else
317 fd = open("/dev/dsp", O_RDONLY);
319 if(fd < 0){
320 WARN("Couldn't open default device!\n");
321 return 0;
324 ai.dev = -1;
325 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
326 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
327 close(fd);
328 return 0;
331 close(fd);
333 TRACE("Default devnode: %s\n", ai.devnode);
334 devnode = oss_clean_devnode(ai.devnode);
335 for(i = 0; i < num; ++i)
336 if(!strcmp(devnode, keys[i]))
337 return i;
339 WARN("Couldn't find default device! Choosing first.\n");
340 return 0;
343 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
344 UINT *num, UINT *def_index)
346 int i, j, mixer_fd;
347 oss_sysinfo sysinfo;
348 static int print_once = 0;
350 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
352 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
353 if(mixer_fd < 0){
354 ERR("OSS /dev/mixer doesn't seem to exist\n");
355 return AUDCLNT_E_SERVICE_NOT_RUNNING;
358 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
359 close(mixer_fd);
361 if(errno == EINVAL){
362 ERR("OSS version too old, need at least OSSv4\n");
363 return AUDCLNT_E_SERVICE_NOT_RUNNING;
366 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
367 return E_FAIL;
370 if(!print_once){
371 TRACE("OSS sysinfo:\n");
372 TRACE("product: %s\n", sysinfo.product);
373 TRACE("version: %s\n", sysinfo.version);
374 TRACE("versionnum: %x\n", sysinfo.versionnum);
375 TRACE("numaudios: %d\n", sysinfo.numaudios);
376 TRACE("nummixers: %d\n", sysinfo.nummixers);
377 TRACE("numcards: %d\n", sysinfo.numcards);
378 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
379 print_once = 1;
382 if(sysinfo.numaudios <= 0){
383 WARN("No audio devices!\n");
384 close(mixer_fd);
385 return AUDCLNT_E_SERVICE_NOT_RUNNING;
388 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
389 *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
391 *num = 0;
392 for(i = 0; i < sysinfo.numaudios; ++i){
393 oss_audioinfo ai = {0};
394 const char *devnode;
395 int fd;
397 ai.dev = i;
398 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
399 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
400 strerror(errno));
401 continue;
404 devnode = oss_clean_devnode(ai.devnode);
406 /* check for duplicates */
407 for(j = 0; j < *num; ++j)
408 if(!strcmp(devnode, (*keys)[j]))
409 break;
410 if(j != *num)
411 continue;
413 if(flow == eRender)
414 fd = open(devnode, O_WRONLY, 0);
415 else
416 fd = open(devnode, O_RDONLY, 0);
417 if(fd < 0){
418 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
419 devnode, errno, strerror(errno));
420 continue;
422 close(fd);
424 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
425 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
426 size_t len;
428 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
429 strlen(devnode) + 1);
430 if(!(*keys)[*num]){
431 for(i = 0; i < *num; ++i){
432 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
433 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
435 HeapFree(GetProcessHeap(), 0, *ids);
436 HeapFree(GetProcessHeap(), 0, *keys);
437 close(mixer_fd);
438 return E_OUTOFMEMORY;
440 strcpy((*keys)[*num], devnode);
442 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
443 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
444 len * sizeof(WCHAR));
445 if(!(*ids)[*num]){
446 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
447 for(i = 0; i < *num; ++i){
448 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
449 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
451 HeapFree(GetProcessHeap(), 0, *ids);
452 HeapFree(GetProcessHeap(), 0, *keys);
453 close(mixer_fd);
454 return E_OUTOFMEMORY;
456 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
457 (*ids)[*num], len);
459 (*num)++;
463 close(mixer_fd);
465 *def_index = get_default_index(flow, *keys, *num);
467 return S_OK;
470 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
471 EDataFlow dataflow, IAudioClient **out)
473 ACImpl *This;
475 TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
477 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
478 if(!This)
479 return E_OUTOFMEMORY;
481 if(dataflow == eRender)
482 This->fd = open(devnode, O_WRONLY, 0);
483 else if(dataflow == eCapture)
484 This->fd = open(devnode, O_RDONLY, 0);
485 else{
486 HeapFree(GetProcessHeap(), 0, This);
487 return E_INVALIDARG;
489 if(This->fd < 0){
490 ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
491 strerror(errno));
492 HeapFree(GetProcessHeap(), 0, This);
493 return AUDCLNT_E_DEVICE_INVALIDATED;
496 This->dataflow = dataflow;
498 This->ai.dev = -1;
499 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
500 ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
501 errno, strerror(errno));
502 close(This->fd);
503 HeapFree(GetProcessHeap(), 0, This);
504 return E_FAIL;
507 strcpy(This->devnode, devnode);
509 TRACE("OSS audioinfo:\n");
510 TRACE("devnode: %s\n", This->ai.devnode);
511 TRACE("name: %s\n", This->ai.name);
512 TRACE("busy: %x\n", This->ai.busy);
513 TRACE("caps: %x\n", This->ai.caps);
514 TRACE("iformats: %x\n", This->ai.iformats);
515 TRACE("oformats: %x\n", This->ai.oformats);
516 TRACE("enabled: %d\n", This->ai.enabled);
517 TRACE("min_rate: %d\n", This->ai.min_rate);
518 TRACE("max_rate: %d\n", This->ai.max_rate);
519 TRACE("min_channels: %d\n", This->ai.min_channels);
520 TRACE("max_channels: %d\n", This->ai.max_channels);
522 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
523 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
524 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
525 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
526 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
527 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
529 InitializeCriticalSection(&This->lock);
531 This->parent = dev;
532 IMMDevice_AddRef(This->parent);
534 IAudioClient_AddRef(&This->IAudioClient_iface);
536 *out = &This->IAudioClient_iface;
538 return S_OK;
541 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
542 REFIID riid, void **ppv)
544 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
546 if(!ppv)
547 return E_POINTER;
548 *ppv = NULL;
549 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
550 *ppv = iface;
551 if(*ppv){
552 IUnknown_AddRef((IUnknown*)*ppv);
553 return S_OK;
555 WARN("Unknown interface %s\n", debugstr_guid(riid));
556 return E_NOINTERFACE;
559 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
561 ACImpl *This = impl_from_IAudioClient(iface);
562 ULONG ref;
563 ref = InterlockedIncrement(&This->ref);
564 TRACE("(%p) Refcount now %u\n", This, ref);
565 return ref;
568 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
570 ACImpl *This = impl_from_IAudioClient(iface);
571 ULONG ref;
572 ref = InterlockedDecrement(&This->ref);
573 TRACE("(%p) Refcount now %u\n", This, ref);
574 if(!ref){
575 IAudioClient_Stop(iface);
576 IMMDevice_Release(This->parent);
577 DeleteCriticalSection(&This->lock);
578 close(This->fd);
579 if(This->initted){
580 EnterCriticalSection(&g_sessions_lock);
581 list_remove(&This->entry);
582 LeaveCriticalSection(&g_sessions_lock);
584 HeapFree(GetProcessHeap(), 0, This->vols);
585 HeapFree(GetProcessHeap(), 0, This->local_buffer);
586 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
587 CoTaskMemFree(This->fmt);
588 HeapFree(GetProcessHeap(), 0, This);
590 return ref;
593 static void dump_fmt(const WAVEFORMATEX *fmt)
595 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
596 switch(fmt->wFormatTag){
597 case WAVE_FORMAT_PCM:
598 TRACE("WAVE_FORMAT_PCM");
599 break;
600 case WAVE_FORMAT_IEEE_FLOAT:
601 TRACE("WAVE_FORMAT_IEEE_FLOAT");
602 break;
603 case WAVE_FORMAT_EXTENSIBLE:
604 TRACE("WAVE_FORMAT_EXTENSIBLE");
605 break;
606 default:
607 TRACE("Unknown");
608 break;
610 TRACE(")\n");
612 TRACE("nChannels: %u\n", fmt->nChannels);
613 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
614 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
615 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
616 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
617 TRACE("cbSize: %u\n", fmt->cbSize);
619 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
620 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
621 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
622 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
623 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
627 static DWORD get_channel_mask(unsigned int channels)
629 switch(channels){
630 case 0:
631 return 0;
632 case 1:
633 return KSAUDIO_SPEAKER_MONO;
634 case 2:
635 return KSAUDIO_SPEAKER_STEREO;
636 case 3:
637 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
638 case 4:
639 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
640 case 5:
641 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
642 case 6:
643 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
644 case 7:
645 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
646 case 8:
647 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
649 FIXME("Unknown speaker configuration: %u\n", channels);
650 return 0;
653 static int get_oss_format(const WAVEFORMATEX *fmt)
655 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
657 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
658 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
659 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
660 switch(fmt->wBitsPerSample){
661 case 8:
662 return AFMT_U8;
663 case 16:
664 return AFMT_S16_LE;
665 case 24:
666 return AFMT_S24_LE;
667 case 32:
668 return AFMT_S32_LE;
670 return -1;
673 #ifdef AFMT_FLOAT
674 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
675 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
676 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
677 if(fmt->wBitsPerSample != 32)
678 return -1;
680 return AFMT_FLOAT;
682 #endif
684 return -1;
687 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
689 WAVEFORMATEX *ret;
690 size_t size;
692 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
693 size = sizeof(WAVEFORMATEXTENSIBLE);
694 else
695 size = sizeof(WAVEFORMATEX);
697 ret = CoTaskMemAlloc(size);
698 if(!ret)
699 return NULL;
701 memcpy(ret, fmt, size);
703 ret->cbSize = size - sizeof(WAVEFORMATEX);
705 return ret;
708 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
709 WAVEFORMATEX **out, BOOL query)
711 int tmp, oss_format;
712 double tenth;
713 HRESULT ret = S_OK;
714 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
715 WAVEFORMATEX *closest = NULL;
717 if(out)
718 *out = NULL;
720 tmp = oss_format = get_oss_format(fmt);
721 if(oss_format < 0)
722 return AUDCLNT_E_UNSUPPORTED_FORMAT;
723 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
724 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
725 return E_FAIL;
727 if(tmp != oss_format){
728 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
729 return AUDCLNT_E_UNSUPPORTED_FORMAT;
732 closest = clone_format(fmt);
733 if(!closest)
734 return E_OUTOFMEMORY;
736 tmp = fmt->nSamplesPerSec;
737 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
738 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
739 CoTaskMemFree(closest);
740 return E_FAIL;
742 tenth = fmt->nSamplesPerSec * 0.1;
743 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
744 ret = S_FALSE;
745 closest->nSamplesPerSec = tmp;
748 tmp = fmt->nChannels;
749 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
750 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
751 CoTaskMemFree(closest);
752 return E_FAIL;
754 if(tmp != fmt->nChannels){
755 ret = S_FALSE;
756 closest->nChannels = tmp;
759 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
760 DWORD mask = get_channel_mask(closest->nChannels);
762 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
764 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
765 fmtex->dwChannelMask != 0 &&
766 fmtex->dwChannelMask != mask)
767 ret = S_FALSE;
770 if(ret == S_FALSE && out){
771 closest->nBlockAlign =
772 closest->nChannels * closest->wBitsPerSample / 8;
773 closest->nAvgBytesPerSec =
774 closest->nBlockAlign * closest->nSamplesPerSec;
775 *out = closest;
776 } else
777 CoTaskMemFree(closest);
779 TRACE("returning: %08x\n", ret);
780 return ret;
783 static void session_init_vols(AudioSession *session, UINT channels)
785 if(session->channel_count < channels){
786 UINT i;
788 if(session->channel_vols)
789 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
790 session->channel_vols, sizeof(float) * channels);
791 else
792 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
793 sizeof(float) * channels);
794 if(!session->channel_vols)
795 return;
797 for(i = session->channel_count; i < channels; ++i)
798 session->channel_vols[i] = 1.f;
800 session->channel_count = channels;
804 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
805 UINT num_channels)
807 AudioSession *ret;
809 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
810 if(!ret)
811 return NULL;
813 memcpy(&ret->guid, guid, sizeof(GUID));
815 ret->device = device;
817 list_init(&ret->clients);
819 list_add_head(&g_sessions, &ret->entry);
821 InitializeCriticalSection(&ret->lock);
823 session_init_vols(ret, num_channels);
825 ret->master_vol = 1.f;
827 return ret;
830 /* if channels == 0, then this will return or create a session with
831 * matching dataflow and GUID. otherwise, channels must also match */
832 static HRESULT get_audio_session(const GUID *sessionguid,
833 IMMDevice *device, UINT channels, AudioSession **out)
835 AudioSession *session;
837 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
838 *out = create_session(&GUID_NULL, device, channels);
839 if(!*out)
840 return E_OUTOFMEMORY;
842 return S_OK;
845 *out = NULL;
846 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
847 if(session->device == device &&
848 IsEqualGUID(sessionguid, &session->guid)){
849 session_init_vols(session, channels);
850 *out = session;
851 break;
855 if(!*out){
856 *out = create_session(sessionguid, device, channels);
857 if(!*out)
858 return E_OUTOFMEMORY;
861 return S_OK;
864 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
865 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
866 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
867 const GUID *sessionguid)
869 ACImpl *This = impl_from_IAudioClient(iface);
870 int mask, i;
871 HRESULT hr;
873 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
874 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
876 if(!fmt)
877 return E_POINTER;
879 dump_fmt(fmt);
881 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
882 return AUDCLNT_E_NOT_INITIALIZED;
884 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
885 AUDCLNT_STREAMFLAGS_LOOPBACK |
886 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
887 AUDCLNT_STREAMFLAGS_NOPERSIST |
888 AUDCLNT_STREAMFLAGS_RATEADJUST |
889 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
890 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
891 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
892 TRACE("Unknown flags: %08x\n", flags);
893 return E_INVALIDARG;
896 EnterCriticalSection(&This->lock);
898 if(This->initted){
899 LeaveCriticalSection(&This->lock);
900 return AUDCLNT_E_ALREADY_INITIALIZED;
903 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
904 if(hr == S_FALSE){
905 LeaveCriticalSection(&This->lock);
906 return AUDCLNT_E_UNSUPPORTED_FORMAT;
908 if(FAILED(hr)){
909 LeaveCriticalSection(&This->lock);
910 return hr;
913 mask = 0;
914 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
915 LeaveCriticalSection(&This->lock);
916 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
917 return E_FAIL;
920 mask = (100 << 8) | 100;
921 if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
922 WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
924 This->fmt = clone_format(fmt);
925 if(!This->fmt){
926 LeaveCriticalSection(&This->lock);
927 return E_OUTOFMEMORY;
930 if(period)
931 This->period_us = period / 10;
932 else
933 This->period_us = DefaultPeriod / 10;
935 if(!duration)
936 duration = 300000; /* 0.03s */
937 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
938 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
939 This->bufsize_frames * fmt->nBlockAlign);
940 if(!This->local_buffer){
941 CoTaskMemFree(This->fmt);
942 This->fmt = NULL;
943 LeaveCriticalSection(&This->lock);
944 return E_OUTOFMEMORY;
947 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
948 if(!This->vols){
949 CoTaskMemFree(This->fmt);
950 This->fmt = NULL;
951 LeaveCriticalSection(&This->lock);
952 return E_OUTOFMEMORY;
955 for(i = 0; i < fmt->nChannels; ++i)
956 This->vols[i] = 1.f;
958 This->share = mode;
959 This->flags = flags;
961 EnterCriticalSection(&g_sessions_lock);
963 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
964 &This->session);
965 if(FAILED(hr)){
966 LeaveCriticalSection(&g_sessions_lock);
967 HeapFree(GetProcessHeap(), 0, This->vols);
968 This->vols = NULL;
969 CoTaskMemFree(This->fmt);
970 This->fmt = NULL;
971 LeaveCriticalSection(&This->lock);
972 return hr;
975 list_add_tail(&This->session->clients, &This->entry);
977 LeaveCriticalSection(&g_sessions_lock);
979 This->initted = TRUE;
981 oss_setvol(This, -1);
983 LeaveCriticalSection(&This->lock);
985 return S_OK;
988 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
989 UINT32 *frames)
991 ACImpl *This = impl_from_IAudioClient(iface);
993 TRACE("(%p)->(%p)\n", This, frames);
995 if(!frames)
996 return E_POINTER;
998 EnterCriticalSection(&This->lock);
1000 if(!This->initted){
1001 LeaveCriticalSection(&This->lock);
1002 return AUDCLNT_E_NOT_INITIALIZED;
1005 *frames = This->bufsize_frames;
1007 LeaveCriticalSection(&This->lock);
1009 return S_OK;
1012 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1013 REFERENCE_TIME *latency)
1015 ACImpl *This = impl_from_IAudioClient(iface);
1017 TRACE("(%p)->(%p)\n", This, latency);
1019 if(!latency)
1020 return E_POINTER;
1022 EnterCriticalSection(&This->lock);
1024 if(!This->initted){
1025 LeaveCriticalSection(&This->lock);
1026 return AUDCLNT_E_NOT_INITIALIZED;
1029 if(This->dataflow == eRender){
1030 int delay_bytes;
1031 double delay_s;
1033 if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
1034 LeaveCriticalSection(&This->lock);
1035 WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
1036 return E_FAIL;
1039 delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
1040 This->fmt->nBlockAlign);
1042 *latency = delay_s * 10000000;
1043 }else
1044 *latency = 10000; /* OSS doesn't provide input latency */
1046 LeaveCriticalSection(&This->lock);
1048 return S_OK;
1051 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1052 UINT32 *numpad)
1054 ACImpl *This = impl_from_IAudioClient(iface);
1055 audio_buf_info bi;
1057 TRACE("(%p)->(%p)\n", This, numpad);
1059 if(!numpad)
1060 return E_POINTER;
1062 EnterCriticalSection(&This->lock);
1064 if(!This->initted){
1065 LeaveCriticalSection(&This->lock);
1066 return AUDCLNT_E_NOT_INITIALIZED;
1069 if(This->dataflow == eRender){
1070 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1071 LeaveCriticalSection(&This->lock);
1072 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1073 return E_FAIL;
1076 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1077 This->fmt->nBlockAlign;
1079 /* when the OSS buffer has less than one fragment of data, including
1080 * no data, it often reports it as some non-zero portion of a
1081 * fragment. when it has more than one fragment of data, it reports
1082 * it as some multiple of that portion of the fragment size.
1084 * so, we have to do some ugly workarounds to report the timing
1085 * as accurately as possible */
1086 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1087 *numpad = This->inbuf_frames;
1088 This->inbuf_frames = 0;
1089 }else{
1090 if(*numpad < This->inbuf_frames)
1091 This->inbuf_frames = *numpad;
1092 else
1093 *numpad = This->inbuf_frames;
1095 }else if(This->dataflow == eCapture){
1096 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1097 LeaveCriticalSection(&This->lock);
1098 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1099 return E_FAIL;
1102 if(bi.bytes <= bi.fragsize)
1103 *numpad = 0;
1104 else
1105 *numpad = bi.bytes / This->fmt->nBlockAlign;
1106 }else{
1107 LeaveCriticalSection(&This->lock);
1108 return E_UNEXPECTED;
1111 *numpad += This->held_frames;
1113 LeaveCriticalSection(&This->lock);
1115 return S_OK;
1118 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1119 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1120 WAVEFORMATEX **outpwfx)
1122 ACImpl *This = impl_from_IAudioClient(iface);
1123 int fd = -1;
1124 HRESULT ret;
1126 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1128 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1129 return E_POINTER;
1131 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1132 return E_INVALIDARG;
1134 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1135 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1136 return E_INVALIDARG;
1138 dump_fmt(pwfx);
1140 if(This->dataflow == eRender)
1141 fd = open(This->devnode, O_WRONLY, 0);
1142 else if(This->dataflow == eCapture)
1143 fd = open(This->devnode, O_RDONLY, 0);
1145 if(fd < 0){
1146 ERR("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1147 strerror(errno));
1148 return AUDCLNT_E_DEVICE_INVALIDATED;
1151 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1153 close(fd);
1155 return ret;
1158 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1159 WAVEFORMATEX **pwfx)
1161 ACImpl *This = impl_from_IAudioClient(iface);
1162 WAVEFORMATEXTENSIBLE *fmt;
1163 int formats;
1165 TRACE("(%p)->(%p)\n", This, pwfx);
1167 if(!pwfx)
1168 return E_POINTER;
1169 *pwfx = NULL;
1171 if(This->dataflow == eRender)
1172 formats = This->ai.oformats;
1173 else if(This->dataflow == eCapture)
1174 formats = This->ai.iformats;
1175 else
1176 return E_UNEXPECTED;
1178 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1179 if(!fmt)
1180 return E_OUTOFMEMORY;
1182 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1183 if(formats & AFMT_S16_LE){
1184 fmt->Format.wBitsPerSample = 16;
1185 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1186 #ifdef AFMT_FLOAT
1187 }else if(formats & AFMT_FLOAT){
1188 fmt->Format.wBitsPerSample = 32;
1189 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1190 #endif
1191 }else if(formats & AFMT_U8){
1192 fmt->Format.wBitsPerSample = 8;
1193 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1194 }else if(formats & AFMT_S32_LE){
1195 fmt->Format.wBitsPerSample = 32;
1196 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1197 }else if(formats & AFMT_S24_LE){
1198 fmt->Format.wBitsPerSample = 24;
1199 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1200 }else{
1201 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1202 CoTaskMemFree(fmt);
1203 return E_FAIL;
1206 fmt->Format.nChannels = This->ai.max_channels;
1207 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1208 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1210 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1211 fmt->Format.nChannels) / 8;
1212 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1213 fmt->Format.nBlockAlign;
1215 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1216 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1218 *pwfx = (WAVEFORMATEX*)fmt;
1219 dump_fmt(*pwfx);
1221 return S_OK;
1224 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1225 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1227 ACImpl *This = impl_from_IAudioClient(iface);
1229 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1231 if(!defperiod && !minperiod)
1232 return E_POINTER;
1234 EnterCriticalSection(&This->lock);
1236 if(defperiod)
1237 *defperiod = DefaultPeriod;
1238 if(minperiod)
1239 *minperiod = MinimumPeriod;
1241 LeaveCriticalSection(&This->lock);
1243 return S_OK;
1246 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1248 if(This->fmt->wBitsPerSample == 8)
1249 memset(buf, 128, frames * This->fmt->nBlockAlign);
1250 else
1251 memset(buf, 0, frames * This->fmt->nBlockAlign);
1254 static void oss_write_data(ACImpl *This)
1256 ssize_t written;
1257 UINT32 written_frames;
1258 size_t to_write;
1259 BYTE *buf =
1260 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1262 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1263 to_write = This->bufsize_frames - This->lcl_offs_frames;
1264 else
1265 to_write = This->held_frames;
1267 if(This->session->mute)
1268 oss_silence_buffer(This, buf, to_write);
1270 written = write(This->fd, buf, to_write * This->fmt->nBlockAlign);
1271 if(written < 0){
1272 /* EAGAIN is OSS buffer full, log that too */
1273 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1274 return;
1276 written_frames = written / This->fmt->nBlockAlign;
1278 This->lcl_offs_frames += written_frames;
1279 This->lcl_offs_frames %= This->bufsize_frames;
1280 This->held_frames -= written_frames;
1281 This->inbuf_frames += written_frames;
1283 if(written_frames < to_write){
1284 /* OSS buffer probably full */
1285 return;
1288 if(This->held_frames){
1289 /* wrapped and have some data back at the start to write */
1291 if(This->session->mute)
1292 oss_silence_buffer(This, This->local_buffer, This->held_frames);
1294 written = write(This->fd, This->local_buffer,
1295 This->held_frames * This->fmt->nBlockAlign);
1296 if(written < 0){
1297 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1298 return;
1300 written_frames = written / This->fmt->nBlockAlign;
1302 This->lcl_offs_frames += written_frames;
1303 This->lcl_offs_frames %= This->bufsize_frames;
1304 This->held_frames -= written_frames;
1305 This->inbuf_frames += written_frames;
1309 static void oss_read_data(ACImpl *This)
1311 UINT64 pos, readable;
1312 audio_buf_info bi;
1313 ssize_t nread;
1315 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1316 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1317 return;
1320 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1321 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1323 if(bi.bytes < readable)
1324 readable = bi.bytes;
1326 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1327 readable);
1328 if(nread < 0){
1329 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1330 return;
1333 This->held_frames += nread / This->fmt->nBlockAlign;
1335 if(This->held_frames > This->bufsize_frames){
1336 WARN("Overflow of unread data\n");
1337 This->lcl_offs_frames += This->held_frames;
1338 This->lcl_offs_frames %= This->bufsize_frames;
1339 This->held_frames = This->bufsize_frames;
1343 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1345 ACImpl *This = user;
1347 EnterCriticalSection(&This->lock);
1349 if(This->dataflow == eRender && This->held_frames)
1350 oss_write_data(This);
1351 else if(This->dataflow == eCapture)
1352 oss_read_data(This);
1354 if(This->event)
1355 SetEvent(This->event);
1357 LeaveCriticalSection(&This->lock);
1360 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1362 ACImpl *This = impl_from_IAudioClient(iface);
1363 int mask;
1365 TRACE("(%p)\n", This);
1367 EnterCriticalSection(&This->lock);
1369 if(!This->initted){
1370 LeaveCriticalSection(&This->lock);
1371 return AUDCLNT_E_NOT_INITIALIZED;
1374 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1375 LeaveCriticalSection(&This->lock);
1376 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1379 if(This->playing){
1380 LeaveCriticalSection(&This->lock);
1381 return AUDCLNT_E_NOT_STOPPED;
1384 if(This->dataflow == eRender)
1385 mask = PCM_ENABLE_OUTPUT;
1386 else if(This->dataflow == eCapture)
1387 mask = PCM_ENABLE_INPUT;
1388 else{
1389 LeaveCriticalSection(&This->lock);
1390 return E_UNEXPECTED;
1393 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1394 LeaveCriticalSection(&This->lock);
1395 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1396 return E_FAIL;
1399 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1400 oss_period_callback, This, 0, This->period_us / 1000,
1401 WT_EXECUTEINTIMERTHREAD))
1402 ERR("Unable to create period timer: %u\n", GetLastError());
1404 This->playing = TRUE;
1406 LeaveCriticalSection(&This->lock);
1408 return S_OK;
1411 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1413 ACImpl *This = impl_from_IAudioClient(iface);
1414 int mask;
1416 TRACE("(%p)\n", This);
1418 EnterCriticalSection(&This->lock);
1420 if(!This->initted){
1421 LeaveCriticalSection(&This->lock);
1422 return AUDCLNT_E_NOT_INITIALIZED;
1425 if(!This->playing){
1426 LeaveCriticalSection(&This->lock);
1427 return S_FALSE;
1430 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1431 DeleteTimerQueueTimer(g_timer_q, This->timer,
1432 INVALID_HANDLE_VALUE);
1433 This->timer = NULL;
1436 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1437 LeaveCriticalSection(&This->lock);
1438 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1439 return E_FAIL;
1442 mask = 0;
1443 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1444 LeaveCriticalSection(&This->lock);
1445 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1446 return E_FAIL;
1449 This->playing = FALSE;
1451 LeaveCriticalSection(&This->lock);
1453 return S_OK;
1456 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1458 ACImpl *This = impl_from_IAudioClient(iface);
1460 TRACE("(%p)\n", This);
1462 EnterCriticalSection(&This->lock);
1464 if(!This->initted){
1465 LeaveCriticalSection(&This->lock);
1466 return AUDCLNT_E_NOT_INITIALIZED;
1469 if(This->playing){
1470 LeaveCriticalSection(&This->lock);
1471 return AUDCLNT_E_NOT_STOPPED;
1474 if(This->buf_state != NOT_LOCKED){
1475 LeaveCriticalSection(&This->lock);
1476 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1479 This->written_frames = 0;
1480 This->inbuf_frames = 0;
1481 This->held_frames = 0;
1483 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1484 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1486 LeaveCriticalSection(&This->lock);
1488 return S_OK;
1491 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1492 HANDLE event)
1494 ACImpl *This = impl_from_IAudioClient(iface);
1496 TRACE("(%p)->(%p)\n", This, event);
1498 if(!event)
1499 return E_INVALIDARG;
1501 EnterCriticalSection(&This->lock);
1503 if(!This->initted){
1504 LeaveCriticalSection(&This->lock);
1505 return AUDCLNT_E_NOT_INITIALIZED;
1508 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1509 LeaveCriticalSection(&This->lock);
1510 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1513 This->event = event;
1515 LeaveCriticalSection(&This->lock);
1517 return S_OK;
1520 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1521 void **ppv)
1523 ACImpl *This = impl_from_IAudioClient(iface);
1525 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1527 if(!ppv)
1528 return E_POINTER;
1529 *ppv = NULL;
1531 EnterCriticalSection(&This->lock);
1533 if(!This->initted){
1534 LeaveCriticalSection(&This->lock);
1535 return AUDCLNT_E_NOT_INITIALIZED;
1538 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1539 if(This->dataflow != eRender){
1540 LeaveCriticalSection(&This->lock);
1541 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1543 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1544 *ppv = &This->IAudioRenderClient_iface;
1545 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1546 if(This->dataflow != eCapture){
1547 LeaveCriticalSection(&This->lock);
1548 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1550 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1551 *ppv = &This->IAudioCaptureClient_iface;
1552 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1553 IAudioClock_AddRef(&This->IAudioClock_iface);
1554 *ppv = &This->IAudioClock_iface;
1555 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1556 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1557 *ppv = &This->IAudioStreamVolume_iface;
1558 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1559 if(!This->session_wrapper){
1560 This->session_wrapper = AudioSessionWrapper_Create(This);
1561 if(!This->session_wrapper){
1562 LeaveCriticalSection(&This->lock);
1563 return E_OUTOFMEMORY;
1565 }else
1566 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1568 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1569 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1570 if(!This->session_wrapper){
1571 This->session_wrapper = AudioSessionWrapper_Create(This);
1572 if(!This->session_wrapper){
1573 LeaveCriticalSection(&This->lock);
1574 return E_OUTOFMEMORY;
1576 }else
1577 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1579 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1580 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1581 if(!This->session_wrapper){
1582 This->session_wrapper = AudioSessionWrapper_Create(This);
1583 if(!This->session_wrapper){
1584 LeaveCriticalSection(&This->lock);
1585 return E_OUTOFMEMORY;
1587 }else
1588 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1590 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1593 if(*ppv){
1594 LeaveCriticalSection(&This->lock);
1595 return S_OK;
1598 LeaveCriticalSection(&This->lock);
1600 FIXME("stub %s\n", debugstr_guid(riid));
1601 return E_NOINTERFACE;
1604 static const IAudioClientVtbl AudioClient_Vtbl =
1606 AudioClient_QueryInterface,
1607 AudioClient_AddRef,
1608 AudioClient_Release,
1609 AudioClient_Initialize,
1610 AudioClient_GetBufferSize,
1611 AudioClient_GetStreamLatency,
1612 AudioClient_GetCurrentPadding,
1613 AudioClient_IsFormatSupported,
1614 AudioClient_GetMixFormat,
1615 AudioClient_GetDevicePeriod,
1616 AudioClient_Start,
1617 AudioClient_Stop,
1618 AudioClient_Reset,
1619 AudioClient_SetEventHandle,
1620 AudioClient_GetService
1623 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1624 IAudioRenderClient *iface, REFIID riid, void **ppv)
1626 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1628 if(!ppv)
1629 return E_POINTER;
1630 *ppv = NULL;
1632 if(IsEqualIID(riid, &IID_IUnknown) ||
1633 IsEqualIID(riid, &IID_IAudioRenderClient))
1634 *ppv = iface;
1635 if(*ppv){
1636 IUnknown_AddRef((IUnknown*)*ppv);
1637 return S_OK;
1640 WARN("Unknown interface %s\n", debugstr_guid(riid));
1641 return E_NOINTERFACE;
1644 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1646 ACImpl *This = impl_from_IAudioRenderClient(iface);
1647 return AudioClient_AddRef(&This->IAudioClient_iface);
1650 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1652 ACImpl *This = impl_from_IAudioRenderClient(iface);
1653 return AudioClient_Release(&This->IAudioClient_iface);
1656 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1657 UINT32 frames, BYTE **data)
1659 ACImpl *This = impl_from_IAudioRenderClient(iface);
1660 UINT32 pad, write_pos;
1661 HRESULT hr;
1663 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1665 if(!data)
1666 return E_POINTER;
1668 EnterCriticalSection(&This->lock);
1670 if(This->buf_state != NOT_LOCKED){
1671 LeaveCriticalSection(&This->lock);
1672 return AUDCLNT_E_OUT_OF_ORDER;
1675 if(!frames){
1676 This->buf_state = LOCKED_NORMAL;
1677 LeaveCriticalSection(&This->lock);
1678 return S_OK;
1681 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1682 if(FAILED(hr)){
1683 LeaveCriticalSection(&This->lock);
1684 return hr;
1687 if(pad + frames > This->bufsize_frames){
1688 LeaveCriticalSection(&This->lock);
1689 return AUDCLNT_E_BUFFER_TOO_LARGE;
1692 write_pos =
1693 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1694 if(write_pos + frames > This->bufsize_frames){
1695 if(This->tmp_buffer_frames < frames){
1696 if(This->tmp_buffer)
1697 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1698 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1699 else
1700 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1701 frames * This->fmt->nBlockAlign);
1702 if(!This->tmp_buffer){
1703 LeaveCriticalSection(&This->lock);
1704 return E_OUTOFMEMORY;
1706 This->tmp_buffer_frames = frames;
1708 *data = This->tmp_buffer;
1709 This->buf_state = LOCKED_WRAPPED;
1710 }else{
1711 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1712 This->buf_state = LOCKED_NORMAL;
1715 LeaveCriticalSection(&This->lock);
1717 return S_OK;
1720 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1722 UINT32 write_offs_frames =
1723 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1724 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1725 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1726 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1727 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1729 if(written_bytes <= chunk_bytes){
1730 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1731 }else{
1732 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1733 memcpy(This->local_buffer, buffer + chunk_bytes,
1734 written_bytes - chunk_bytes);
1738 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1739 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1741 ACImpl *This = impl_from_IAudioRenderClient(iface);
1742 BYTE *buffer;
1744 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1746 EnterCriticalSection(&This->lock);
1748 if(This->buf_state == NOT_LOCKED || !written_frames){
1749 This->buf_state = NOT_LOCKED;
1750 LeaveCriticalSection(&This->lock);
1751 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1754 if(This->buf_state == LOCKED_NORMAL)
1755 buffer = This->local_buffer + This->fmt->nBlockAlign *
1756 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1757 else
1758 buffer = This->tmp_buffer;
1760 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1761 oss_silence_buffer(This, buffer, written_frames);
1763 if(This->held_frames){
1764 if(This->buf_state == LOCKED_WRAPPED)
1765 oss_wrap_buffer(This, buffer, written_frames);
1767 This->held_frames += written_frames;
1768 }else{
1769 ssize_t w_bytes;
1770 UINT32 w_frames;
1772 if(This->session->mute)
1773 oss_silence_buffer(This, buffer, written_frames);
1775 w_bytes = write(This->fd, buffer,
1776 written_frames * This->fmt->nBlockAlign);
1777 if(w_bytes < 0){
1778 if(errno != EAGAIN){
1779 This->buf_state = NOT_LOCKED;
1780 LeaveCriticalSection(&This->lock);
1781 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1782 return E_FAIL;
1783 }else /* OSS buffer full */
1784 w_bytes = 0;
1786 w_frames = w_bytes / This->fmt->nBlockAlign;
1787 This->inbuf_frames += w_frames;
1789 if(w_frames < written_frames){
1790 if(This->buf_state == LOCKED_WRAPPED)
1791 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1792 written_frames - w_frames);
1793 else
1794 This->lcl_offs_frames += w_frames;
1795 This->held_frames = written_frames - w_frames;
1799 This->written_frames += written_frames;
1800 This->buf_state = NOT_LOCKED;
1802 LeaveCriticalSection(&This->lock);
1804 return S_OK;
1807 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1808 AudioRenderClient_QueryInterface,
1809 AudioRenderClient_AddRef,
1810 AudioRenderClient_Release,
1811 AudioRenderClient_GetBuffer,
1812 AudioRenderClient_ReleaseBuffer
1815 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1816 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1818 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1820 if(!ppv)
1821 return E_POINTER;
1822 *ppv = NULL;
1824 if(IsEqualIID(riid, &IID_IUnknown) ||
1825 IsEqualIID(riid, &IID_IAudioCaptureClient))
1826 *ppv = iface;
1827 if(*ppv){
1828 IUnknown_AddRef((IUnknown*)*ppv);
1829 return S_OK;
1832 WARN("Unknown interface %s\n", debugstr_guid(riid));
1833 return E_NOINTERFACE;
1836 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1838 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1839 return IAudioClient_AddRef(&This->IAudioClient_iface);
1842 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1844 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1845 return IAudioClient_Release(&This->IAudioClient_iface);
1848 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1849 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1850 UINT64 *qpcpos)
1852 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1853 HRESULT hr;
1855 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1856 devpos, qpcpos);
1858 if(!data || !frames || !flags)
1859 return E_POINTER;
1861 EnterCriticalSection(&This->lock);
1863 if(This->buf_state != NOT_LOCKED){
1864 LeaveCriticalSection(&This->lock);
1865 return AUDCLNT_E_OUT_OF_ORDER;
1868 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1869 if(FAILED(hr)){
1870 LeaveCriticalSection(&This->lock);
1871 return hr;
1874 *flags = 0;
1876 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1877 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1878 if(This->tmp_buffer_frames < *frames){
1879 if(This->tmp_buffer)
1880 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1881 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1882 else
1883 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1884 *frames * This->fmt->nBlockAlign);
1885 if(!This->tmp_buffer){
1886 LeaveCriticalSection(&This->lock);
1887 return E_OUTOFMEMORY;
1889 This->tmp_buffer_frames = *frames;
1892 *data = This->tmp_buffer;
1893 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1894 This->fmt->nBlockAlign;
1895 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1896 frames_bytes = *frames * This->fmt->nBlockAlign;
1897 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1898 memcpy(This->tmp_buffer, This->local_buffer,
1899 frames_bytes - chunk_bytes);
1900 }else
1901 *data = This->local_buffer +
1902 This->lcl_offs_frames * This->fmt->nBlockAlign;
1904 This->buf_state = LOCKED_NORMAL;
1906 if(devpos || qpcpos)
1907 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1909 LeaveCriticalSection(&This->lock);
1911 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1914 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1915 IAudioCaptureClient *iface, UINT32 done)
1917 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1919 TRACE("(%p)->(%u)\n", This, done);
1921 EnterCriticalSection(&This->lock);
1923 if(This->buf_state == NOT_LOCKED){
1924 LeaveCriticalSection(&This->lock);
1925 return AUDCLNT_E_OUT_OF_ORDER;
1928 This->held_frames -= done;
1929 This->lcl_offs_frames += done;
1930 This->lcl_offs_frames %= This->bufsize_frames;
1932 This->buf_state = NOT_LOCKED;
1934 LeaveCriticalSection(&This->lock);
1936 return S_OK;
1939 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1940 IAudioCaptureClient *iface, UINT32 *frames)
1942 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1944 TRACE("(%p)->(%p)\n", This, frames);
1946 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1949 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1951 AudioCaptureClient_QueryInterface,
1952 AudioCaptureClient_AddRef,
1953 AudioCaptureClient_Release,
1954 AudioCaptureClient_GetBuffer,
1955 AudioCaptureClient_ReleaseBuffer,
1956 AudioCaptureClient_GetNextPacketSize
1959 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1960 REFIID riid, void **ppv)
1962 ACImpl *This = impl_from_IAudioClock(iface);
1964 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1966 if(!ppv)
1967 return E_POINTER;
1968 *ppv = NULL;
1970 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1971 *ppv = iface;
1972 else if(IsEqualIID(riid, &IID_IAudioClock2))
1973 *ppv = &This->IAudioClock2_iface;
1974 if(*ppv){
1975 IUnknown_AddRef((IUnknown*)*ppv);
1976 return S_OK;
1979 WARN("Unknown interface %s\n", debugstr_guid(riid));
1980 return E_NOINTERFACE;
1983 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1985 ACImpl *This = impl_from_IAudioClock(iface);
1986 return IAudioClient_AddRef(&This->IAudioClient_iface);
1989 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1991 ACImpl *This = impl_from_IAudioClock(iface);
1992 return IAudioClient_Release(&This->IAudioClient_iface);
1995 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1997 ACImpl *This = impl_from_IAudioClock(iface);
1999 TRACE("(%p)->(%p)\n", This, freq);
2001 *freq = This->fmt->nSamplesPerSec;
2003 return S_OK;
2006 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2007 UINT64 *qpctime)
2009 ACImpl *This = impl_from_IAudioClock(iface);
2010 UINT32 pad;
2011 HRESULT hr;
2013 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2015 if(!pos)
2016 return E_POINTER;
2018 EnterCriticalSection(&This->lock);
2020 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2021 if(FAILED(hr)){
2022 LeaveCriticalSection(&This->lock);
2023 return hr;
2026 if(This->dataflow == eRender)
2027 *pos = This->written_frames - pad;
2028 else if(This->dataflow == eCapture)
2029 *pos = This->written_frames + pad;
2031 LeaveCriticalSection(&This->lock);
2033 if(qpctime){
2034 LARGE_INTEGER stamp, freq;
2035 QueryPerformanceCounter(&stamp);
2036 QueryPerformanceFrequency(&freq);
2037 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2040 return S_OK;
2043 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2044 DWORD *chars)
2046 ACImpl *This = impl_from_IAudioClock(iface);
2048 TRACE("(%p)->(%p)\n", This, chars);
2050 if(!chars)
2051 return E_POINTER;
2053 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2055 return S_OK;
2058 static const IAudioClockVtbl AudioClock_Vtbl =
2060 AudioClock_QueryInterface,
2061 AudioClock_AddRef,
2062 AudioClock_Release,
2063 AudioClock_GetFrequency,
2064 AudioClock_GetPosition,
2065 AudioClock_GetCharacteristics
2068 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2069 REFIID riid, void **ppv)
2071 ACImpl *This = impl_from_IAudioClock2(iface);
2072 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2075 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2077 ACImpl *This = impl_from_IAudioClock2(iface);
2078 return IAudioClient_AddRef(&This->IAudioClient_iface);
2081 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2083 ACImpl *This = impl_from_IAudioClock2(iface);
2084 return IAudioClient_Release(&This->IAudioClient_iface);
2087 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2088 UINT64 *pos, UINT64 *qpctime)
2090 ACImpl *This = impl_from_IAudioClock2(iface);
2092 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2094 return E_NOTIMPL;
2097 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2099 AudioClock2_QueryInterface,
2100 AudioClock2_AddRef,
2101 AudioClock2_Release,
2102 AudioClock2_GetDevicePosition
2105 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2107 AudioSessionWrapper *ret;
2109 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2110 sizeof(AudioSessionWrapper));
2111 if(!ret)
2112 return NULL;
2114 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2115 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2116 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2118 ret->ref = 1;
2120 ret->client = client;
2121 if(client){
2122 ret->session = client->session;
2123 AudioClient_AddRef(&client->IAudioClient_iface);
2126 return ret;
2129 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2130 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2132 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2134 if(!ppv)
2135 return E_POINTER;
2136 *ppv = NULL;
2138 if(IsEqualIID(riid, &IID_IUnknown) ||
2139 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2140 IsEqualIID(riid, &IID_IAudioSessionControl2))
2141 *ppv = iface;
2142 if(*ppv){
2143 IUnknown_AddRef((IUnknown*)*ppv);
2144 return S_OK;
2147 WARN("Unknown interface %s\n", debugstr_guid(riid));
2148 return E_NOINTERFACE;
2151 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2153 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2154 ULONG ref;
2155 ref = InterlockedIncrement(&This->ref);
2156 TRACE("(%p) Refcount now %u\n", This, ref);
2157 return ref;
2160 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2162 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2163 ULONG ref;
2164 ref = InterlockedDecrement(&This->ref);
2165 TRACE("(%p) Refcount now %u\n", This, ref);
2166 if(!ref){
2167 if(This->client){
2168 EnterCriticalSection(&This->client->lock);
2169 This->client->session_wrapper = NULL;
2170 LeaveCriticalSection(&This->client->lock);
2171 AudioClient_Release(&This->client->IAudioClient_iface);
2173 HeapFree(GetProcessHeap(), 0, This);
2175 return ref;
2178 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2179 AudioSessionState *state)
2181 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2182 ACImpl *client;
2184 TRACE("(%p)->(%p)\n", This, state);
2186 if(!state)
2187 return NULL_PTR_ERR;
2189 EnterCriticalSection(&g_sessions_lock);
2191 if(list_empty(&This->session->clients)){
2192 *state = AudioSessionStateExpired;
2193 LeaveCriticalSection(&g_sessions_lock);
2194 return S_OK;
2197 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2198 EnterCriticalSection(&client->lock);
2199 if(client->playing){
2200 *state = AudioSessionStateActive;
2201 LeaveCriticalSection(&client->lock);
2202 LeaveCriticalSection(&g_sessions_lock);
2203 return S_OK;
2205 LeaveCriticalSection(&client->lock);
2208 LeaveCriticalSection(&g_sessions_lock);
2210 *state = AudioSessionStateInactive;
2212 return S_OK;
2215 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2216 IAudioSessionControl2 *iface, WCHAR **name)
2218 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2220 FIXME("(%p)->(%p) - stub\n", This, name);
2222 return E_NOTIMPL;
2225 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2226 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2228 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2230 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2232 return E_NOTIMPL;
2235 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2236 IAudioSessionControl2 *iface, WCHAR **path)
2238 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2240 FIXME("(%p)->(%p) - stub\n", This, path);
2242 return E_NOTIMPL;
2245 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2246 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2248 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2250 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2252 return E_NOTIMPL;
2255 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2256 IAudioSessionControl2 *iface, GUID *group)
2258 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2260 FIXME("(%p)->(%p) - stub\n", This, group);
2262 return E_NOTIMPL;
2265 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2266 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2268 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2270 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2271 debugstr_guid(session));
2273 return E_NOTIMPL;
2276 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2277 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2279 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2281 FIXME("(%p)->(%p) - stub\n", This, events);
2283 return S_OK;
2286 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2287 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2289 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2291 FIXME("(%p)->(%p) - stub\n", This, events);
2293 return S_OK;
2296 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2297 IAudioSessionControl2 *iface, WCHAR **id)
2299 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2301 FIXME("(%p)->(%p) - stub\n", This, id);
2303 return E_NOTIMPL;
2306 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2307 IAudioSessionControl2 *iface, WCHAR **id)
2309 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2311 FIXME("(%p)->(%p) - stub\n", This, id);
2313 return E_NOTIMPL;
2316 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2317 IAudioSessionControl2 *iface, DWORD *pid)
2319 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2321 TRACE("(%p)->(%p)\n", This, pid);
2323 if(!pid)
2324 return E_POINTER;
2326 *pid = GetCurrentProcessId();
2328 return S_OK;
2331 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2332 IAudioSessionControl2 *iface)
2334 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2336 TRACE("(%p)\n", This);
2338 return S_FALSE;
2341 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2342 IAudioSessionControl2 *iface, BOOL optout)
2344 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2346 TRACE("(%p)->(%d)\n", This, optout);
2348 return S_OK;
2351 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2353 AudioSessionControl_QueryInterface,
2354 AudioSessionControl_AddRef,
2355 AudioSessionControl_Release,
2356 AudioSessionControl_GetState,
2357 AudioSessionControl_GetDisplayName,
2358 AudioSessionControl_SetDisplayName,
2359 AudioSessionControl_GetIconPath,
2360 AudioSessionControl_SetIconPath,
2361 AudioSessionControl_GetGroupingParam,
2362 AudioSessionControl_SetGroupingParam,
2363 AudioSessionControl_RegisterAudioSessionNotification,
2364 AudioSessionControl_UnregisterAudioSessionNotification,
2365 AudioSessionControl_GetSessionIdentifier,
2366 AudioSessionControl_GetSessionInstanceIdentifier,
2367 AudioSessionControl_GetProcessId,
2368 AudioSessionControl_IsSystemSoundsSession,
2369 AudioSessionControl_SetDuckingPreference
2372 /* index == -1 means set all channels, otherwise sets only the given channel */
2373 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2375 int setreq, getreq;
2376 unsigned int vol;
2377 unsigned short l;
2378 float level;
2380 if(index == (UINT32)-1){
2381 HRESULT ret = S_OK;
2382 UINT32 i;
2383 for(i = 0; i < This->fmt->nChannels; ++i){
2384 HRESULT hr;
2385 hr = oss_setvol(This, i);
2386 if(FAILED(hr))
2387 ret = hr;
2389 return ret;
2392 if(index > 1)
2393 /* OSS doesn't support volume control past the first two channels */
2394 return S_OK;
2396 if(This->dataflow == eRender){
2397 setreq = SNDCTL_DSP_SETPLAYVOL;
2398 getreq = SNDCTL_DSP_GETPLAYVOL;
2399 }else if(This->dataflow == eCapture){
2400 setreq = SNDCTL_DSP_SETRECVOL;
2401 getreq = SNDCTL_DSP_GETRECVOL;
2402 }else
2403 return E_UNEXPECTED;
2405 if(ioctl(This->fd, getreq, &vol) < 0){
2406 if(errno == EINVAL)
2407 /* device doesn't support this call */
2408 return S_OK;
2410 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2411 return E_FAIL;
2414 level = This->session->master_vol * This->session->channel_vols[index] *
2415 This->vols[index];
2416 l = level * 100;
2417 if(index == 0)
2418 vol = l | (vol & 0xFF00);
2419 else
2420 vol = (vol & 0xFF) | (l << 8);
2422 if(ioctl(This->fd, setreq, &vol) < 0){
2423 if(errno == EINVAL)
2424 /* device doesn't support this call */
2425 return S_OK;
2427 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2428 return E_FAIL;
2431 return S_OK;
2434 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2436 HRESULT ret = S_OK;
2437 ACImpl *client;
2439 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2440 HRESULT hr;
2441 hr = oss_setvol(client, index);
2442 if(FAILED(hr))
2443 ret = hr;
2446 return ret;
2449 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2450 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2452 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2454 if(!ppv)
2455 return E_POINTER;
2456 *ppv = NULL;
2458 if(IsEqualIID(riid, &IID_IUnknown) ||
2459 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2460 *ppv = iface;
2461 if(*ppv){
2462 IUnknown_AddRef((IUnknown*)*ppv);
2463 return S_OK;
2466 WARN("Unknown interface %s\n", debugstr_guid(riid));
2467 return E_NOINTERFACE;
2470 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2472 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2473 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2476 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2478 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2479 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2482 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2483 ISimpleAudioVolume *iface, float level, const GUID *context)
2485 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2486 AudioSession *session = This->session;
2487 HRESULT ret;
2489 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2491 if(level < 0.f || level > 1.f)
2492 return E_INVALIDARG;
2494 if(context)
2495 FIXME("Notifications not supported yet\n");
2497 EnterCriticalSection(&session->lock);
2499 session->master_vol = level;
2501 ret = oss_session_setvol(session, -1);
2503 LeaveCriticalSection(&session->lock);
2505 return ret;
2508 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2509 ISimpleAudioVolume *iface, float *level)
2511 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2512 AudioSession *session = This->session;
2514 TRACE("(%p)->(%p)\n", session, level);
2516 if(!level)
2517 return NULL_PTR_ERR;
2519 *level = session->master_vol;
2521 return S_OK;
2524 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2525 BOOL mute, const GUID *context)
2527 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2528 AudioSession *session = This->session;
2530 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2532 EnterCriticalSection(&session->lock);
2534 if(!mute && session->mute){
2535 ACImpl *client;
2537 session->mute = mute;
2539 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2540 EnterCriticalSection(&client->lock);
2541 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2542 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2543 strerror(errno));
2544 oss_write_data(client);
2545 LeaveCriticalSection(&client->lock);
2547 }else
2548 session->mute = mute;
2550 LeaveCriticalSection(&session->lock);
2552 return S_OK;
2555 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2556 BOOL *mute)
2558 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2559 AudioSession *session = This->session;
2561 TRACE("(%p)->(%p)\n", session, mute);
2563 if(!mute)
2564 return NULL_PTR_ERR;
2566 *mute = This->session->mute;
2568 return S_OK;
2571 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2573 SimpleAudioVolume_QueryInterface,
2574 SimpleAudioVolume_AddRef,
2575 SimpleAudioVolume_Release,
2576 SimpleAudioVolume_SetMasterVolume,
2577 SimpleAudioVolume_GetMasterVolume,
2578 SimpleAudioVolume_SetMute,
2579 SimpleAudioVolume_GetMute
2582 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2583 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2585 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2587 if(!ppv)
2588 return E_POINTER;
2589 *ppv = NULL;
2591 if(IsEqualIID(riid, &IID_IUnknown) ||
2592 IsEqualIID(riid, &IID_IAudioStreamVolume))
2593 *ppv = iface;
2594 if(*ppv){
2595 IUnknown_AddRef((IUnknown*)*ppv);
2596 return S_OK;
2599 WARN("Unknown interface %s\n", debugstr_guid(riid));
2600 return E_NOINTERFACE;
2603 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2605 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2606 return IAudioClient_AddRef(&This->IAudioClient_iface);
2609 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2611 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2612 return IAudioClient_Release(&This->IAudioClient_iface);
2615 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2616 IAudioStreamVolume *iface, UINT32 *out)
2618 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2620 TRACE("(%p)->(%p)\n", This, out);
2622 if(!out)
2623 return E_POINTER;
2625 *out = This->fmt->nChannels;
2627 return S_OK;
2630 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2631 IAudioStreamVolume *iface, UINT32 index, float level)
2633 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2634 HRESULT ret;
2636 TRACE("(%p)->(%d, %f)\n", This, index, level);
2638 if(level < 0.f || level > 1.f)
2639 return E_INVALIDARG;
2641 if(index >= This->fmt->nChannels)
2642 return E_INVALIDARG;
2644 EnterCriticalSection(&This->lock);
2646 This->vols[index] = level;
2648 ret = oss_setvol(This, index);
2650 LeaveCriticalSection(&This->lock);
2652 return ret;
2655 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2656 IAudioStreamVolume *iface, UINT32 index, float *level)
2658 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2660 TRACE("(%p)->(%d, %p)\n", This, index, level);
2662 if(!level)
2663 return E_POINTER;
2665 if(index >= This->fmt->nChannels)
2666 return E_INVALIDARG;
2668 *level = This->vols[index];
2670 return S_OK;
2673 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2674 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2676 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2677 int i;
2678 HRESULT ret;
2680 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2682 if(!levels)
2683 return E_POINTER;
2685 if(count != This->fmt->nChannels)
2686 return E_INVALIDARG;
2688 EnterCriticalSection(&This->lock);
2690 for(i = 0; i < count; ++i)
2691 This->vols[i] = levels[i];
2693 ret = oss_setvol(This, -1);
2695 LeaveCriticalSection(&This->lock);
2697 return ret;
2700 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2701 IAudioStreamVolume *iface, UINT32 count, float *levels)
2703 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2704 int i;
2706 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2708 if(!levels)
2709 return E_POINTER;
2711 if(count != This->fmt->nChannels)
2712 return E_INVALIDARG;
2714 EnterCriticalSection(&This->lock);
2716 for(i = 0; i < count; ++i)
2717 levels[i] = This->vols[i];
2719 LeaveCriticalSection(&This->lock);
2721 return S_OK;
2724 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2726 AudioStreamVolume_QueryInterface,
2727 AudioStreamVolume_AddRef,
2728 AudioStreamVolume_Release,
2729 AudioStreamVolume_GetChannelCount,
2730 AudioStreamVolume_SetChannelVolume,
2731 AudioStreamVolume_GetChannelVolume,
2732 AudioStreamVolume_SetAllVolumes,
2733 AudioStreamVolume_GetAllVolumes
2736 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2737 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2739 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2741 if(!ppv)
2742 return E_POINTER;
2743 *ppv = NULL;
2745 if(IsEqualIID(riid, &IID_IUnknown) ||
2746 IsEqualIID(riid, &IID_IChannelAudioVolume))
2747 *ppv = iface;
2748 if(*ppv){
2749 IUnknown_AddRef((IUnknown*)*ppv);
2750 return S_OK;
2753 WARN("Unknown interface %s\n", debugstr_guid(riid));
2754 return E_NOINTERFACE;
2757 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2759 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2760 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2763 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2765 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2766 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2769 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2770 IChannelAudioVolume *iface, UINT32 *out)
2772 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2773 AudioSession *session = This->session;
2775 TRACE("(%p)->(%p)\n", session, out);
2777 if(!out)
2778 return NULL_PTR_ERR;
2780 *out = session->channel_count;
2782 return S_OK;
2785 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2786 IChannelAudioVolume *iface, UINT32 index, float level,
2787 const GUID *context)
2789 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2790 AudioSession *session = This->session;
2791 HRESULT ret;
2793 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2794 wine_dbgstr_guid(context));
2796 if(level < 0.f || level > 1.f)
2797 return E_INVALIDARG;
2799 if(index >= session->channel_count)
2800 return E_INVALIDARG;
2802 if(context)
2803 FIXME("Notifications not supported yet\n");
2805 EnterCriticalSection(&session->lock);
2807 session->channel_vols[index] = level;
2809 ret = oss_session_setvol(session, index);
2811 LeaveCriticalSection(&session->lock);
2813 return ret;
2816 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2817 IChannelAudioVolume *iface, UINT32 index, float *level)
2819 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2820 AudioSession *session = This->session;
2822 TRACE("(%p)->(%d, %p)\n", session, index, level);
2824 if(!level)
2825 return NULL_PTR_ERR;
2827 if(index >= session->channel_count)
2828 return E_INVALIDARG;
2830 *level = session->channel_vols[index];
2832 return S_OK;
2835 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2836 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2837 const GUID *context)
2839 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2840 AudioSession *session = This->session;
2841 int i;
2842 HRESULT ret;
2844 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2845 wine_dbgstr_guid(context));
2847 if(!levels)
2848 return NULL_PTR_ERR;
2850 if(count != session->channel_count)
2851 return E_INVALIDARG;
2853 if(context)
2854 FIXME("Notifications not supported yet\n");
2856 EnterCriticalSection(&session->lock);
2858 for(i = 0; i < count; ++i)
2859 session->channel_vols[i] = levels[i];
2861 ret = oss_session_setvol(session, -1);
2863 LeaveCriticalSection(&session->lock);
2865 return ret;
2868 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2869 IChannelAudioVolume *iface, UINT32 count, float *levels)
2871 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2872 AudioSession *session = This->session;
2873 int i;
2875 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2877 if(!levels)
2878 return NULL_PTR_ERR;
2880 if(count != session->channel_count)
2881 return E_INVALIDARG;
2883 for(i = 0; i < count; ++i)
2884 levels[i] = session->channel_vols[i];
2886 return S_OK;
2889 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2891 ChannelAudioVolume_QueryInterface,
2892 ChannelAudioVolume_AddRef,
2893 ChannelAudioVolume_Release,
2894 ChannelAudioVolume_GetChannelCount,
2895 ChannelAudioVolume_SetChannelVolume,
2896 ChannelAudioVolume_GetChannelVolume,
2897 ChannelAudioVolume_SetAllVolumes,
2898 ChannelAudioVolume_GetAllVolumes
2901 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2902 REFIID riid, void **ppv)
2904 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2906 if(!ppv)
2907 return E_POINTER;
2908 *ppv = NULL;
2910 if(IsEqualIID(riid, &IID_IUnknown) ||
2911 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2912 IsEqualIID(riid, &IID_IAudioSessionManager2))
2913 *ppv = iface;
2914 if(*ppv){
2915 IUnknown_AddRef((IUnknown*)*ppv);
2916 return S_OK;
2919 WARN("Unknown interface %s\n", debugstr_guid(riid));
2920 return E_NOINTERFACE;
2923 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2925 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2926 ULONG ref;
2927 ref = InterlockedIncrement(&This->ref);
2928 TRACE("(%p) Refcount now %u\n", This, ref);
2929 return ref;
2932 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2934 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2935 ULONG ref;
2936 ref = InterlockedDecrement(&This->ref);
2937 TRACE("(%p) Refcount now %u\n", This, ref);
2938 if(!ref)
2939 HeapFree(GetProcessHeap(), 0, This);
2940 return ref;
2943 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2944 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2945 IAudioSessionControl **out)
2947 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2948 AudioSession *session;
2949 AudioSessionWrapper *wrapper;
2950 HRESULT hr;
2952 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2953 flags, out);
2955 hr = get_audio_session(session_guid, This->device, 0, &session);
2956 if(FAILED(hr))
2957 return hr;
2959 wrapper = AudioSessionWrapper_Create(NULL);
2960 if(!wrapper)
2961 return E_OUTOFMEMORY;
2963 wrapper->session = session;
2965 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2967 return S_OK;
2970 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2971 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2972 ISimpleAudioVolume **out)
2974 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2975 AudioSession *session;
2976 AudioSessionWrapper *wrapper;
2977 HRESULT hr;
2979 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2980 flags, out);
2982 hr = get_audio_session(session_guid, This->device, 0, &session);
2983 if(FAILED(hr))
2984 return hr;
2986 wrapper = AudioSessionWrapper_Create(NULL);
2987 if(!wrapper)
2988 return E_OUTOFMEMORY;
2990 wrapper->session = session;
2992 *out = &wrapper->ISimpleAudioVolume_iface;
2994 return S_OK;
2997 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2998 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3000 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3001 FIXME("(%p)->(%p) - stub\n", This, out);
3002 return E_NOTIMPL;
3005 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3006 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3008 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3009 FIXME("(%p)->(%p) - stub\n", This, notification);
3010 return E_NOTIMPL;
3013 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3014 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3016 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3017 FIXME("(%p)->(%p) - stub\n", This, notification);
3018 return E_NOTIMPL;
3021 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3022 IAudioSessionManager2 *iface, const WCHAR *session_id,
3023 IAudioVolumeDuckNotification *notification)
3025 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3026 FIXME("(%p)->(%p) - stub\n", This, notification);
3027 return E_NOTIMPL;
3030 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3031 IAudioSessionManager2 *iface,
3032 IAudioVolumeDuckNotification *notification)
3034 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3035 FIXME("(%p)->(%p) - stub\n", This, notification);
3036 return E_NOTIMPL;
3039 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3041 AudioSessionManager_QueryInterface,
3042 AudioSessionManager_AddRef,
3043 AudioSessionManager_Release,
3044 AudioSessionManager_GetAudioSessionControl,
3045 AudioSessionManager_GetSimpleAudioVolume,
3046 AudioSessionManager_GetSessionEnumerator,
3047 AudioSessionManager_RegisterSessionNotification,
3048 AudioSessionManager_UnregisterSessionNotification,
3049 AudioSessionManager_RegisterDuckNotification,
3050 AudioSessionManager_UnregisterDuckNotification
3053 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3054 IAudioSessionManager2 **out)
3056 SessionMgr *This;
3058 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3059 if(!This)
3060 return E_OUTOFMEMORY;
3062 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3063 This->device = device;
3064 This->ref = 1;
3066 *out = &This->IAudioSessionManager2_iface;
3068 return S_OK;