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
29 #include <sys/types.h>
31 #include <sys/ioctl.h>
35 #include <sys/soundcard.h>
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
46 #include "mmdeviceapi.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
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;
71 typedef struct ACImpl ACImpl;
73 typedef struct _AudioSession {
84 CRITICAL_SECTION lock;
89 typedef struct _AudioSessionWrapper {
90 IAudioSessionControl2 IAudioSessionControl2_iface;
91 IChannelAudioVolume IChannelAudioVolume_iface;
92 ISimpleAudioVolume ISimpleAudioVolume_iface;
97 AudioSession *session;
98 } AudioSessionWrapper;
101 IAudioClient IAudioClient_iface;
102 IAudioRenderClient IAudioRenderClient_iface;
103 IAudioCaptureClient IAudioCaptureClient_iface;
104 IAudioClock IAudioClock_iface;
105 IAudioClock2 IAudioClock2_iface;
106 IAudioStreamVolume IAudioStreamVolume_iface;
116 AUDCLNT_SHAREMODE share;
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;
133 CRITICAL_SECTION lock;
135 AudioSession *session;
136 AudioSessionWrapper *session_wrapper;
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;
155 static HANDLE g_timer_q;
157 static CRITICAL_SECTION g_sessions_lock;
158 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
160 0, 0, &g_sessions_lock,
161 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
162 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
164 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
165 static struct list g_sessions = LIST_INIT(g_sessions);
167 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
168 static HRESULT oss_setvol(ACImpl *This, UINT32 index);
170 static const IAudioClientVtbl AudioClient_Vtbl;
171 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
172 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
173 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
174 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
175 static const IAudioClockVtbl AudioClock_Vtbl;
176 static const IAudioClock2Vtbl AudioClock2_Vtbl;
177 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
178 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
179 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
181 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
183 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
186 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
188 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
191 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
193 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
196 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
198 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
201 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
203 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
206 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
208 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
211 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
213 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
216 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
218 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
221 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
223 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
226 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
228 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
231 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
235 case DLL_PROCESS_ATTACH:
236 g_timer_q = CreateTimerQueue();
241 case DLL_PROCESS_DETACH:
242 DeleteCriticalSection(&g_sessions_lock);
248 /* From <dlls/mmdevapi/mmdevapi.h> */
249 enum DriverPriority {
250 Priority_Unavailable = 0,
256 int WINAPI AUDDRV_GetPriority(void)
261 /* Attempt to determine if we are running on OSS or ALSA's OSS
262 * compatibility layer. There is no official way to do that, so just check
263 * for validity as best as possible, without rejecting valid OSS
264 * implementations. */
266 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
268 TRACE("Priority_Unavailable: open failed\n");
269 return Priority_Unavailable;
272 sysinfo.version[0] = 0xFF;
273 sysinfo.versionnum = ~0;
274 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
275 TRACE("Priority_Unavailable: ioctl failed\n");
277 return Priority_Unavailable;
282 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
283 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
286 if(sysinfo.versionnum & 0x80000000){
287 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
291 TRACE("Priority_Preferred: Seems like valid OSS!\n");
293 return Priority_Preferred;
296 static const char *oss_clean_devnode(const char *devnode)
298 static char ret[OSS_DEVNODE_SIZE];
300 const char *dot, *slash;
303 dot = strrchr(devnode, '.');
307 slash = strrchr(devnode, '/');
308 if(slash && dot < slash)
313 memcpy(ret, devnode, len);
319 static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
326 fd = open("/dev/dsp", O_WRONLY);
328 fd = open("/dev/dsp", O_RDONLY);
331 WARN("Couldn't open default device!\n");
336 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
337 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
344 TRACE("Default devnode: %s\n", ai.devnode);
345 devnode = oss_clean_devnode(ai.devnode);
346 for(i = 0; i < num; ++i)
347 if(!strcmp(devnode, keys[i]))
350 WARN("Couldn't find default device! Choosing first.\n");
354 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
355 UINT *num, UINT *def_index)
359 static int print_once = 0;
361 TRACE("%d %p %p %p\n", flow, ids, num, def_index);
363 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
365 ERR("OSS /dev/mixer doesn't seem to exist\n");
366 return AUDCLNT_E_SERVICE_NOT_RUNNING;
369 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
373 ERR("OSS version too old, need at least OSSv4\n");
374 return AUDCLNT_E_SERVICE_NOT_RUNNING;
377 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
382 TRACE("OSS sysinfo:\n");
383 TRACE("product: %s\n", sysinfo.product);
384 TRACE("version: %s\n", sysinfo.version);
385 TRACE("versionnum: %x\n", sysinfo.versionnum);
386 TRACE("numaudios: %d\n", sysinfo.numaudios);
387 TRACE("nummixers: %d\n", sysinfo.nummixers);
388 TRACE("numcards: %d\n", sysinfo.numcards);
389 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
393 if(sysinfo.numaudios <= 0){
394 WARN("No audio devices!\n");
396 return AUDCLNT_E_SERVICE_NOT_RUNNING;
399 *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
400 *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
403 for(i = 0; i < sysinfo.numaudios; ++i){
404 oss_audioinfo ai = {0};
409 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
410 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
415 devnode = oss_clean_devnode(ai.devnode);
417 /* check for duplicates */
418 for(j = 0; j < *num; ++j)
419 if(!strcmp(devnode, (*keys)[j]))
425 fd = open(devnode, O_WRONLY, 0);
427 fd = open(devnode, O_RDONLY, 0);
429 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
430 devnode, errno, strerror(errno));
435 if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
436 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
439 (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
440 strlen(devnode) + 1);
442 for(i = 0; i < *num; ++i){
443 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
444 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
446 HeapFree(GetProcessHeap(), 0, *ids);
447 HeapFree(GetProcessHeap(), 0, *keys);
449 return E_OUTOFMEMORY;
451 strcpy((*keys)[*num], devnode);
453 len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
454 (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
455 len * sizeof(WCHAR));
457 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
458 for(i = 0; i < *num; ++i){
459 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
460 HeapFree(GetProcessHeap(), 0, (*keys)[i]);
462 HeapFree(GetProcessHeap(), 0, *ids);
463 HeapFree(GetProcessHeap(), 0, *keys);
465 return E_OUTOFMEMORY;
467 MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
476 *def_index = get_default_index(flow, *keys, *num);
481 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
482 EDataFlow dataflow, IAudioClient **out)
486 TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
488 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
490 return E_OUTOFMEMORY;
492 if(dataflow == eRender)
493 This->fd = open(devnode, O_WRONLY, 0);
494 else if(dataflow == eCapture)
495 This->fd = open(devnode, O_RDONLY, 0);
497 HeapFree(GetProcessHeap(), 0, This);
501 ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
503 HeapFree(GetProcessHeap(), 0, This);
504 return AUDCLNT_E_DEVICE_INVALIDATED;
507 This->dataflow = dataflow;
510 if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
511 ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
512 errno, strerror(errno));
514 HeapFree(GetProcessHeap(), 0, This);
518 strcpy(This->devnode, devnode);
520 TRACE("OSS audioinfo:\n");
521 TRACE("devnode: %s\n", This->ai.devnode);
522 TRACE("name: %s\n", This->ai.name);
523 TRACE("busy: %x\n", This->ai.busy);
524 TRACE("caps: %x\n", This->ai.caps);
525 TRACE("iformats: %x\n", This->ai.iformats);
526 TRACE("oformats: %x\n", This->ai.oformats);
527 TRACE("enabled: %d\n", This->ai.enabled);
528 TRACE("min_rate: %d\n", This->ai.min_rate);
529 TRACE("max_rate: %d\n", This->ai.max_rate);
530 TRACE("min_channels: %d\n", This->ai.min_channels);
531 TRACE("max_channels: %d\n", This->ai.max_channels);
533 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
534 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
535 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
536 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
537 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
538 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
540 InitializeCriticalSection(&This->lock);
541 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
544 IMMDevice_AddRef(This->parent);
546 IAudioClient_AddRef(&This->IAudioClient_iface);
548 *out = &This->IAudioClient_iface;
553 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
554 REFIID riid, void **ppv)
556 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
561 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
564 IUnknown_AddRef((IUnknown*)*ppv);
567 WARN("Unknown interface %s\n", debugstr_guid(riid));
568 return E_NOINTERFACE;
571 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
573 ACImpl *This = impl_from_IAudioClient(iface);
575 ref = InterlockedIncrement(&This->ref);
576 TRACE("(%p) Refcount now %u\n", This, ref);
580 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
582 ACImpl *This = impl_from_IAudioClient(iface);
584 ref = InterlockedDecrement(&This->ref);
585 TRACE("(%p) Refcount now %u\n", This, ref);
587 IAudioClient_Stop(iface);
588 IMMDevice_Release(This->parent);
589 This->lock.DebugInfo->Spare[0] = 0;
590 DeleteCriticalSection(&This->lock);
593 EnterCriticalSection(&g_sessions_lock);
594 list_remove(&This->entry);
595 LeaveCriticalSection(&g_sessions_lock);
597 HeapFree(GetProcessHeap(), 0, This->vols);
598 HeapFree(GetProcessHeap(), 0, This->local_buffer);
599 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
600 CoTaskMemFree(This->fmt);
601 HeapFree(GetProcessHeap(), 0, This);
606 static void dump_fmt(const WAVEFORMATEX *fmt)
608 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
609 switch(fmt->wFormatTag){
610 case WAVE_FORMAT_PCM:
611 TRACE("WAVE_FORMAT_PCM");
613 case WAVE_FORMAT_IEEE_FLOAT:
614 TRACE("WAVE_FORMAT_IEEE_FLOAT");
616 case WAVE_FORMAT_EXTENSIBLE:
617 TRACE("WAVE_FORMAT_EXTENSIBLE");
625 TRACE("nChannels: %u\n", fmt->nChannels);
626 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
627 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
628 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
629 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
630 TRACE("cbSize: %u\n", fmt->cbSize);
632 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
633 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
634 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
635 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
636 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
640 static DWORD get_channel_mask(unsigned int channels)
646 return KSAUDIO_SPEAKER_MONO;
648 return KSAUDIO_SPEAKER_STEREO;
650 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
652 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
654 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
656 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
658 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
660 return KSAUDIO_SPEAKER_7POINT1; /* not 7POINT1_SURROUND */
662 FIXME("Unknown speaker configuration: %u\n", channels);
666 static int get_oss_format(const WAVEFORMATEX *fmt)
668 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
670 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
671 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
672 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
673 switch(fmt->wBitsPerSample){
687 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
688 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
689 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
690 if(fmt->wBitsPerSample != 32)
700 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
705 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
706 size = sizeof(WAVEFORMATEXTENSIBLE);
708 size = sizeof(WAVEFORMATEX);
710 ret = CoTaskMemAlloc(size);
714 memcpy(ret, fmt, size);
716 ret->cbSize = size - sizeof(WAVEFORMATEX);
721 static HRESULT setup_oss_device(int fd, const WAVEFORMATEX *fmt,
722 WAVEFORMATEX **out, BOOL query)
727 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
728 WAVEFORMATEX *closest = NULL;
730 tmp = oss_format = get_oss_format(fmt);
732 return AUDCLNT_E_UNSUPPORTED_FORMAT;
733 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
734 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
737 if(tmp != oss_format){
738 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
739 return AUDCLNT_E_UNSUPPORTED_FORMAT;
742 closest = clone_format(fmt);
744 return E_OUTOFMEMORY;
746 tmp = fmt->nSamplesPerSec;
747 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
748 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
749 CoTaskMemFree(closest);
752 tenth = fmt->nSamplesPerSec * 0.1;
753 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
755 closest->nSamplesPerSec = tmp;
758 tmp = fmt->nChannels;
759 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
760 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
761 CoTaskMemFree(closest);
764 if(tmp != fmt->nChannels){
766 closest->nChannels = tmp;
769 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
770 DWORD mask = get_channel_mask(closest->nChannels);
772 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
774 if(query && fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
775 fmtex->dwChannelMask != 0 &&
776 fmtex->dwChannelMask != mask)
780 if(ret == S_FALSE && !out)
781 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
783 if(ret == S_FALSE && out){
784 closest->nBlockAlign =
785 closest->nChannels * closest->wBitsPerSample / 8;
786 closest->nAvgBytesPerSec =
787 closest->nBlockAlign * closest->nSamplesPerSec;
790 CoTaskMemFree(closest);
792 TRACE("returning: %08x\n", ret);
796 static void session_init_vols(AudioSession *session, UINT channels)
798 if(session->channel_count < channels){
801 if(session->channel_vols)
802 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
803 session->channel_vols, sizeof(float) * channels);
805 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
806 sizeof(float) * channels);
807 if(!session->channel_vols)
810 for(i = session->channel_count; i < channels; ++i)
811 session->channel_vols[i] = 1.f;
813 session->channel_count = channels;
817 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
822 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
826 memcpy(&ret->guid, guid, sizeof(GUID));
828 ret->device = device;
830 list_init(&ret->clients);
832 list_add_head(&g_sessions, &ret->entry);
834 InitializeCriticalSection(&ret->lock);
835 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
837 session_init_vols(ret, num_channels);
839 ret->master_vol = 1.f;
844 /* if channels == 0, then this will return or create a session with
845 * matching dataflow and GUID. otherwise, channels must also match */
846 static HRESULT get_audio_session(const GUID *sessionguid,
847 IMMDevice *device, UINT channels, AudioSession **out)
849 AudioSession *session;
851 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
852 *out = create_session(&GUID_NULL, device, channels);
854 return E_OUTOFMEMORY;
860 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
861 if(session->device == device &&
862 IsEqualGUID(sessionguid, &session->guid)){
863 session_init_vols(session, channels);
870 *out = create_session(sessionguid, device, channels);
872 return E_OUTOFMEMORY;
878 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
879 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
880 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
881 const GUID *sessionguid)
883 ACImpl *This = impl_from_IAudioClient(iface);
887 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
888 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
895 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
896 return AUDCLNT_E_NOT_INITIALIZED;
898 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
899 AUDCLNT_STREAMFLAGS_LOOPBACK |
900 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
901 AUDCLNT_STREAMFLAGS_NOPERSIST |
902 AUDCLNT_STREAMFLAGS_RATEADJUST |
903 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
904 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
905 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
906 TRACE("Unknown flags: %08x\n", flags);
910 EnterCriticalSection(&This->lock);
913 LeaveCriticalSection(&This->lock);
914 return AUDCLNT_E_ALREADY_INITIALIZED;
917 hr = setup_oss_device(This->fd, fmt, NULL, FALSE);
919 LeaveCriticalSection(&This->lock);
924 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
925 LeaveCriticalSection(&This->lock);
926 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
930 mask = (100 << 8) | 100;
931 if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
932 WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
934 This->fmt = clone_format(fmt);
936 LeaveCriticalSection(&This->lock);
937 return E_OUTOFMEMORY;
941 This->period_us = period / 10;
943 This->period_us = DefaultPeriod / 10;
946 duration = 300000; /* 0.03s */
947 This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
948 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
949 This->bufsize_frames * fmt->nBlockAlign);
950 if(!This->local_buffer){
951 CoTaskMemFree(This->fmt);
953 LeaveCriticalSection(&This->lock);
954 return E_OUTOFMEMORY;
957 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
959 CoTaskMemFree(This->fmt);
961 LeaveCriticalSection(&This->lock);
962 return E_OUTOFMEMORY;
965 for(i = 0; i < fmt->nChannels; ++i)
971 EnterCriticalSection(&g_sessions_lock);
973 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
976 LeaveCriticalSection(&g_sessions_lock);
977 HeapFree(GetProcessHeap(), 0, This->vols);
979 CoTaskMemFree(This->fmt);
981 LeaveCriticalSection(&This->lock);
985 list_add_tail(&This->session->clients, &This->entry);
987 LeaveCriticalSection(&g_sessions_lock);
989 This->initted = TRUE;
991 oss_setvol(This, -1);
993 LeaveCriticalSection(&This->lock);
998 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1001 ACImpl *This = impl_from_IAudioClient(iface);
1003 TRACE("(%p)->(%p)\n", This, frames);
1008 EnterCriticalSection(&This->lock);
1011 LeaveCriticalSection(&This->lock);
1012 return AUDCLNT_E_NOT_INITIALIZED;
1015 *frames = This->bufsize_frames;
1017 LeaveCriticalSection(&This->lock);
1022 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1023 REFERENCE_TIME *latency)
1025 ACImpl *This = impl_from_IAudioClient(iface);
1027 TRACE("(%p)->(%p)\n", This, latency);
1032 EnterCriticalSection(&This->lock);
1035 LeaveCriticalSection(&This->lock);
1036 return AUDCLNT_E_NOT_INITIALIZED;
1039 if(This->dataflow == eRender){
1043 if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
1044 LeaveCriticalSection(&This->lock);
1045 WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
1049 delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
1050 This->fmt->nBlockAlign);
1052 *latency = delay_s * 10000000;
1054 *latency = 10000; /* OSS doesn't provide input latency */
1056 LeaveCriticalSection(&This->lock);
1061 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1064 ACImpl *This = impl_from_IAudioClient(iface);
1067 TRACE("(%p)->(%p)\n", This, numpad);
1072 EnterCriticalSection(&This->lock);
1075 LeaveCriticalSection(&This->lock);
1076 return AUDCLNT_E_NOT_INITIALIZED;
1079 if(This->dataflow == eRender){
1080 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1081 LeaveCriticalSection(&This->lock);
1082 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1086 *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
1087 This->fmt->nBlockAlign;
1089 /* when the OSS buffer has less than one fragment of data, including
1090 * no data, it often reports it as some non-zero portion of a
1091 * fragment. when it has more than one fragment of data, it reports
1092 * it as some multiple of that portion of the fragment size.
1094 * so, we have to do some ugly workarounds to report the timing
1095 * as accurately as possible */
1096 if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
1097 *numpad = This->inbuf_frames;
1098 This->inbuf_frames = 0;
1100 if(*numpad < This->inbuf_frames)
1101 This->inbuf_frames = *numpad;
1103 *numpad = This->inbuf_frames;
1105 }else if(This->dataflow == eCapture){
1106 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1107 LeaveCriticalSection(&This->lock);
1108 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1112 if(bi.bytes <= bi.fragsize)
1115 *numpad = bi.bytes / This->fmt->nBlockAlign;
1117 LeaveCriticalSection(&This->lock);
1118 return E_UNEXPECTED;
1121 *numpad += This->held_frames;
1123 LeaveCriticalSection(&This->lock);
1128 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1129 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
1130 WAVEFORMATEX **outpwfx)
1132 ACImpl *This = impl_from_IAudioClient(iface);
1136 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
1138 if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
1141 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1142 return E_INVALIDARG;
1144 if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1145 pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1146 return E_INVALIDARG;
1152 if(mode != AUDCLNT_SHAREMODE_SHARED)
1156 if(This->dataflow == eRender)
1157 fd = open(This->devnode, O_WRONLY, 0);
1158 else if(This->dataflow == eCapture)
1159 fd = open(This->devnode, O_RDONLY, 0);
1162 ERR("Unable to open device %s: %d (%s)\n", This->devnode, errno,
1164 return AUDCLNT_E_DEVICE_INVALIDATED;
1167 ret = setup_oss_device(fd, pwfx, outpwfx, TRUE);
1174 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1175 WAVEFORMATEX **pwfx)
1177 ACImpl *This = impl_from_IAudioClient(iface);
1178 WAVEFORMATEXTENSIBLE *fmt;
1181 TRACE("(%p)->(%p)\n", This, pwfx);
1187 if(This->dataflow == eRender)
1188 formats = This->ai.oformats;
1189 else if(This->dataflow == eCapture)
1190 formats = This->ai.iformats;
1192 return E_UNEXPECTED;
1194 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1196 return E_OUTOFMEMORY;
1198 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1199 if(formats & AFMT_S16_LE){
1200 fmt->Format.wBitsPerSample = 16;
1201 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1203 }else if(formats & AFMT_FLOAT){
1204 fmt->Format.wBitsPerSample = 32;
1205 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1207 }else if(formats & AFMT_U8){
1208 fmt->Format.wBitsPerSample = 8;
1209 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1210 }else if(formats & AFMT_S32_LE){
1211 fmt->Format.wBitsPerSample = 32;
1212 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1213 }else if(formats & AFMT_S24_LE){
1214 fmt->Format.wBitsPerSample = 24;
1215 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1217 ERR("Didn't recognize any available OSS formats: %x\n", formats);
1222 fmt->Format.nChannels = This->ai.max_channels;
1223 fmt->Format.nSamplesPerSec = This->ai.max_rate;
1224 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1226 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1227 fmt->Format.nChannels) / 8;
1228 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1229 fmt->Format.nBlockAlign;
1231 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1232 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1234 *pwfx = (WAVEFORMATEX*)fmt;
1240 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1241 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1243 ACImpl *This = impl_from_IAudioClient(iface);
1245 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1247 if(!defperiod && !minperiod)
1250 EnterCriticalSection(&This->lock);
1253 *defperiod = DefaultPeriod;
1255 *minperiod = MinimumPeriod;
1257 LeaveCriticalSection(&This->lock);
1262 static void oss_silence_buffer(ACImpl *This, BYTE *buf, UINT32 frames)
1264 if(This->fmt->wBitsPerSample == 8)
1265 memset(buf, 128, frames * This->fmt->nBlockAlign);
1267 memset(buf, 0, frames * This->fmt->nBlockAlign);
1270 static void oss_write_data(ACImpl *This)
1272 ssize_t written_bytes;
1273 UINT32 written_frames;
1274 size_t to_write_frames, to_write_bytes;
1277 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1279 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1280 to_write_frames = This->bufsize_frames - This->lcl_offs_frames;
1282 to_write_frames = This->held_frames;
1283 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1285 if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
1286 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
1290 if(bi.bytes < to_write_bytes){
1291 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1292 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1295 if(This->session->mute)
1296 oss_silence_buffer(This, buf, to_write_frames);
1298 written_bytes = write(This->fd, buf, to_write_bytes);
1299 if(written_bytes < 0){
1300 /* EAGAIN is OSS buffer full, log that too */
1301 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1304 written_frames = written_bytes / This->fmt->nBlockAlign;
1306 This->lcl_offs_frames += written_frames;
1307 This->lcl_offs_frames %= This->bufsize_frames;
1308 This->held_frames -= written_frames;
1309 This->inbuf_frames += written_frames;
1311 if(written_frames < to_write_frames){
1312 /* OSS buffer probably full */
1316 bi.bytes -= written_bytes;
1317 if(This->held_frames && bi.bytes >= This->fmt->nBlockAlign){
1318 /* wrapped and have some data back at the start to write */
1320 to_write_frames = bi.bytes / This->fmt->nBlockAlign;
1321 to_write_bytes = to_write_frames * This->fmt->nBlockAlign;
1323 if(This->session->mute)
1324 oss_silence_buffer(This, This->local_buffer, to_write_frames);
1326 written_bytes = write(This->fd, This->local_buffer, to_write_bytes);
1327 if(written_bytes < 0){
1328 WARN("write failed: %d (%s)\n", errno, strerror(errno));
1331 written_frames = written_bytes / This->fmt->nBlockAlign;
1333 This->lcl_offs_frames += written_frames;
1334 This->lcl_offs_frames %= This->bufsize_frames;
1335 This->held_frames -= written_frames;
1336 This->inbuf_frames += written_frames;
1340 static void oss_read_data(ACImpl *This)
1342 UINT64 pos, readable;
1346 if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1347 WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1351 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1352 readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1354 if(bi.bytes < readable)
1355 readable = bi.bytes;
1357 nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1360 WARN("read failed: %d (%s)\n", errno, strerror(errno));
1364 This->held_frames += nread / This->fmt->nBlockAlign;
1366 if(This->held_frames > This->bufsize_frames){
1367 WARN("Overflow of unread data\n");
1368 This->lcl_offs_frames += This->held_frames;
1369 This->lcl_offs_frames %= This->bufsize_frames;
1370 This->held_frames = This->bufsize_frames;
1374 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1376 ACImpl *This = user;
1378 EnterCriticalSection(&This->lock);
1380 if(This->dataflow == eRender && This->held_frames)
1381 oss_write_data(This);
1382 else if(This->dataflow == eCapture)
1383 oss_read_data(This);
1386 SetEvent(This->event);
1388 LeaveCriticalSection(&This->lock);
1391 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1393 ACImpl *This = impl_from_IAudioClient(iface);
1396 TRACE("(%p)\n", This);
1398 EnterCriticalSection(&This->lock);
1401 LeaveCriticalSection(&This->lock);
1402 return AUDCLNT_E_NOT_INITIALIZED;
1405 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1406 LeaveCriticalSection(&This->lock);
1407 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1411 LeaveCriticalSection(&This->lock);
1412 return AUDCLNT_E_NOT_STOPPED;
1415 if(This->dataflow == eRender)
1416 mask = PCM_ENABLE_OUTPUT;
1417 else if(This->dataflow == eCapture)
1418 mask = PCM_ENABLE_INPUT;
1420 LeaveCriticalSection(&This->lock);
1421 return E_UNEXPECTED;
1424 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1425 LeaveCriticalSection(&This->lock);
1426 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1430 if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1431 oss_period_callback, This, 0, This->period_us / 1000,
1432 WT_EXECUTEINTIMERTHREAD))
1433 ERR("Unable to create period timer: %u\n", GetLastError());
1435 This->playing = TRUE;
1437 LeaveCriticalSection(&This->lock);
1442 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1444 ACImpl *This = impl_from_IAudioClient(iface);
1447 TRACE("(%p)\n", This);
1449 EnterCriticalSection(&This->lock);
1452 LeaveCriticalSection(&This->lock);
1453 return AUDCLNT_E_NOT_INITIALIZED;
1457 LeaveCriticalSection(&This->lock);
1461 if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1462 DeleteTimerQueueTimer(g_timer_q, This->timer,
1463 INVALID_HANDLE_VALUE);
1467 if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1468 LeaveCriticalSection(&This->lock);
1469 WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1474 if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1475 LeaveCriticalSection(&This->lock);
1476 WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1480 This->playing = FALSE;
1482 LeaveCriticalSection(&This->lock);
1487 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1489 ACImpl *This = impl_from_IAudioClient(iface);
1491 TRACE("(%p)\n", This);
1493 EnterCriticalSection(&This->lock);
1496 LeaveCriticalSection(&This->lock);
1497 return AUDCLNT_E_NOT_INITIALIZED;
1501 LeaveCriticalSection(&This->lock);
1502 return AUDCLNT_E_NOT_STOPPED;
1505 if(This->buf_state != NOT_LOCKED){
1506 LeaveCriticalSection(&This->lock);
1507 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
1510 This->written_frames = 0;
1511 This->inbuf_frames = 0;
1512 This->held_frames = 0;
1514 if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1515 WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1517 LeaveCriticalSection(&This->lock);
1522 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1525 ACImpl *This = impl_from_IAudioClient(iface);
1527 TRACE("(%p)->(%p)\n", This, event);
1530 return E_INVALIDARG;
1532 EnterCriticalSection(&This->lock);
1535 LeaveCriticalSection(&This->lock);
1536 return AUDCLNT_E_NOT_INITIALIZED;
1539 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1540 LeaveCriticalSection(&This->lock);
1541 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1544 This->event = event;
1546 LeaveCriticalSection(&This->lock);
1551 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1554 ACImpl *This = impl_from_IAudioClient(iface);
1556 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1562 EnterCriticalSection(&This->lock);
1565 LeaveCriticalSection(&This->lock);
1566 return AUDCLNT_E_NOT_INITIALIZED;
1569 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1570 if(This->dataflow != eRender){
1571 LeaveCriticalSection(&This->lock);
1572 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1574 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
1575 *ppv = &This->IAudioRenderClient_iface;
1576 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1577 if(This->dataflow != eCapture){
1578 LeaveCriticalSection(&This->lock);
1579 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1581 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
1582 *ppv = &This->IAudioCaptureClient_iface;
1583 }else if(IsEqualIID(riid, &IID_IAudioClock)){
1584 IAudioClock_AddRef(&This->IAudioClock_iface);
1585 *ppv = &This->IAudioClock_iface;
1586 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1587 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
1588 *ppv = &This->IAudioStreamVolume_iface;
1589 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1590 if(!This->session_wrapper){
1591 This->session_wrapper = AudioSessionWrapper_Create(This);
1592 if(!This->session_wrapper){
1593 LeaveCriticalSection(&This->lock);
1594 return E_OUTOFMEMORY;
1597 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
1599 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1600 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1601 if(!This->session_wrapper){
1602 This->session_wrapper = AudioSessionWrapper_Create(This);
1603 if(!This->session_wrapper){
1604 LeaveCriticalSection(&This->lock);
1605 return E_OUTOFMEMORY;
1608 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
1610 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1611 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1612 if(!This->session_wrapper){
1613 This->session_wrapper = AudioSessionWrapper_Create(This);
1614 if(!This->session_wrapper){
1615 LeaveCriticalSection(&This->lock);
1616 return E_OUTOFMEMORY;
1619 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1621 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1625 LeaveCriticalSection(&This->lock);
1629 LeaveCriticalSection(&This->lock);
1631 FIXME("stub %s\n", debugstr_guid(riid));
1632 return E_NOINTERFACE;
1635 static const IAudioClientVtbl AudioClient_Vtbl =
1637 AudioClient_QueryInterface,
1639 AudioClient_Release,
1640 AudioClient_Initialize,
1641 AudioClient_GetBufferSize,
1642 AudioClient_GetStreamLatency,
1643 AudioClient_GetCurrentPadding,
1644 AudioClient_IsFormatSupported,
1645 AudioClient_GetMixFormat,
1646 AudioClient_GetDevicePeriod,
1650 AudioClient_SetEventHandle,
1651 AudioClient_GetService
1654 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1655 IAudioRenderClient *iface, REFIID riid, void **ppv)
1657 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1663 if(IsEqualIID(riid, &IID_IUnknown) ||
1664 IsEqualIID(riid, &IID_IAudioRenderClient))
1667 IUnknown_AddRef((IUnknown*)*ppv);
1671 WARN("Unknown interface %s\n", debugstr_guid(riid));
1672 return E_NOINTERFACE;
1675 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1677 ACImpl *This = impl_from_IAudioRenderClient(iface);
1678 return AudioClient_AddRef(&This->IAudioClient_iface);
1681 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1683 ACImpl *This = impl_from_IAudioRenderClient(iface);
1684 return AudioClient_Release(&This->IAudioClient_iface);
1687 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1688 UINT32 frames, BYTE **data)
1690 ACImpl *This = impl_from_IAudioRenderClient(iface);
1691 UINT32 pad, write_pos;
1694 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1699 EnterCriticalSection(&This->lock);
1701 if(This->buf_state != NOT_LOCKED){
1702 LeaveCriticalSection(&This->lock);
1703 return AUDCLNT_E_OUT_OF_ORDER;
1707 This->buf_state = LOCKED_NORMAL;
1708 LeaveCriticalSection(&This->lock);
1712 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1714 LeaveCriticalSection(&This->lock);
1718 if(pad + frames > This->bufsize_frames){
1719 LeaveCriticalSection(&This->lock);
1720 return AUDCLNT_E_BUFFER_TOO_LARGE;
1724 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1725 if(write_pos + frames > This->bufsize_frames){
1726 if(This->tmp_buffer_frames < frames){
1727 if(This->tmp_buffer)
1728 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1729 This->tmp_buffer, frames * This->fmt->nBlockAlign);
1731 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1732 frames * This->fmt->nBlockAlign);
1733 if(!This->tmp_buffer){
1734 LeaveCriticalSection(&This->lock);
1735 return E_OUTOFMEMORY;
1737 This->tmp_buffer_frames = frames;
1739 *data = This->tmp_buffer;
1740 This->buf_state = LOCKED_WRAPPED;
1742 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
1743 This->buf_state = LOCKED_NORMAL;
1746 LeaveCriticalSection(&This->lock);
1751 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
1753 UINT32 write_offs_frames =
1754 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1755 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1756 UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1757 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1758 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1760 if(written_bytes <= chunk_bytes){
1761 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1763 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1764 memcpy(This->local_buffer, buffer + chunk_bytes,
1765 written_bytes - chunk_bytes);
1769 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1770 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1772 ACImpl *This = impl_from_IAudioRenderClient(iface);
1775 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1777 EnterCriticalSection(&This->lock);
1779 if(This->buf_state == NOT_LOCKED || !written_frames){
1780 This->buf_state = NOT_LOCKED;
1781 LeaveCriticalSection(&This->lock);
1782 return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1785 if(This->buf_state == LOCKED_NORMAL)
1786 buffer = This->local_buffer + This->fmt->nBlockAlign *
1787 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
1789 buffer = This->tmp_buffer;
1791 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1792 oss_silence_buffer(This, buffer, written_frames);
1794 if(This->held_frames){
1795 if(This->buf_state == LOCKED_WRAPPED)
1796 oss_wrap_buffer(This, buffer, written_frames);
1798 This->held_frames += written_frames;
1803 if(This->session->mute)
1804 oss_silence_buffer(This, buffer, written_frames);
1806 w_bytes = write(This->fd, buffer,
1807 written_frames * This->fmt->nBlockAlign);
1809 if(errno != EAGAIN){
1810 This->buf_state = NOT_LOCKED;
1811 LeaveCriticalSection(&This->lock);
1812 ERR("write failed: %d (%s)\n", errno, strerror(errno));
1814 }else /* OSS buffer full */
1817 w_frames = w_bytes / This->fmt->nBlockAlign;
1818 This->inbuf_frames += w_frames;
1820 if(w_frames < written_frames){
1821 if(This->buf_state == LOCKED_WRAPPED)
1822 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1823 written_frames - w_frames);
1825 This->lcl_offs_frames += w_frames;
1826 This->held_frames = written_frames - w_frames;
1830 This->written_frames += written_frames;
1831 This->buf_state = NOT_LOCKED;
1833 LeaveCriticalSection(&This->lock);
1838 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1839 AudioRenderClient_QueryInterface,
1840 AudioRenderClient_AddRef,
1841 AudioRenderClient_Release,
1842 AudioRenderClient_GetBuffer,
1843 AudioRenderClient_ReleaseBuffer
1846 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1847 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1849 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1855 if(IsEqualIID(riid, &IID_IUnknown) ||
1856 IsEqualIID(riid, &IID_IAudioCaptureClient))
1859 IUnknown_AddRef((IUnknown*)*ppv);
1863 WARN("Unknown interface %s\n", debugstr_guid(riid));
1864 return E_NOINTERFACE;
1867 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1869 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1870 return IAudioClient_AddRef(&This->IAudioClient_iface);
1873 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1875 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1876 return IAudioClient_Release(&This->IAudioClient_iface);
1879 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1880 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1883 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1886 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1889 if(!data || !frames || !flags)
1892 EnterCriticalSection(&This->lock);
1894 if(This->buf_state != NOT_LOCKED){
1895 LeaveCriticalSection(&This->lock);
1896 return AUDCLNT_E_OUT_OF_ORDER;
1899 hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1901 LeaveCriticalSection(&This->lock);
1907 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1908 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1909 if(This->tmp_buffer_frames < *frames){
1910 if(This->tmp_buffer)
1911 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1912 This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1914 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1915 *frames * This->fmt->nBlockAlign);
1916 if(!This->tmp_buffer){
1917 LeaveCriticalSection(&This->lock);
1918 return E_OUTOFMEMORY;
1920 This->tmp_buffer_frames = *frames;
1923 *data = This->tmp_buffer;
1924 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1925 This->fmt->nBlockAlign;
1926 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1927 frames_bytes = *frames * This->fmt->nBlockAlign;
1928 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1929 memcpy(This->tmp_buffer, This->local_buffer,
1930 frames_bytes - chunk_bytes);
1932 *data = This->local_buffer +
1933 This->lcl_offs_frames * This->fmt->nBlockAlign;
1935 This->buf_state = LOCKED_NORMAL;
1937 if(devpos || qpcpos)
1938 IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1940 LeaveCriticalSection(&This->lock);
1942 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1945 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1946 IAudioCaptureClient *iface, UINT32 done)
1948 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1950 TRACE("(%p)->(%u)\n", This, done);
1952 EnterCriticalSection(&This->lock);
1954 if(This->buf_state == NOT_LOCKED){
1955 LeaveCriticalSection(&This->lock);
1956 return AUDCLNT_E_OUT_OF_ORDER;
1959 This->held_frames -= done;
1960 This->lcl_offs_frames += done;
1961 This->lcl_offs_frames %= This->bufsize_frames;
1963 This->buf_state = NOT_LOCKED;
1965 LeaveCriticalSection(&This->lock);
1970 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1971 IAudioCaptureClient *iface, UINT32 *frames)
1973 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1975 TRACE("(%p)->(%p)\n", This, frames);
1977 return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1980 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1982 AudioCaptureClient_QueryInterface,
1983 AudioCaptureClient_AddRef,
1984 AudioCaptureClient_Release,
1985 AudioCaptureClient_GetBuffer,
1986 AudioCaptureClient_ReleaseBuffer,
1987 AudioCaptureClient_GetNextPacketSize
1990 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1991 REFIID riid, void **ppv)
1993 ACImpl *This = impl_from_IAudioClock(iface);
1995 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2001 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2003 else if(IsEqualIID(riid, &IID_IAudioClock2))
2004 *ppv = &This->IAudioClock2_iface;
2006 IUnknown_AddRef((IUnknown*)*ppv);
2010 WARN("Unknown interface %s\n", debugstr_guid(riid));
2011 return E_NOINTERFACE;
2014 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2016 ACImpl *This = impl_from_IAudioClock(iface);
2017 return IAudioClient_AddRef(&This->IAudioClient_iface);
2020 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2022 ACImpl *This = impl_from_IAudioClock(iface);
2023 return IAudioClient_Release(&This->IAudioClient_iface);
2026 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2028 ACImpl *This = impl_from_IAudioClock(iface);
2030 TRACE("(%p)->(%p)\n", This, freq);
2032 *freq = This->fmt->nSamplesPerSec;
2037 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2040 ACImpl *This = impl_from_IAudioClock(iface);
2044 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2049 EnterCriticalSection(&This->lock);
2051 hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
2053 LeaveCriticalSection(&This->lock);
2057 if(This->dataflow == eRender)
2058 *pos = This->written_frames - pad;
2059 else if(This->dataflow == eCapture)
2060 *pos = This->written_frames + pad;
2062 LeaveCriticalSection(&This->lock);
2065 LARGE_INTEGER stamp, freq;
2066 QueryPerformanceCounter(&stamp);
2067 QueryPerformanceFrequency(&freq);
2068 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2074 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2077 ACImpl *This = impl_from_IAudioClock(iface);
2079 TRACE("(%p)->(%p)\n", This, chars);
2084 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2089 static const IAudioClockVtbl AudioClock_Vtbl =
2091 AudioClock_QueryInterface,
2094 AudioClock_GetFrequency,
2095 AudioClock_GetPosition,
2096 AudioClock_GetCharacteristics
2099 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2100 REFIID riid, void **ppv)
2102 ACImpl *This = impl_from_IAudioClock2(iface);
2103 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2106 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2108 ACImpl *This = impl_from_IAudioClock2(iface);
2109 return IAudioClient_AddRef(&This->IAudioClient_iface);
2112 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2114 ACImpl *This = impl_from_IAudioClock2(iface);
2115 return IAudioClient_Release(&This->IAudioClient_iface);
2118 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2119 UINT64 *pos, UINT64 *qpctime)
2121 ACImpl *This = impl_from_IAudioClock2(iface);
2123 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2128 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2130 AudioClock2_QueryInterface,
2132 AudioClock2_Release,
2133 AudioClock2_GetDevicePosition
2136 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2138 AudioSessionWrapper *ret;
2140 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2141 sizeof(AudioSessionWrapper));
2145 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2146 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2147 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2151 ret->client = client;
2153 ret->session = client->session;
2154 AudioClient_AddRef(&client->IAudioClient_iface);
2160 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2161 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2163 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2169 if(IsEqualIID(riid, &IID_IUnknown) ||
2170 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2171 IsEqualIID(riid, &IID_IAudioSessionControl2))
2174 IUnknown_AddRef((IUnknown*)*ppv);
2178 WARN("Unknown interface %s\n", debugstr_guid(riid));
2179 return E_NOINTERFACE;
2182 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2184 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2186 ref = InterlockedIncrement(&This->ref);
2187 TRACE("(%p) Refcount now %u\n", This, ref);
2191 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2193 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2195 ref = InterlockedDecrement(&This->ref);
2196 TRACE("(%p) Refcount now %u\n", This, ref);
2199 EnterCriticalSection(&This->client->lock);
2200 This->client->session_wrapper = NULL;
2201 LeaveCriticalSection(&This->client->lock);
2202 AudioClient_Release(&This->client->IAudioClient_iface);
2204 HeapFree(GetProcessHeap(), 0, This);
2209 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2210 AudioSessionState *state)
2212 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2215 TRACE("(%p)->(%p)\n", This, state);
2218 return NULL_PTR_ERR;
2220 EnterCriticalSection(&g_sessions_lock);
2222 if(list_empty(&This->session->clients)){
2223 *state = AudioSessionStateExpired;
2224 LeaveCriticalSection(&g_sessions_lock);
2228 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2229 EnterCriticalSection(&client->lock);
2230 if(client->playing){
2231 *state = AudioSessionStateActive;
2232 LeaveCriticalSection(&client->lock);
2233 LeaveCriticalSection(&g_sessions_lock);
2236 LeaveCriticalSection(&client->lock);
2239 LeaveCriticalSection(&g_sessions_lock);
2241 *state = AudioSessionStateInactive;
2246 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2247 IAudioSessionControl2 *iface, WCHAR **name)
2249 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2251 FIXME("(%p)->(%p) - stub\n", This, name);
2256 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2257 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2259 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2261 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2266 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2267 IAudioSessionControl2 *iface, WCHAR **path)
2269 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2271 FIXME("(%p)->(%p) - stub\n", This, path);
2276 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2277 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2279 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2281 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2286 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2287 IAudioSessionControl2 *iface, GUID *group)
2289 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2291 FIXME("(%p)->(%p) - stub\n", This, group);
2296 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2297 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2299 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2301 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2302 debugstr_guid(session));
2307 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2308 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2310 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2312 FIXME("(%p)->(%p) - stub\n", This, events);
2317 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2318 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2320 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2322 FIXME("(%p)->(%p) - stub\n", This, events);
2327 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2328 IAudioSessionControl2 *iface, WCHAR **id)
2330 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2332 FIXME("(%p)->(%p) - stub\n", This, id);
2337 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2338 IAudioSessionControl2 *iface, WCHAR **id)
2340 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2342 FIXME("(%p)->(%p) - stub\n", This, id);
2347 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2348 IAudioSessionControl2 *iface, DWORD *pid)
2350 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2352 TRACE("(%p)->(%p)\n", This, pid);
2357 *pid = GetCurrentProcessId();
2362 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2363 IAudioSessionControl2 *iface)
2365 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2367 TRACE("(%p)\n", This);
2372 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2373 IAudioSessionControl2 *iface, BOOL optout)
2375 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2377 TRACE("(%p)->(%d)\n", This, optout);
2382 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2384 AudioSessionControl_QueryInterface,
2385 AudioSessionControl_AddRef,
2386 AudioSessionControl_Release,
2387 AudioSessionControl_GetState,
2388 AudioSessionControl_GetDisplayName,
2389 AudioSessionControl_SetDisplayName,
2390 AudioSessionControl_GetIconPath,
2391 AudioSessionControl_SetIconPath,
2392 AudioSessionControl_GetGroupingParam,
2393 AudioSessionControl_SetGroupingParam,
2394 AudioSessionControl_RegisterAudioSessionNotification,
2395 AudioSessionControl_UnregisterAudioSessionNotification,
2396 AudioSessionControl_GetSessionIdentifier,
2397 AudioSessionControl_GetSessionInstanceIdentifier,
2398 AudioSessionControl_GetProcessId,
2399 AudioSessionControl_IsSystemSoundsSession,
2400 AudioSessionControl_SetDuckingPreference
2403 /* index == -1 means set all channels, otherwise sets only the given channel */
2404 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2411 if(index == (UINT32)-1){
2414 for(i = 0; i < This->fmt->nChannels; ++i){
2416 hr = oss_setvol(This, i);
2424 /* OSS doesn't support volume control past the first two channels */
2427 if(This->dataflow == eRender){
2428 setreq = SNDCTL_DSP_SETPLAYVOL;
2429 getreq = SNDCTL_DSP_GETPLAYVOL;
2430 }else if(This->dataflow == eCapture){
2431 setreq = SNDCTL_DSP_SETRECVOL;
2432 getreq = SNDCTL_DSP_GETRECVOL;
2434 return E_UNEXPECTED;
2436 if(ioctl(This->fd, getreq, &vol) < 0){
2438 /* device doesn't support this call */
2441 WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2445 level = This->session->master_vol * This->session->channel_vols[index] *
2449 vol = l | (vol & 0xFF00);
2451 vol = (vol & 0xFF) | (l << 8);
2453 if(ioctl(This->fd, setreq, &vol) < 0){
2455 /* device doesn't support this call */
2458 WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2465 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2470 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2472 hr = oss_setvol(client, index);
2480 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2481 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2483 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2489 if(IsEqualIID(riid, &IID_IUnknown) ||
2490 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2493 IUnknown_AddRef((IUnknown*)*ppv);
2497 WARN("Unknown interface %s\n", debugstr_guid(riid));
2498 return E_NOINTERFACE;
2501 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2503 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2504 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2507 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2509 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2510 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2513 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2514 ISimpleAudioVolume *iface, float level, const GUID *context)
2516 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2517 AudioSession *session = This->session;
2520 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2522 if(level < 0.f || level > 1.f)
2523 return E_INVALIDARG;
2526 FIXME("Notifications not supported yet\n");
2528 EnterCriticalSection(&session->lock);
2530 session->master_vol = level;
2532 ret = oss_session_setvol(session, -1);
2534 LeaveCriticalSection(&session->lock);
2539 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2540 ISimpleAudioVolume *iface, float *level)
2542 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2543 AudioSession *session = This->session;
2545 TRACE("(%p)->(%p)\n", session, level);
2548 return NULL_PTR_ERR;
2550 *level = session->master_vol;
2555 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2556 BOOL mute, const GUID *context)
2558 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2559 AudioSession *session = This->session;
2561 TRACE("(%p)->(%u, %p)\n", session, mute, context);
2563 EnterCriticalSection(&session->lock);
2565 if(!mute && session->mute){
2568 session->mute = mute;
2570 LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2571 EnterCriticalSection(&client->lock);
2572 if(ioctl(client->fd, SNDCTL_DSP_SKIP) < 0)
2573 WARN("Error calling DSP_SKIP: %d (%s)\n", errno,
2575 oss_write_data(client);
2576 LeaveCriticalSection(&client->lock);
2579 session->mute = mute;
2581 LeaveCriticalSection(&session->lock);
2586 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2589 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2590 AudioSession *session = This->session;
2592 TRACE("(%p)->(%p)\n", session, mute);
2595 return NULL_PTR_ERR;
2597 *mute = This->session->mute;
2602 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2604 SimpleAudioVolume_QueryInterface,
2605 SimpleAudioVolume_AddRef,
2606 SimpleAudioVolume_Release,
2607 SimpleAudioVolume_SetMasterVolume,
2608 SimpleAudioVolume_GetMasterVolume,
2609 SimpleAudioVolume_SetMute,
2610 SimpleAudioVolume_GetMute
2613 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2614 IAudioStreamVolume *iface, REFIID riid, void **ppv)
2616 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2622 if(IsEqualIID(riid, &IID_IUnknown) ||
2623 IsEqualIID(riid, &IID_IAudioStreamVolume))
2626 IUnknown_AddRef((IUnknown*)*ppv);
2630 WARN("Unknown interface %s\n", debugstr_guid(riid));
2631 return E_NOINTERFACE;
2634 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2636 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2637 return IAudioClient_AddRef(&This->IAudioClient_iface);
2640 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2642 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2643 return IAudioClient_Release(&This->IAudioClient_iface);
2646 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2647 IAudioStreamVolume *iface, UINT32 *out)
2649 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2651 TRACE("(%p)->(%p)\n", This, out);
2656 *out = This->fmt->nChannels;
2661 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2662 IAudioStreamVolume *iface, UINT32 index, float level)
2664 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2667 TRACE("(%p)->(%d, %f)\n", This, index, level);
2669 if(level < 0.f || level > 1.f)
2670 return E_INVALIDARG;
2672 if(index >= This->fmt->nChannels)
2673 return E_INVALIDARG;
2675 EnterCriticalSection(&This->lock);
2677 This->vols[index] = level;
2679 ret = oss_setvol(This, index);
2681 LeaveCriticalSection(&This->lock);
2686 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2687 IAudioStreamVolume *iface, UINT32 index, float *level)
2689 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2691 TRACE("(%p)->(%d, %p)\n", This, index, level);
2696 if(index >= This->fmt->nChannels)
2697 return E_INVALIDARG;
2699 *level = This->vols[index];
2704 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2705 IAudioStreamVolume *iface, UINT32 count, const float *levels)
2707 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2711 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2716 if(count != This->fmt->nChannels)
2717 return E_INVALIDARG;
2719 EnterCriticalSection(&This->lock);
2721 for(i = 0; i < count; ++i)
2722 This->vols[i] = levels[i];
2724 ret = oss_setvol(This, -1);
2726 LeaveCriticalSection(&This->lock);
2731 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2732 IAudioStreamVolume *iface, UINT32 count, float *levels)
2734 ACImpl *This = impl_from_IAudioStreamVolume(iface);
2737 TRACE("(%p)->(%d, %p)\n", This, count, levels);
2742 if(count != This->fmt->nChannels)
2743 return E_INVALIDARG;
2745 EnterCriticalSection(&This->lock);
2747 for(i = 0; i < count; ++i)
2748 levels[i] = This->vols[i];
2750 LeaveCriticalSection(&This->lock);
2755 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2757 AudioStreamVolume_QueryInterface,
2758 AudioStreamVolume_AddRef,
2759 AudioStreamVolume_Release,
2760 AudioStreamVolume_GetChannelCount,
2761 AudioStreamVolume_SetChannelVolume,
2762 AudioStreamVolume_GetChannelVolume,
2763 AudioStreamVolume_SetAllVolumes,
2764 AudioStreamVolume_GetAllVolumes
2767 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2768 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2770 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2776 if(IsEqualIID(riid, &IID_IUnknown) ||
2777 IsEqualIID(riid, &IID_IChannelAudioVolume))
2780 IUnknown_AddRef((IUnknown*)*ppv);
2784 WARN("Unknown interface %s\n", debugstr_guid(riid));
2785 return E_NOINTERFACE;
2788 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2790 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2791 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2794 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2796 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2797 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2800 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2801 IChannelAudioVolume *iface, UINT32 *out)
2803 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2804 AudioSession *session = This->session;
2806 TRACE("(%p)->(%p)\n", session, out);
2809 return NULL_PTR_ERR;
2811 *out = session->channel_count;
2816 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2817 IChannelAudioVolume *iface, UINT32 index, float level,
2818 const GUID *context)
2820 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2821 AudioSession *session = This->session;
2824 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2825 wine_dbgstr_guid(context));
2827 if(level < 0.f || level > 1.f)
2828 return E_INVALIDARG;
2830 if(index >= session->channel_count)
2831 return E_INVALIDARG;
2834 FIXME("Notifications not supported yet\n");
2836 EnterCriticalSection(&session->lock);
2838 session->channel_vols[index] = level;
2840 ret = oss_session_setvol(session, index);
2842 LeaveCriticalSection(&session->lock);
2847 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2848 IChannelAudioVolume *iface, UINT32 index, float *level)
2850 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2851 AudioSession *session = This->session;
2853 TRACE("(%p)->(%d, %p)\n", session, index, level);
2856 return NULL_PTR_ERR;
2858 if(index >= session->channel_count)
2859 return E_INVALIDARG;
2861 *level = session->channel_vols[index];
2866 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2867 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2868 const GUID *context)
2870 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2871 AudioSession *session = This->session;
2875 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2876 wine_dbgstr_guid(context));
2879 return NULL_PTR_ERR;
2881 if(count != session->channel_count)
2882 return E_INVALIDARG;
2885 FIXME("Notifications not supported yet\n");
2887 EnterCriticalSection(&session->lock);
2889 for(i = 0; i < count; ++i)
2890 session->channel_vols[i] = levels[i];
2892 ret = oss_session_setvol(session, -1);
2894 LeaveCriticalSection(&session->lock);
2899 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2900 IChannelAudioVolume *iface, UINT32 count, float *levels)
2902 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2903 AudioSession *session = This->session;
2906 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2909 return NULL_PTR_ERR;
2911 if(count != session->channel_count)
2912 return E_INVALIDARG;
2914 for(i = 0; i < count; ++i)
2915 levels[i] = session->channel_vols[i];
2920 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2922 ChannelAudioVolume_QueryInterface,
2923 ChannelAudioVolume_AddRef,
2924 ChannelAudioVolume_Release,
2925 ChannelAudioVolume_GetChannelCount,
2926 ChannelAudioVolume_SetChannelVolume,
2927 ChannelAudioVolume_GetChannelVolume,
2928 ChannelAudioVolume_SetAllVolumes,
2929 ChannelAudioVolume_GetAllVolumes
2932 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2933 REFIID riid, void **ppv)
2935 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2941 if(IsEqualIID(riid, &IID_IUnknown) ||
2942 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2943 IsEqualIID(riid, &IID_IAudioSessionManager2))
2946 IUnknown_AddRef((IUnknown*)*ppv);
2950 WARN("Unknown interface %s\n", debugstr_guid(riid));
2951 return E_NOINTERFACE;
2954 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2956 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2958 ref = InterlockedIncrement(&This->ref);
2959 TRACE("(%p) Refcount now %u\n", This, ref);
2963 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2965 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2967 ref = InterlockedDecrement(&This->ref);
2968 TRACE("(%p) Refcount now %u\n", This, ref);
2970 HeapFree(GetProcessHeap(), 0, This);
2974 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2975 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2976 IAudioSessionControl **out)
2978 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2979 AudioSession *session;
2980 AudioSessionWrapper *wrapper;
2983 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2986 hr = get_audio_session(session_guid, This->device, 0, &session);
2990 wrapper = AudioSessionWrapper_Create(NULL);
2992 return E_OUTOFMEMORY;
2994 wrapper->session = session;
2996 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3001 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3002 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3003 ISimpleAudioVolume **out)
3005 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3006 AudioSession *session;
3007 AudioSessionWrapper *wrapper;
3010 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3013 hr = get_audio_session(session_guid, This->device, 0, &session);
3017 wrapper = AudioSessionWrapper_Create(NULL);
3019 return E_OUTOFMEMORY;
3021 wrapper->session = session;
3023 *out = &wrapper->ISimpleAudioVolume_iface;
3028 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3029 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3031 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3032 FIXME("(%p)->(%p) - stub\n", This, out);
3036 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3037 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3039 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3040 FIXME("(%p)->(%p) - stub\n", This, notification);
3044 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3045 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3047 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3048 FIXME("(%p)->(%p) - stub\n", This, notification);
3052 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3053 IAudioSessionManager2 *iface, const WCHAR *session_id,
3054 IAudioVolumeDuckNotification *notification)
3056 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3057 FIXME("(%p)->(%p) - stub\n", This, notification);
3061 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3062 IAudioSessionManager2 *iface,
3063 IAudioVolumeDuckNotification *notification)
3065 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3066 FIXME("(%p)->(%p) - stub\n", This, notification);
3070 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3072 AudioSessionManager_QueryInterface,
3073 AudioSessionManager_AddRef,
3074 AudioSessionManager_Release,
3075 AudioSessionManager_GetAudioSessionControl,
3076 AudioSessionManager_GetSimpleAudioVolume,
3077 AudioSessionManager_GetSessionEnumerator,
3078 AudioSessionManager_RegisterSessionNotification,
3079 AudioSessionManager_UnregisterSessionNotification,
3080 AudioSessionManager_RegisterDuckNotification,
3081 AudioSessionManager_UnregisterDuckNotification
3084 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3085 IAudioSessionManager2 **out)
3089 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3091 return E_OUTOFMEMORY;
3093 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3094 This->device = device;
3097 *out = &This->IAudioSessionManager2_iface;