Commit 50140999 authored by Andrew Eikum's avatar Andrew Eikum Committed by Alexandre Julliard

mmdevapi: Reimplement using a driver system.

parent 30b8d046
MODULE = mmdevapi.dll
IMPORTS = uuid ole32 oleaut32 user32 advapi32
EXTRALIBS = @FRAMEWORK_OPENAL@
C_SRCS = \
audio.c \
audiovolume.c \
devenum.c \
main.c
......
/**
* OpenAL cross platform audio library
* Copyright (C) 2008 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#ifndef AL_ALEXT_H
#define AL_ALEXT_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef AL_LOKI_IMA_ADPCM_format
#define AL_LOKI_IMA_ADPCM_format 1
#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000
#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001
#endif
#ifndef AL_LOKI_WAVE_format
#define AL_LOKI_WAVE_format 1
#define AL_FORMAT_WAVE_EXT 0x10002
#endif
#ifndef AL_EXT_vorbis
#define AL_EXT_vorbis 1
#define AL_FORMAT_VORBIS_EXT 0x10003
#endif
#ifndef AL_LOKI_quadriphonic
#define AL_LOKI_quadriphonic 1
#define AL_FORMAT_QUAD8_LOKI 0x10004
#define AL_FORMAT_QUAD16_LOKI 0x10005
#endif
#ifndef AL_EXT_float32
#define AL_EXT_float32 1
#define AL_FORMAT_MONO_FLOAT32 0x10010
#define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif
#ifndef AL_EXT_double
#define AL_EXT_double 1
#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012
#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013
#endif
#ifndef ALC_LOKI_audio_channel
#define ALC_LOKI_audio_channel 1
#define ALC_CHAN_MAIN_LOKI 0x500001
#define ALC_CHAN_PCM_LOKI 0x500002
#define ALC_CHAN_CD_LOKI 0x500003
#endif
#ifndef ALC_ENUMERATE_ALL_EXT
#define ALC_ENUMERATE_ALL_EXT 1
#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
#endif
#ifndef AL_EXT_MCFORMATS
#define AL_EXT_MCFORMATS 1
#define AL_FORMAT_QUAD8 0x1204
#define AL_FORMAT_QUAD16 0x1205
#define AL_FORMAT_QUAD32 0x1206
#define AL_FORMAT_REAR8 0x1207
#define AL_FORMAT_REAR16 0x1208
#define AL_FORMAT_REAR32 0x1209
#define AL_FORMAT_51CHN8 0x120A
#define AL_FORMAT_51CHN16 0x120B
#define AL_FORMAT_51CHN32 0x120C
#define AL_FORMAT_61CHN8 0x120D
#define AL_FORMAT_61CHN16 0x120E
#define AL_FORMAT_61CHN32 0x120F
#define AL_FORMAT_71CHN8 0x1210
#define AL_FORMAT_71CHN16 0x1211
#define AL_FORMAT_71CHN32 0x1212
#endif
#ifndef AL_EXT_MULAW_MCFORMATS
#define AL_EXT_MULAW_MCFORMATS 1
#define AL_FORMAT_MONO_MULAW 0x10014
#define AL_FORMAT_STEREO_MULAW 0x10015
#define AL_FORMAT_QUAD_MULAW 0x10021
#define AL_FORMAT_REAR_MULAW 0x10022
#define AL_FORMAT_51CHN_MULAW 0x10023
#define AL_FORMAT_61CHN_MULAW 0x10024
#define AL_FORMAT_71CHN_MULAW 0x10025
#endif
#ifndef AL_EXT_IMA4
#define AL_EXT_IMA4 1
#define AL_FORMAT_MONO_IMA4 0x1300
#define AL_FORMAT_STEREO_IMA4 0x1301
#endif
#ifndef AL_EXT_STATIC_BUFFER
#define AL_EXT_STATIC_BUFFER 1
typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);
#ifdef AL_ALEXT_PROTOTYPES
AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);
#endif
#endif
#ifndef ALC_EXT_disconnect
#define ALC_EXT_disconnect 1
#define ALC_CONNECTED 0x313
#endif
#ifndef ALC_EXT_thread_local_context
#define ALC_EXT_thread_local_context 1
typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);
typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
#endif
#endif
#ifndef AL_EXT_source_distance_model
#define AL_EXT_source_distance_model 1
#define AL_SOURCE_DISTANCE_MODEL 0x200
#endif
#ifdef __cplusplus
}
#endif
#endif
/*
* Copyright 2010 Maarten Lankhorst for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define NONAMELESSUNION
#define COBJMACROS
#include "config.h"
#include <stdarg.h>
#ifdef HAVE_AL_AL_H
#include <AL/al.h>
#include <AL/alc.h>
#elif defined(HAVE_OPENAL_AL_H)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winreg.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "ole2.h"
#include "mmdeviceapi.h"
#include "dshow.h"
#include "dsound.h"
#include "audioclient.h"
#include "endpointvolume.h"
#include "audiopolicy.h"
#include "mmdevapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
#ifdef HAVE_OPENAL
typedef struct ACRender ACRender;
typedef struct ACCapture ACCapture;
typedef struct ACSession ACSession;
typedef struct ASVolume ASVolume;
typedef struct AClock AClock;
typedef struct ACImpl {
const IAudioClientVtbl *lpVtbl;
LONG ref;
MMDevice *parent;
BOOL init, running;
CRITICAL_SECTION *crst;
HANDLE handle;
DWORD locked, flags, candisconnect;
DWORD bufsize_frames, ofs_frames, periodsize_frames, pad, padpartial;
BYTE *buffer;
WAVEFORMATEX *pwfx;
ALuint source;
INT64 frameswritten;
REFERENCE_TIME laststamp;
HANDLE timer_id;
ALCdevice *dev;
ALint format;
ACRender *render;
ACCapture *capture;
ACSession *session;
ASVolume *svolume;
AClock *clock;
} ACImpl;
struct ACRender {
const IAudioRenderClientVtbl *lpVtbl;
LONG ref;
ACImpl *parent;
};
struct ACCapture {
const IAudioCaptureClientVtbl *lpVtbl;
LONG ref;
ACImpl *parent;
};
struct ACSession {
const IAudioSessionControl2Vtbl *lpVtbl;
LONG ref;
ACImpl *parent;
};
struct ASVolume {
const ISimpleAudioVolumeVtbl *lpVtbl;
LONG ref;
ACImpl *parent;
};
struct AClock {
const IAudioClockVtbl *lpVtbl;
const IAudioClock2Vtbl *lp2Vtbl;
LONG ref;
ACImpl *parent;
};
static const IAudioClientVtbl ACImpl_Vtbl;
static const IAudioRenderClientVtbl ACRender_Vtbl;
static const IAudioCaptureClientVtbl ACCapture_Vtbl;
static const IAudioSessionControl2Vtbl ACSession_Vtbl;
static const ISimpleAudioVolumeVtbl ASVolume_Vtbl;
static const IAudioClockVtbl AClock_Vtbl;
static const IAudioClock2Vtbl AClock2_Vtbl;
static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv);
static void AudioRenderClient_Destroy(ACRender *This);
static HRESULT AudioCaptureClient_Create(ACImpl *parent, ACCapture **ppv);
static void AudioCaptureClient_Destroy(ACCapture *This);
static HRESULT AudioSessionControl_Create(ACImpl *parent, ACSession **ppv);
static void AudioSessionControl_Destroy(ACSession *This);
static HRESULT AudioSimpleVolume_Create(ACImpl *parent, ASVolume **ppv);
static void AudioSimpleVolume_Destroy(ASVolume *This);
static HRESULT AudioClock_Create(ACImpl *parent, AClock **ppv);
static void AudioClock_Destroy(AClock *This);
static int valid_dev(ACImpl *This)
{
if (!This->dev)
return 0;
if (This->parent->flow == eRender && This->dev != This->parent->device)
return 0;
return 1;
}
static int get_format_PCM(WAVEFORMATEX *format)
{
if (format->nChannels > 2) {
FIXME("nChannels > 2 not documented for WAVE_FORMAT_PCM!\n");
return 0;
}
format->cbSize = 0;
if (format->nBlockAlign != format->wBitsPerSample/8*format->nChannels) {
WARN("Invalid nBlockAlign %u, from %u %u\n",
format->nBlockAlign, format->wBitsPerSample, format->nChannels);
return 0;
}
switch (format->wBitsPerSample) {
case 8: {
switch (format->nChannels) {
case 1: return AL_FORMAT_MONO8;
case 2: return AL_FORMAT_STEREO8;
}
}
case 16: {
switch (format->nChannels) {
case 1: return AL_FORMAT_MONO16;
case 2: return AL_FORMAT_STEREO16;
}
}
}
if (!(format->wBitsPerSample % 8))
WARN("Could not get OpenAL format (%d-bit, %d channels)\n",
format->wBitsPerSample, format->nChannels);
return 0;
}
/* Speaker configs */
#define MONO SPEAKER_FRONT_CENTER
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
#define REAR (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
static int get_format_EXT(WAVEFORMATEX *format)
{
WAVEFORMATEXTENSIBLE *wfe;
if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)) {
WARN("Invalid cbSize specified for WAVE_FORMAT_EXTENSIBLE (%d)\n", format->cbSize);
return 0;
}
wfe = (WAVEFORMATEXTENSIBLE*)format;
wfe->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
if (wfe->Samples.wValidBitsPerSample &&
wfe->Samples.wValidBitsPerSample != format->wBitsPerSample) {
FIXME("wValidBitsPerSample(%u) != wBitsPerSample(%u) unsupported\n",
wfe->Samples.wValidBitsPerSample, format->wBitsPerSample);
return 0;
}
TRACE("Extensible values:\n"
" Samples = %d\n"
" ChannelMask = 0x%08x\n"
" SubFormat = %s\n",
wfe->Samples.wReserved, wfe->dwChannelMask,
debugstr_guid(&wfe->SubFormat));
if (wfe->dwChannelMask != MONO
&& wfe->dwChannelMask != STEREO
&& !palIsExtensionPresent("AL_EXT_MCFORMATS")) {
/* QUAD PCM might still work, special case */
if (palIsExtensionPresent("AL_LOKI_quadriphonic")
&& IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)
&& wfe->dwChannelMask == QUAD) {
if (format->wBitsPerSample == 16)
return AL_FORMAT_QUAD16_LOKI;
else if (format->wBitsPerSample == 8)
return AL_FORMAT_QUAD8_LOKI;
}
WARN("Not all formats available\n");
return 0;
}
if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
if (format->wBitsPerSample == 8) {
switch (wfe->dwChannelMask) {
case MONO: return AL_FORMAT_MONO8;
case STEREO: return AL_FORMAT_STEREO8;
case REAR: return AL_FORMAT_REAR8;
case QUAD: return AL_FORMAT_QUAD8;
case X5DOT1: return AL_FORMAT_51CHN8;
case X6DOT1: return AL_FORMAT_61CHN8;
case X7DOT1: return AL_FORMAT_71CHN8;
default: break;
}
} else if (format->wBitsPerSample == 16) {
switch (wfe->dwChannelMask) {
case MONO: return AL_FORMAT_MONO16;
case STEREO: return AL_FORMAT_STEREO16;
case REAR: return AL_FORMAT_REAR16;
case QUAD: return AL_FORMAT_QUAD16;
case X5DOT1: return AL_FORMAT_51CHN16;
case X6DOT1: return AL_FORMAT_61CHN16;
case X7DOT1: return AL_FORMAT_71CHN16;
default: break;
}
}
else if (!(format->wBitsPerSample % 8))
ERR("Could not get OpenAL PCM format (%d-bit, mask 0x%08x)\n",
format->wBitsPerSample, wfe->dwChannelMask);
return 0;
}
else if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
if (format->wBitsPerSample != 32) {
WARN("Invalid valid bits %u/32\n", format->wBitsPerSample);
return 0;
}
switch (wfe->dwChannelMask) {
case MONO: return AL_FORMAT_MONO_FLOAT32;
case STEREO: return AL_FORMAT_STEREO_FLOAT32;
case REAR: return AL_FORMAT_REAR32;
case QUAD: return AL_FORMAT_QUAD32;
case X5DOT1: return AL_FORMAT_51CHN32;
case X6DOT1: return AL_FORMAT_61CHN32;
case X7DOT1: return AL_FORMAT_71CHN32;
default:
ERR("Could not get OpenAL float format (%d-bit, mask 0x%08x)\n",
format->wBitsPerSample, wfe->dwChannelMask);
return 0;
}
}
else if (!IsEqualGUID(&wfe->SubFormat, &GUID_NULL))
ERR("Unhandled extensible format: %s\n", debugstr_guid(&wfe->SubFormat));
return 0;
}
static ALint get_format(WAVEFORMATEX *in)
{
int ret = 0;
if (in->wFormatTag == WAVE_FORMAT_PCM)
ret = get_format_PCM(in);
else if (in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
ret = get_format_EXT(in);
return ret;
}
static REFERENCE_TIME gettime(void) {
LARGE_INTEGER stamp, freq;
QueryPerformanceCounter(&stamp);
QueryPerformanceFrequency(&freq);
return (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
}
HRESULT AudioClient_Create(MMDevice *parent, IAudioClient **ppv)
{
ACImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
*ppv = (IAudioClient*)This;
if (!*ppv)
return E_OUTOFMEMORY;
This->crst = &parent->crst;
This->lpVtbl = &ACImpl_Vtbl;
This->ref = 1;
This->parent = parent;
return S_OK;
}
static void AudioClient_Destroy(ACImpl *This)
{
if (This->timer_id)
{
DeleteTimerQueueTimer(NULL, This->timer_id, INVALID_HANDLE_VALUE);
This->timer_id = 0;
}
if (This->render)
AudioRenderClient_Destroy(This->render);
if (This->capture)
AudioCaptureClient_Destroy(This->capture);
if (This->session)
AudioSessionControl_Destroy(This->session);
if (This->svolume)
AudioSimpleVolume_Destroy(This->svolume);
if (This->clock)
AudioClock_Destroy(This->clock);
if (!valid_dev(This))
TRACE("Not destroying device since none exists\n");
else if (This->parent->flow == eRender) {
setALContext(This->parent->ctx);
IAudioClient_Stop((IAudioClient*)This);
IAudioClient_Reset((IAudioClient*)This);
palDeleteSources(1, &This->source);
getALError();
popALContext();
}
else if (This->parent->flow == eCapture)
palcCaptureCloseDevice(This->dev);
HeapFree(GetProcessHeap(), 0, This->pwfx);
HeapFree(GetProcessHeap(), 0, This->buffer);
HeapFree(GetProcessHeap(), 0, This);
}
static void CALLBACK AC_tick(void *data, BOOLEAN fired)
{
ACImpl *This = data;
DWORD pad;
EnterCriticalSection(This->crst);
if (This->running)
IAudioClient_GetCurrentPadding((IAudioClient*)This, &pad);
LeaveCriticalSection(This->crst);
}
/* Open device and set/update internal mixing format based on information
* openal provides us. if the device cannot be opened, assume 48khz
* Guessing the frequency is harmless, since if GetMixFormat fails to open
* the device, then Initialize will likely fail as well
*/
static HRESULT AC_OpenRenderAL(ACImpl *This)
{
char alname[MAX_PATH];
MMDevice *cur = This->parent;
alname[sizeof(alname)-1] = 0;
if (cur->device)
return cur->ctx ? S_OK : AUDCLNT_E_SERVICE_NOT_RUNNING;
WideCharToMultiByte(CP_UNIXCP, 0, cur->alname, -1,
alname, sizeof(alname)/sizeof(*alname)-1, NULL, NULL);
cur->device = palcOpenDevice(alname);
if (!cur->device) {
ALCenum err = palcGetError(NULL);
WARN("Could not open device %s: 0x%04x\n", alname, err);
return AUDCLNT_E_DEVICE_IN_USE;
}
cur->ctx = palcCreateContext(cur->device, NULL);
if (!cur->ctx) {
ALCenum err = palcGetError(cur->device);
ERR("Could not create context: 0x%04x\n", err);
return AUDCLNT_E_SERVICE_NOT_RUNNING;
}
if (!cur->device)
return AUDCLNT_E_DEVICE_IN_USE;
return S_OK;
}
static HRESULT AC_OpenCaptureAL(ACImpl *This)
{
char alname[MAX_PATH];
ALint freq, size_frames;
freq = This->pwfx->nSamplesPerSec;
size_frames = This->bufsize_frames;
alname[sizeof(alname)-1] = 0;
if (This->dev) {
FIXME("Attempting to open device while already open\n");
return S_OK;
}
WideCharToMultiByte(CP_UNIXCP, 0, This->parent->alname, -1,
alname, sizeof(alname)/sizeof(*alname)-1, NULL, NULL);
This->dev = palcCaptureOpenDevice(alname, freq, This->format, size_frames);
if (!This->dev) {
ALCenum err = palcGetError(NULL);
FIXME("Could not open device %s with buf size %u: 0x%04x\n",
alname, This->bufsize_frames, err);
return AUDCLNT_E_DEVICE_IN_USE;
}
return S_OK;
}
static HRESULT WINAPI AC_QueryInterface(IAudioClient *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IAudioClient))
*ppv = iface;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI AC_AddRef(IAudioClient *iface)
{
ACImpl *This = (ACImpl*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI AC_Release(IAudioClient *iface)
{
ACImpl *This = (ACImpl*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioClient_Destroy(This);
return ref;
}
static HRESULT WINAPI AC_Initialize(IAudioClient *iface, AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period, const WAVEFORMATEX *pwfx, const GUID *sessionguid)
{
ACImpl *This = (ACImpl*)iface;
HRESULT hr = S_OK;
WAVEFORMATEX *pwfx2;
REFERENCE_TIME time;
DWORD bufsize_bytes;
TRACE("(%p)->(%x,%x,%u,%u,%p,%s)\n", This, mode, flags, (int)duration, (int)period, pwfx, debugstr_guid(sessionguid));
if (This->init)
return AUDCLNT_E_ALREADY_INITIALIZED;
if (mode != AUDCLNT_SHAREMODE_SHARED
&& mode != AUDCLNT_SHAREMODE_EXCLUSIVE) {
WARN("Unknown mode %x\n", mode);
return AUDCLNT_E_NOT_INITIALIZED;
}
if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|AUDCLNT_STREAMFLAGS_LOOPBACK
|AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|AUDCLNT_STREAMFLAGS_NOPERSIST
|AUDCLNT_STREAMFLAGS_RATEADJUST
|AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
WARN("Unknown flags 0x%08x\n", flags);
return E_INVALIDARG;
}
if (flags)
WARN("Flags 0x%08x ignored\n", flags);
if (!pwfx)
return E_POINTER;
if (sessionguid)
WARN("Session guid %s ignored\n", debugstr_guid(sessionguid));
hr = IAudioClient_IsFormatSupported(iface, mode, pwfx, &pwfx2);
CoTaskMemFree(pwfx2);
if (FAILED(hr) || pwfx2) {
WARN("Format not supported, or had to be modified!\n");
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
EnterCriticalSection(This->crst);
HeapFree(GetProcessHeap(), 0, This->pwfx);
This->pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(*pwfx) + pwfx->cbSize);
if (!This->pwfx) {
hr = E_OUTOFMEMORY;
goto out;
}
memcpy(This->pwfx, pwfx, sizeof(*pwfx) + pwfx->cbSize);
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE *)This->pwfx;
switch (pwfx->nChannels) {
case 1: wfe->dwChannelMask = MONO; break;
case 2: wfe->dwChannelMask = STEREO; break;
case 4: wfe->dwChannelMask = QUAD; break;
case 6: wfe->dwChannelMask = X5DOT1; break;
case 7: wfe->dwChannelMask = X6DOT1; break;
case 8: wfe->dwChannelMask = X7DOT1; break;
default:
ERR("How did we end up with %i channels?\n", pwfx->nChannels);
hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
goto out;
}
}
hr = IAudioClient_GetDevicePeriod(iface, &time, NULL);
if (FAILED(hr))
goto out;
This->periodsize_frames = (DWORD64)This->pwfx->nSamplesPerSec * time / (DWORD64)10000000;
if (duration > 20000000)
duration = 20000000;
This->bufsize_frames = duration / time * This->periodsize_frames;
if (duration % time)
This->bufsize_frames += This->periodsize_frames;
bufsize_bytes = This->bufsize_frames * pwfx->nBlockAlign;
This->format = get_format(This->pwfx);
if (This->parent->flow == eRender) {
char silence[32];
ALuint buf = 0, towrite;
hr = AC_OpenRenderAL(This);
This->dev = This->parent->device;
if (FAILED(hr))
goto out;
/* Test the returned format */
towrite = sizeof(silence);
towrite -= towrite % This->pwfx->nBlockAlign;
if (This->pwfx->wBitsPerSample != 8)
memset(silence, 0, sizeof(silence));
else
memset(silence, 128, sizeof(silence));
setALContext(This->parent->ctx);
getALError();
palGenBuffers(1, &buf);
palBufferData(buf, This->format, silence, towrite, This->pwfx->nSamplesPerSec);
palDeleteBuffers(1, &buf);
if (palGetError())
hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
else if (!This->source) {
palGenSources(1, &This->source);
palSourcei(This->source, AL_LOOPING, AL_FALSE);
getALError();
}
popALContext();
}
else
hr = AC_OpenCaptureAL(This);
if (FAILED(hr))
goto out;
This->candisconnect = palcIsExtensionPresent(This->dev, "ALC_EXT_disconnect");
This->buffer = HeapAlloc(GetProcessHeap(), 0, bufsize_bytes);
if (!This->buffer) {
hr = E_OUTOFMEMORY;
goto out;
}
This->flags = flags;
This->handle = NULL;
This->running = FALSE;
This->init = TRUE;
out:
LeaveCriticalSection(This->crst);
return hr;
}
static HRESULT WINAPI AC_GetBufferSize(IAudioClient *iface, UINT32 *frames)
{
ACImpl *This = (ACImpl*)iface;
TRACE("(%p)->(%p)\n", This, frames);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!frames)
return E_POINTER;
*frames = This->bufsize_frames;
return S_OK;
}
static HRESULT WINAPI AC_GetStreamLatency(IAudioClient *iface, REFERENCE_TIME *latency)
{
ACImpl *This = (ACImpl*)iface;
TRACE("(%p)->(%p)\n", This, latency);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!latency)
return E_POINTER;
*latency = 50000;
return S_OK;
}
static int disconnected(ACImpl *This)
{
if (!This->candisconnect)
return 0;
if (This->parent->flow == eRender) {
if (This->parent->device) {
ALCint con = 1;
palcGetIntegerv(This->parent->device, ALC_CONNECTED, 1, &con);
palcGetError(This->parent->device);
if (!con) {
palcCloseDevice(This->parent->device);
This->parent->device = NULL;
This->parent->ctx = NULL;
This->dev = NULL;
}
}
if (!This->parent->device && FAILED(AC_OpenRenderAL(This))) {
This->pad -= This->padpartial;
This->padpartial = 0;
return 1;
}
if (This->parent->device != This->dev) {
WARN("Emptying buffer after newly reconnected!\n");
This->pad -= This->padpartial;
This->padpartial = 0;
This->dev = This->parent->device;
setALContext(This->parent->ctx);
palGenSources(1, &This->source);
palSourcei(This->source, AL_LOOPING, AL_FALSE);
getALError();
if (This->render && !This->locked && This->pad) {
UINT pad = This->pad;
BYTE *data;
This->pad = 0;
/* Probably will cause sound glitches, who cares? */
IAudioRenderClient_GetBuffer((IAudioRenderClient *)This->render, pad, &data);
IAudioRenderClient_ReleaseBuffer((IAudioRenderClient *)This->render, pad, 0);
}
popALContext();
}
} else {
if (This->dev) {
ALCint con = 1;
palcGetIntegerv(This->dev, ALC_CONNECTED, 1, &con);
palcGetError(This->dev);
if (!con) {
palcCaptureCloseDevice(This->dev);
This->dev = NULL;
}
}
if (!This->dev) {
if (FAILED(AC_OpenCaptureAL(This)))
return 1;
WARN("Emptying buffer after newly reconnected!\n");
This->pad = This->ofs_frames = 0;
if (This->running)
palcCaptureStart(This->dev);
}
}
return 0;
}
static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad)
{
ACImpl *This = (ACImpl*)iface;
ALint avail = 0;
TRACE("(%p)->(%p)\n", This, numpad);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!numpad)
return E_POINTER;
EnterCriticalSection(This->crst);
if (disconnected(This)) {
REFERENCE_TIME time = gettime(), period;
WARN("No device found, faking increment\n");
IAudioClient_GetDevicePeriod(iface, &period, NULL);
while (This->running && time - This->laststamp >= period) {
This->laststamp += period;
if (This->parent->flow == eCapture) {
This->pad += This->periodsize_frames;
if (This->pad > This->bufsize_frames)
This->pad = This->bufsize_frames;
} else {
if (This->pad <= This->periodsize_frames) {
This->pad = 0;
break;
} else
This->pad -= This->periodsize_frames;
}
}
if (This->parent->flow == eCapture)
*numpad = This->pad >= This->periodsize_frames ? This->periodsize_frames: 0;
else
*numpad = This->pad;
} else if (This->parent->flow == eRender) {
UINT64 played = 0;
ALint state, padpart;
setALContext(This->parent->ctx);
palGetSourcei(This->source, AL_BYTE_OFFSET, &padpart);
palGetSourcei(This->source, AL_SOURCE_STATE, &state);
padpart /= This->pwfx->nBlockAlign;
if (state == AL_STOPPED && This->running)
padpart = This->pad;
if (This->running && This->padpartial != padpart) {
This->padpartial = padpart;
This->laststamp = gettime();
#if 0 /* Manipulative lie */
} else if (This->running) {
ALint size = This->pad - padpart;
if (size > This->periodsize_frames)
size = This->periodsize_frames;
played = (gettime() - This->laststamp)*8;
played = played * This->pwfx->nSamplesPerSec / 10000000;
if (played > size)
played = size;
#endif
}
*numpad = This->pad - This->padpartial - played;
if (This->handle && (*numpad + This->periodsize_frames) <= This->bufsize_frames)
SetEvent(This->handle);
getALError();
popALContext();
} else {
DWORD block = This->pwfx->nBlockAlign;
palcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
if (avail) {
DWORD ofs_frames = This->ofs_frames + This->pad;
BYTE *buf1;
ofs_frames %= This->bufsize_frames;
buf1 = This->buffer + (ofs_frames * block);
This->laststamp = gettime();
if (This->handle)
SetEvent(This->handle);
if (ofs_frames + avail <= This->bufsize_frames)
palcCaptureSamples(This->dev, buf1, avail);
else {
DWORD part1 = This->bufsize_frames - ofs_frames;
palcCaptureSamples(This->dev, buf1, part1);
palcCaptureSamples(This->dev, This->buffer, avail - part1);
}
This->pad += avail;
This->frameswritten += avail;
/* Increase ofs if the app forgets to read */
if (This->pad > This->bufsize_frames) {
DWORD rest;
WARN("Overflowed! %u frames\n", This->pad - This->bufsize_frames);
This->ofs_frames += This->pad - This->bufsize_frames;
rest = This->ofs_frames % This->periodsize_frames;
if (rest)
This->ofs_frames += This->periodsize_frames - rest;
This->ofs_frames %= This->bufsize_frames;
This->pad = This->bufsize_frames;
}
}
if (This->pad >= This->periodsize_frames)
*numpad = This->periodsize_frames;
else
*numpad = 0;
}
LeaveCriticalSection(This->crst);
TRACE("%u queued\n", *numpad);
return S_OK;
}
static HRESULT WINAPI AC_IsFormatSupported(IAudioClient *iface, AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx, WAVEFORMATEX **outpwfx)
{
ACImpl *This = (ACImpl*)iface;
WAVEFORMATEX *tmp;
DWORD mask;
DWORD size;
TRACE("(%p)->(%x,%p,%p)\n", This, mode, pwfx, outpwfx);
if (!pwfx)
return E_POINTER;
if (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx)
return E_POINTER;
if (mode != AUDCLNT_SHAREMODE_SHARED
&& mode != AUDCLNT_SHAREMODE_EXCLUSIVE) {
WARN("Unknown mode %x\n", mode);
return E_INVALIDARG;
}
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
size = sizeof(WAVEFORMATEXTENSIBLE);
else if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
size = sizeof(WAVEFORMATEX);
else
return AUDCLNT_E_UNSUPPORTED_FORMAT;
if (pwfx->nSamplesPerSec < 8000
|| pwfx->nSamplesPerSec > 192000)
return AUDCLNT_E_UNSUPPORTED_FORMAT;
if (pwfx->wFormatTag != WAVE_FORMAT_EXTENSIBLE
|| !IsEqualIID(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
if (pwfx->wBitsPerSample > 16)
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
switch (pwfx->nChannels) {
case 1: mask = MONO; break;
case 2: mask = STEREO; break;
case 4: mask = QUAD; break;
case 6: mask = X5DOT1; break;
case 7: mask = X6DOT1; break;
case 8: mask = X7DOT1; break;
default:
TRACE("Unsupported channel count %i\n", pwfx->nChannels);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
tmp = CoTaskMemAlloc(size);
if (outpwfx)
*outpwfx = tmp;
if (!tmp)
return E_OUTOFMEMORY;
memcpy(tmp, pwfx, size);
tmp->nBlockAlign = tmp->nChannels * tmp->wBitsPerSample / 8;
tmp->nAvgBytesPerSec = tmp->nBlockAlign * tmp->nSamplesPerSec;
tmp->cbSize = size - sizeof(WAVEFORMATEX);
if (tmp->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *ex = (WAVEFORMATEXTENSIBLE*)tmp;
if (ex->Samples.wValidBitsPerSample)
ex->Samples.wValidBitsPerSample = ex->Format.wBitsPerSample;
/* Rear is a special allowed case */
if (ex->dwChannelMask
&& !(ex->Format.nChannels == 2 && ex->dwChannelMask == REAR))
ex->dwChannelMask = mask;
}
if (memcmp(pwfx, tmp, size)) {
if (outpwfx)
return S_FALSE;
CoTaskMemFree(tmp);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
if (outpwfx)
*outpwfx = NULL;
CoTaskMemFree(tmp);
return S_OK;
}
static HRESULT WINAPI AC_GetMixFormat(IAudioClient *iface, WAVEFORMATEX **pwfx)
{
ACImpl *This = (ACImpl*)iface;
PROPVARIANT pv = { VT_EMPTY };
HRESULT hr = S_OK;
TRACE("(%p)->(%p)\n", This, pwfx);
if (!pwfx)
return E_POINTER;
hr = MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow,
&PKEY_AudioEngine_DeviceFormat, &pv);
*pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
if (SUCCEEDED(hr) && pv.vt == VT_EMPTY)
return E_FAIL;
TRACE("Returning 0x%08x\n", hr);
return hr;
}
static HRESULT WINAPI AC_GetDevicePeriod(IAudioClient *iface, REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
{
ACImpl *This = (ACImpl*)iface;
TRACE("(%p)->(%p)\n", This, minperiod);
if (!defperiod && !minperiod)
return E_POINTER;
if (minperiod)
*minperiod = 30000;
if (defperiod)
*defperiod = 200000;
return S_OK;
}
static HRESULT WINAPI AC_Start(IAudioClient *iface)
{
ACImpl *This = (ACImpl*)iface;
HRESULT hr;
REFERENCE_TIME refresh;
TRACE("(%p)\n", This);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) {
if (!This->handle)
return AUDCLNT_E_EVENTHANDLE_NOT_SET;
FIXME("Event handles not fully tested\n");
}
EnterCriticalSection(This->crst);
if (This->running) {
hr = AUDCLNT_E_NOT_STOPPED;
goto out;
}
if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
setALContext(This->parent->ctx);
palSourcePlay(This->source);
getALError();
popALContext();
}
else
palcCaptureStart(This->dev);
AC_GetDevicePeriod(iface, &refresh, NULL);
if (!This->timer_id && This->handle)
CreateTimerQueueTimer(&This->timer_id, NULL, AC_tick, This,
refresh / 20000, refresh / 20000,
WT_EXECUTEINTIMERTHREAD);
/* Set to 0, otherwise risk running the clock backwards
* This will cause AudioClock::GetPosition to return the maximum
* possible value for the current buffer
*/
This->laststamp = 0;
This->running = TRUE;
hr = S_OK;
out:
LeaveCriticalSection(This->crst);
return hr;
}
static HRESULT WINAPI AC_Stop(IAudioClient *iface)
{
ACImpl *This = (ACImpl*)iface;
HANDLE timer_id;
TRACE("(%p)\n", This);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!This->running)
return S_FALSE;
EnterCriticalSection(This->crst);
if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
ALint state;
setALContext(This->parent->ctx);
palSourcePause(This->source);
while (1) {
state = AL_STOPPED;
palGetSourcei(This->source, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING)
break;
Sleep(1);
}
getALError();
popALContext();
}
else
palcCaptureStop(This->dev);
This->running = FALSE;
timer_id = This->timer_id;
This->timer_id = 0;
LeaveCriticalSection(This->crst);
if (timer_id)
DeleteTimerQueueTimer(NULL, timer_id, INVALID_HANDLE_VALUE);
return S_OK;
}
static HRESULT WINAPI AC_Reset(IAudioClient *iface)
{
ACImpl *This = (ACImpl*)iface;
HRESULT hr = S_OK;
TRACE("(%p)\n", This);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (This->running)
return AUDCLNT_E_NOT_STOPPED;
EnterCriticalSection(This->crst);
if (This->locked) {
hr = AUDCLNT_E_BUFFER_OPERATION_PENDING;
goto out;
}
if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
ALuint buf;
ALint n = 0;
setALContext(This->parent->ctx);
palSourceStop(This->source);
palGetSourcei(This->source, AL_BUFFERS_PROCESSED, &n);
while (n--) {
palSourceUnqueueBuffers(This->source, 1, &buf);
palDeleteBuffers(1, &buf);
}
getALError();
popALContext();
} else {
ALint avail = 0;
palcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
if (avail)
palcCaptureSamples(This->dev, This->buffer, avail);
}
This->pad = This->padpartial = 0;
This->ofs_frames = 0;
This->frameswritten = 0;
out:
LeaveCriticalSection(This->crst);
return hr;
}
static HRESULT WINAPI AC_SetEventHandle(IAudioClient *iface, HANDLE handle)
{
ACImpl *This = (ACImpl*)iface;
TRACE("(%p)\n", This);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!handle)
return E_INVALIDARG;
if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
This->handle = handle;
return S_OK;
}
static HRESULT WINAPI AC_GetService(IAudioClient *iface, REFIID riid, void **ppv)
{
ACImpl *This = (ACImpl*)iface;
HRESULT hr = S_OK;
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
if (!This->init)
return AUDCLNT_E_NOT_INITIALIZED;
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
if (This->parent->flow != eRender)
return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
if (!This->render)
hr = AudioRenderClient_Create(This, &This->render);
*ppv = This->render;
} else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
if (This->parent->flow != eCapture)
return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
if (!This->capture)
hr = AudioCaptureClient_Create(This, &This->capture);
*ppv = This->capture;
} else if (IsEqualIID(riid, &IID_IAudioSessionControl)) {
if (!This->session)
hr = AudioSessionControl_Create(This, &This->session);
*ppv = This->session;
} else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
if (!This->svolume)
hr = AudioSimpleVolume_Create(This, &This->svolume);
*ppv = This->svolume;
} else if (IsEqualIID(riid, &IID_IAudioClock)) {
if (!This->clock)
hr = AudioClock_Create(This, &This->clock);
*ppv = This->clock;
}
if (FAILED(hr))
return hr;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
FIXME("stub %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static const IAudioClientVtbl ACImpl_Vtbl =
{
AC_QueryInterface,
AC_AddRef,
AC_Release,
AC_Initialize,
AC_GetBufferSize,
AC_GetStreamLatency,
AC_GetCurrentPadding,
AC_IsFormatSupported,
AC_GetMixFormat,
AC_GetDevicePeriod,
AC_Start,
AC_Stop,
AC_Reset,
AC_SetEventHandle,
AC_GetService
};
static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv)
{
ACRender *This;
This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &ACRender_Vtbl;
This->ref = 0;
This->parent = parent;
AC_AddRef((IAudioClient*)This->parent);
return S_OK;
}
static void AudioRenderClient_Destroy(ACRender *This)
{
This->parent->render = NULL;
AC_Release((IAudioClient*)This->parent);
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI ACR_QueryInterface(IAudioRenderClient *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IAudioRenderClient))
*ppv = iface;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI ACR_AddRef(IAudioRenderClient *iface)
{
ACRender *This = (ACRender*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI ACR_Release(IAudioRenderClient *iface)
{
ACRender *This = (ACRender*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioRenderClient_Destroy(This);
return ref;
}
static HRESULT WINAPI ACR_GetBuffer(IAudioRenderClient *iface, UINT32 frames, BYTE **data)
{
ACRender *This = (ACRender*)iface;
DWORD free, framesize;
TRACE("(%p)->(%u,%p)\n", This, frames, data);
if (!data)
return E_POINTER;
if (!frames)
return S_OK;
*data = NULL;
if (This->parent->locked) {
ERR("Locked\n");
return AUDCLNT_E_OUT_OF_ORDER;
}
AC_GetCurrentPadding((IAudioClient*)This->parent, &free);
if (This->parent->bufsize_frames - free < frames) {
ERR("Too large: %u %u %u\n", This->parent->bufsize_frames, free, frames);
return AUDCLNT_E_BUFFER_TOO_LARGE;
}
EnterCriticalSection(This->parent->crst);
This->parent->locked = frames;
framesize = This->parent->pwfx->nBlockAlign;
/* Exact offset doesn't matter, offset could be 0 forever
* but increasing it is easier to debug */
if (This->parent->ofs_frames + frames > This->parent->bufsize_frames)
This->parent->ofs_frames = 0;
*data = This->parent->buffer + This->parent->ofs_frames * framesize;
LeaveCriticalSection(This->parent->crst);
return S_OK;
}
static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
{
ACRender *This = (ACRender*)iface;
BYTE *buf = This->parent->buffer;
DWORD framesize = This->parent->pwfx->nBlockAlign;
DWORD ofs_bytes = This->parent->ofs_frames * framesize;
DWORD written_bytes = written_frames * framesize;
DWORD freq = This->parent->pwfx->nSamplesPerSec;
DWORD bpp = This->parent->pwfx->wBitsPerSample;
ALuint albuf;
TRACE("(%p)->(%u,%x)\n", This, written_frames, flags);
if (This->parent->locked < written_frames)
return AUDCLNT_E_INVALID_SIZE;
if (flags & ~AUDCLNT_BUFFERFLAGS_SILENT)
return E_INVALIDARG;
if (!written_frames) {
if (This->parent->locked)
FIXME("Handled right?\n");
This->parent->locked = 0;
return S_OK;
}
if (!This->parent->locked)
return AUDCLNT_E_OUT_OF_ORDER;
EnterCriticalSection(This->parent->crst);
This->parent->ofs_frames += written_frames;
This->parent->ofs_frames %= This->parent->bufsize_frames;
This->parent->pad += written_frames;
This->parent->frameswritten += written_frames;
This->parent->locked = 0;
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
memset(buf + ofs_bytes, bpp != 8 ? 0 : 128, written_bytes);
TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf, ofs_bytes, written_bytes, freq);
if (!valid_dev(This->parent))
goto out;
setALContext(This->parent->parent->ctx);
palGenBuffers(1, &albuf);
palBufferData(albuf, This->parent->format, buf + ofs_bytes, written_bytes, freq);
palSourceQueueBuffers(This->parent->source, 1, &albuf);
TRACE("Queued %u\n", albuf);
if (This->parent->running) {
ALint state = AL_PLAYING, done = 0, padpart = 0;
palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
palGetSourcei(This->parent->source, AL_BYTE_OFFSET, &padpart);
palGetSourcei(This->parent->source, AL_SOURCE_STATE, &state);
padpart /= framesize;
if (state == AL_STOPPED) {
padpart = This->parent->pad;
/* Buffer might have been processed in the small window
* between first and third call */
palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
}
if (done || This->parent->padpartial != padpart)
This->parent->laststamp = gettime();
This->parent->padpartial = padpart;
while (done--) {
ALint size, bits, chan;
ALuint which;
palSourceUnqueueBuffers(This->parent->source, 1, &which);
palGetBufferi(which, AL_SIZE, &size);
palGetBufferi(which, AL_BITS, &bits);
palGetBufferi(which, AL_CHANNELS, &chan);
size /= bits * chan / 8;
if (size > This->parent->pad) {
ERR("Overflow!\n");
size = This->parent->pad;
}
This->parent->pad -= size;
This->parent->padpartial -= size;
TRACE("Unqueued %u\n", which);
palDeleteBuffers(1, &which);
}
if (state != AL_PLAYING) {
ERR("Starting from %x\n", state);
palSourcePlay(This->parent->source);
}
getALError();
}
getALError();
popALContext();
out:
LeaveCriticalSection(This->parent->crst);
return S_OK;
}
static const IAudioRenderClientVtbl ACRender_Vtbl = {
ACR_QueryInterface,
ACR_AddRef,
ACR_Release,
ACR_GetBuffer,
ACR_ReleaseBuffer
};
static HRESULT AudioCaptureClient_Create(ACImpl *parent, ACCapture **ppv)
{
ACCapture *This;
This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &ACCapture_Vtbl;
This->ref = 0;
This->parent = parent;
AC_AddRef((IAudioClient*)This->parent);
return S_OK;
}
static void AudioCaptureClient_Destroy(ACCapture *This)
{
This->parent->capture = NULL;
AC_Release((IAudioClient*)This->parent);
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI ACC_QueryInterface(IAudioCaptureClient *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IAudioCaptureClient))
*ppv = iface;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI ACC_AddRef(IAudioCaptureClient *iface)
{
ACCapture *This = (ACCapture*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI ACC_Release(IAudioCaptureClient *iface)
{
ACCapture *This = (ACCapture*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioCaptureClient_Destroy(This);
return ref;
}
static HRESULT WINAPI ACC_GetBuffer(IAudioCaptureClient *iface, BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos, UINT64 *qpcpos)
{
ACCapture *This = (ACCapture*)iface;
HRESULT hr;
DWORD block = This->parent->pwfx->nBlockAlign;
DWORD ofs;
TRACE("(%p)->(%p,%p,%p,%p,%p)\n", This, data, frames, flags, devpos, qpcpos);
if (!data)
return E_POINTER;
if (!frames)
return E_POINTER;
if (!flags) {
FIXME("Flags can be null?\n");
return E_POINTER;
}
EnterCriticalSection(This->parent->crst);
hr = AUDCLNT_E_OUT_OF_ORDER;
if (This->parent->locked)
goto out;
IAudioCaptureClient_GetNextPacketSize(iface, frames);
ofs = This->parent->ofs_frames;
if ( ofs % This->parent->periodsize_frames)
ERR("Unaligned offset %u with %u\n", ofs, This->parent->periodsize_frames);
*data = This->parent->buffer + ofs * block;
This->parent->locked = *frames;
if (devpos)
*devpos = This->parent->frameswritten - This->parent->pad;
if (qpcpos)
*qpcpos = This->parent->laststamp;
if (*frames)
hr = S_OK;
else
hr = AUDCLNT_S_BUFFER_EMPTY;
out:
LeaveCriticalSection(This->parent->crst);
TRACE("Returning %08x %i\n", hr, *frames);
return hr;
}
static HRESULT WINAPI ACC_ReleaseBuffer(IAudioCaptureClient *iface, UINT32 written)
{
ACCapture *This = (ACCapture*)iface;
HRESULT hr = S_OK;
EnterCriticalSection(This->parent->crst);
if (!written || written == This->parent->locked) {
This->parent->locked = 0;
This->parent->ofs_frames += written;
This->parent->ofs_frames %= This->parent->bufsize_frames;
This->parent->pad -= written;
} else if (!This->parent->locked)
hr = AUDCLNT_E_OUT_OF_ORDER;
else
hr = AUDCLNT_E_INVALID_SIZE;
LeaveCriticalSection(This->parent->crst);
return hr;
}
static HRESULT WINAPI ACC_GetNextPacketSize(IAudioCaptureClient *iface, UINT32 *frames)
{
ACCapture *This = (ACCapture*)iface;
return AC_GetCurrentPadding((IAudioClient*)This->parent, frames);
}
static const IAudioCaptureClientVtbl ACCapture_Vtbl =
{
ACC_QueryInterface,
ACC_AddRef,
ACC_Release,
ACC_GetBuffer,
ACC_ReleaseBuffer,
ACC_GetNextPacketSize
};
static HRESULT AudioSessionControl_Create(ACImpl *parent, ACSession **ppv)
{
ACSession *This;
This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &ACSession_Vtbl;
This->ref = 0;
This->parent = parent;
AC_AddRef((IAudioClient*)This->parent);
return S_OK;
}
static void AudioSessionControl_Destroy(ACSession *This)
{
This->parent->session = NULL;
AC_Release((IAudioClient*)This->parent);
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI ACS_QueryInterface(IAudioSessionControl2 *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IAudioSessionControl)
|| IsEqualIID(riid, &IID_IAudioSessionControl2))
*ppv = iface;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI ACS_AddRef(IAudioSessionControl2 *iface)
{
ACSession *This = (ACSession*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI ACS_Release(IAudioSessionControl2 *iface)
{
ACSession *This = (ACSession*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioSessionControl_Destroy(This);
return ref;
}
static HRESULT WINAPI ACS_GetState(IAudioSessionControl2 *iface, AudioSessionState *state)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, state);
if (!state)
return E_POINTER;
*state = This->parent->parent->state;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_GetDisplayName(IAudioSessionControl2 *iface, WCHAR **name)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, name);
FIXME("stub\n");
if (name)
*name = NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_SetDisplayName(IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p,%s)\n", This, name, debugstr_guid(session));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_GetIconPath(IAudioSessionControl2 *iface, WCHAR **path)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, path);
FIXME("stub\n");
if (path)
*path = NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_SetIconPath(IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p,%s)\n", This, path, debugstr_guid(session));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_GetGroupingParam(IAudioSessionControl2 *iface, GUID *group)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, group);
FIXME("stub\n");
if (group)
*group = GUID_NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_SetGroupingParam(IAudioSessionControl2 *iface, GUID *group, const GUID *session)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%s,%s)\n", This, debugstr_guid(group), debugstr_guid(session));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_RegisterAudioSessionNotification(IAudioSessionControl2 *iface, IAudioSessionEvents *events)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, events);
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI ACS_UnregisterAudioSessionNotification(IAudioSessionControl2 *iface, IAudioSessionEvents *events)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, events);
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI ACS_GetSessionIdentifier(IAudioSessionControl2 *iface, WCHAR **id)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, id);
FIXME("stub\n");
if (id)
*id = NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_GetSessionInstanceIdentifier(IAudioSessionControl2 *iface, WCHAR **id)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, id);
FIXME("stub\n");
if (id)
*id = NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI ACS_GetProcessId(IAudioSessionControl2 *iface, DWORD *pid)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)->(%p)\n", This, pid);
if (!pid)
return E_POINTER;
*pid = GetCurrentProcessId();
return S_OK;
}
static HRESULT WINAPI ACS_IsSystemSoundsSession(IAudioSessionControl2 *iface)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)\n", This);
return S_FALSE;
}
static HRESULT WINAPI ACS_SetDuckingPreference(IAudioSessionControl2 *iface, BOOL optout)
{
ACSession *This = (ACSession*)iface;
TRACE("(%p)\n", This);
return S_OK;
}
static const IAudioSessionControl2Vtbl ACSession_Vtbl =
{
ACS_QueryInterface,
ACS_AddRef,
ACS_Release,
ACS_GetState,
ACS_GetDisplayName,
ACS_SetDisplayName,
ACS_GetIconPath,
ACS_SetIconPath,
ACS_GetGroupingParam,
ACS_SetGroupingParam,
ACS_RegisterAudioSessionNotification,
ACS_UnregisterAudioSessionNotification,
ACS_GetSessionIdentifier,
ACS_GetSessionInstanceIdentifier,
ACS_GetProcessId,
ACS_IsSystemSoundsSession,
ACS_SetDuckingPreference
};
static HRESULT AudioSimpleVolume_Create(ACImpl *parent, ASVolume **ppv)
{
ASVolume *This;
This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &ASVolume_Vtbl;
This->ref = 0;
This->parent = parent;
AC_AddRef((IAudioClient*)This->parent);
return S_OK;
}
static void AudioSimpleVolume_Destroy(ASVolume *This)
{
This->parent->svolume = NULL;
AC_Release((IAudioClient*)This->parent);
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI ASV_QueryInterface(ISimpleAudioVolume *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_ISimpleAudioVolume))
*ppv = iface;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI ASV_AddRef(ISimpleAudioVolume *iface)
{
ASVolume *This = (ASVolume*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI ASV_Release(ISimpleAudioVolume *iface)
{
ASVolume *This = (ASVolume*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioSimpleVolume_Destroy(This);
return ref;
}
static HRESULT WINAPI ASV_SetMasterVolume(ISimpleAudioVolume *iface, float level, const GUID *context)
{
ASVolume *This = (ASVolume*)iface;
TRACE("(%p)->(%f,%p)\n", This, level, context);
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI ASV_GetMasterVolume(ISimpleAudioVolume *iface, float *level)
{
ASVolume *This = (ASVolume*)iface;
TRACE("(%p)->(%p)\n", This, level);
*level = 1.f;
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI ASV_SetMute(ISimpleAudioVolume *iface, BOOL mute, const GUID *context)
{
ASVolume *This = (ASVolume*)iface;
TRACE("(%p)->(%u,%p)\n", This, mute, context);
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI ASV_GetMute(ISimpleAudioVolume *iface, BOOL *mute)
{
ASVolume *This = (ASVolume*)iface;
TRACE("(%p)->(%p)\n", This, mute);
*mute = 0;
FIXME("stub\n");
return S_OK;
}
static const ISimpleAudioVolumeVtbl ASVolume_Vtbl =
{
ASV_QueryInterface,
ASV_AddRef,
ASV_Release,
ASV_SetMasterVolume,
ASV_GetMasterVolume,
ASV_SetMute,
ASV_GetMute
};
static HRESULT AudioClock_Create(ACImpl *parent, AClock **ppv)
{
AClock *This;
This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &AClock_Vtbl;
This->lp2Vtbl = &AClock2_Vtbl;
This->ref = 0;
This->parent = parent;
AC_AddRef((IAudioClient*)This->parent);
return S_OK;
}
static void AudioClock_Destroy(AClock *This)
{
This->parent->clock = NULL;
AC_Release((IAudioClient*)This->parent);
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI AClock_QueryInterface(IAudioClock *iface, REFIID riid, void **ppv)
{
AClock *This = (AClock*)iface;
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IAudioClock))
*ppv = iface;
else if (IsEqualIID(riid, &IID_IAudioClock2))
*ppv = &This->lp2Vtbl;
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI AClock_AddRef(IAudioClock *iface)
{
AClock *This = (AClock*)iface;
ULONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("Refcount now %i\n", ref);
return ref;
}
static ULONG WINAPI AClock_Release(IAudioClock *iface)
{
AClock *This = (AClock*)iface;
ULONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("Refcount now %i\n", ref);
if (!ref)
AudioClock_Destroy(This);
return ref;
}
static HRESULT WINAPI AClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
{
AClock *This = (AClock*)iface;
TRACE("(%p)->(%p)\n", This, freq);
*freq = (UINT64)This->parent->pwfx->nSamplesPerSec;
return S_OK;
}
static HRESULT WINAPI AClock_GetPosition(IAudioClock *iface, UINT64 *pos, UINT64 *qpctime)
{
AClock *This = (AClock*)iface;
DWORD pad;
TRACE("(%p)->(%p,%p)\n", This, pos, qpctime);
if (!pos)
return E_POINTER;
EnterCriticalSection(This->parent->crst);
AC_GetCurrentPadding((IAudioClient*)This->parent, &pad);
*pos = This->parent->frameswritten - pad;
if (qpctime)
*qpctime = gettime();
LeaveCriticalSection(This->parent->crst);
return S_OK;
}
static HRESULT WINAPI AClock_GetCharacteristics(IAudioClock *iface, DWORD *chars)
{
AClock *This = (AClock*)iface;
TRACE("(%p)->(%p)\n", This, chars);
if (!chars)
return E_POINTER;
*chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
return S_OK;
}
static const IAudioClockVtbl AClock_Vtbl =
{
AClock_QueryInterface,
AClock_AddRef,
AClock_Release,
AClock_GetFrequency,
AClock_GetPosition,
AClock_GetCharacteristics
};
static AClock *get_clock_from_clock2(IAudioClock2 *iface)
{
return (AClock*)((char*)iface - offsetof(AClock,lp2Vtbl));
}
static HRESULT WINAPI AClock2_QueryInterface(IAudioClock2 *iface, REFIID riid, void **ppv)
{
AClock *This = get_clock_from_clock2(iface);
return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
}
static ULONG WINAPI AClock2_AddRef(IAudioClock2 *iface)
{
AClock *This = get_clock_from_clock2(iface);
return IUnknown_AddRef((IUnknown*)This);
}
static ULONG WINAPI AClock2_Release(IAudioClock2 *iface)
{
AClock *This = get_clock_from_clock2(iface);
return IUnknown_Release((IUnknown*)This);
}
static HRESULT WINAPI AClock2_GetPosition(IAudioClock2 *iface, UINT64 *pos, UINT64 *qpctime)
{
AClock *This = get_clock_from_clock2(iface);
return AClock_GetPosition((IAudioClock*)This, pos, qpctime);
}
static const IAudioClock2Vtbl AClock2_Vtbl =
{
AClock2_QueryInterface,
AClock2_AddRef,
AClock2_Release,
AClock2_GetPosition
};
#endif
......@@ -22,13 +22,6 @@
#include "config.h"
#include <stdarg.h>
#ifdef HAVE_AL_AL_H
#include <AL/al.h>
#include <AL/alc.h>
#elif defined(HAVE_OPENAL_AL_H)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif
#include "windef.h"
#include "winbase.h"
......@@ -49,8 +42,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
#ifdef HAVE_OPENAL
static const IAudioEndpointVolumeExVtbl AEVImpl_Vtbl;
typedef struct AEVImpl {
......@@ -297,5 +288,3 @@ static const IAudioEndpointVolumeExVtbl AEVImpl_Vtbl = {
AEV_GetVolumeRange,
AEV_GetVolumeRangeChannel
};
#endif
......@@ -20,14 +20,6 @@
#include <stdarg.h>
#ifdef HAVE_AL_AL_H
#include <AL/al.h>
#include <AL/alc.h>
#elif defined(HAVE_OPENAL_AL_H)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif
#define NONAMELESSUNION
#define COBJMACROS
#include "windef.h"
......@@ -267,7 +259,7 @@ static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERT
* If GUID is null, a random guid will be assigned
* and the device will be created
*/
static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
static MMDevice *MMDevice_Create(WCHAR *name, void *devkey, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
{
HKEY key, root;
MMDevice *cur;
......@@ -277,11 +269,12 @@ static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flo
for (i = 0; i < MMDevice_count; ++i)
{
cur = MMDevice_head[i];
if (cur->flow == flow && !lstrcmpW(cur->alname, name))
if (cur->flow == flow && !lstrcmpW(cur->drv_id, name))
{
LONG ret;
/* Same device, update state */
cur->state = state;
cur->key = devkey;
StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
if (ret == ERROR_SUCCESS)
......@@ -295,15 +288,19 @@ static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flo
/* No device found, allocate new one */
cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
if (!cur)
return;
cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
if (!cur->alname)
if (!cur){
HeapFree(GetProcessHeap(), 0, devkey);
return NULL;
}
cur->drv_id = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
if (!cur->drv_id)
{
HeapFree(GetProcessHeap(), 0, cur);
return;
HeapFree(GetProcessHeap(), 0, devkey);
return NULL;
}
lstrcpyW(cur->alname, name);
lstrcpyW(cur->drv_id, name);
cur->key = devkey;
cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
cur->ref = 0;
......@@ -311,7 +308,6 @@ static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flo
cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
cur->flow = flow;
cur->state = state;
cur->device = NULL;
if (!id)
{
id = &cur->devguid;
......@@ -352,14 +348,125 @@ done:
else
MMDevice_def_rec = cur;
}
if (dev)
*dev = cur;
return cur;
}
static HRESULT load_devices_from_reg(void)
{
DWORD i = 0;
HKEY root, cur;
LONG ret;
DWORD curflow;
ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
if (ret == ERROR_SUCCESS)
ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
if (ret == ERROR_SUCCESS)
ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
RegCloseKey(root);
cur = key_capture;
curflow = eCapture;
if (ret != ERROR_SUCCESS)
{
RegCloseKey(key_capture);
key_render = key_capture = NULL;
WARN("Couldn't create key: %u\n", ret);
return E_FAIL;
}
do {
WCHAR guidvalue[39];
GUID guid;
DWORD len;
PROPVARIANT pv = { VT_EMPTY };
len = sizeof(guidvalue)/sizeof(guidvalue[0]);
ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
if (ret == ERROR_NO_MORE_ITEMS)
{
if (cur == key_capture)
{
cur = key_render;
curflow = eRender;
i = 0;
continue;
}
break;
}
if (ret != ERROR_SUCCESS)
continue;
if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
&& SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
&& pv.vt == VT_LPWSTR)
{
MMDevice_Create(pv.u.pwszVal, NULL, &guid, curflow,
DEVICE_STATE_NOTPRESENT, FALSE);
CoTaskMemFree(pv.u.pwszVal);
}
} while (1);
return S_OK;
}
static HRESULT set_format(MMDevice *dev)
{
HRESULT hr;
IAudioClient *client;
WAVEFORMATEX *fmt;
PROPVARIANT pv = { VT_EMPTY };
hr = drvs.pGetAudioEndpoint(dev->key, &dev->IMMDevice_iface, dev->flow, &client);
if(FAILED(hr))
return hr;
hr = IAudioClient_GetMixFormat(client, &fmt);
if(FAILED(hr)){
IAudioClient_Release(client);
return hr;
}
IAudioClient_Release(client);
pv.vt = VT_BLOB;
pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
pv.u.blob.pBlobData = (BYTE*)fmt;
MMDevice_SetPropValue(&dev->devguid, dev->flow,
&PKEY_AudioEngine_DeviceFormat, &pv);
MMDevice_SetPropValue(&dev->devguid, dev->flow,
&PKEY_AudioEngine_OEMFormat, &pv);
return S_OK;
}
static HRESULT load_driver_devices(EDataFlow flow)
{
WCHAR **ids;
void **keys;
UINT num, def, i;
HRESULT hr;
hr = drvs.pGetEndpointIDs(flow, &ids, &keys, &num, &def);
if(FAILED(hr))
return hr;
for(i = 0; i < num; ++i){
MMDevice *dev;
dev = MMDevice_Create(ids[i], keys[i], NULL, flow, DEVICE_STATE_ACTIVE,
def == i);
set_format(dev);
HeapFree(GetProcessHeap(), 0, ids[i]);
}
HeapFree(GetProcessHeap(), 0, keys);
HeapFree(GetProcessHeap(), 0, ids);
return S_OK;
}
static void MMDevice_Destroy(MMDevice *This)
{
DWORD i;
TRACE("Freeing %s\n", debugstr_w(This->alname));
TRACE("Freeing %s\n", debugstr_w(This->drv_id));
/* Since this function is called at destruction time, reordering of the list is unimportant */
for (i = 0; i < MMDevice_count; ++i)
{
......@@ -369,13 +476,10 @@ static void MMDevice_Destroy(MMDevice *This)
break;
}
}
#ifdef HAVE_OPENAL
if (This->device)
palcCloseDevice(This->device);
#endif
This->crst.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&This->crst);
HeapFree(GetProcessHeap(), 0, This->alname);
HeapFree(GetProcessHeap(), 0, This->drv_id);
HeapFree(GetProcessHeap(), 0, This->key);
HeapFree(GetProcessHeap(), 0, This);
}
......@@ -429,8 +533,6 @@ static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
{
HRESULT hr = E_NOINTERFACE;
#ifdef HAVE_OPENAL
MMDevice *This = impl_from_IMMDevice(iface);
TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
......@@ -438,14 +540,9 @@ static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD cls
if (!ppv)
return E_POINTER;
if (!openal_loaded)
{
WARN("OpenAL is still not loaded\n");
hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
}
else if (IsEqualIID(riid, &IID_IAudioClient))
hr = AudioClient_Create(This, (IAudioClient**)ppv);
else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
if (IsEqualIID(riid, &IID_IAudioClient)){
hr = drvs.pGetAudioEndpoint(This->key, iface, This->flow, (IAudioClient**)ppv);
}else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
else if (IsEqualIID(riid, &IID_IAudioSessionManager)
|| IsEqualIID(riid, &IID_IAudioSessionManager2))
......@@ -509,10 +606,6 @@ static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD cls
}
else
ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
#else
if (!ppv) return E_POINTER;
hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
#endif
if (FAILED(hr))
*ppv = NULL;
......@@ -727,205 +820,12 @@ static const IMMDeviceCollectionVtbl MMDevColVtbl =
MMDevCol_Item
};
#ifdef HAVE_OPENAL
static void openal_setformat(MMDevice *This, DWORD freq)
{
HRESULT hr;
PROPVARIANT pv = { VT_EMPTY };
hr = MMDevice_GetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
if (SUCCEEDED(hr) && pv.vt == VT_BLOB)
{
WAVEFORMATEX *pwfx;
pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
if (pwfx->nSamplesPerSec != freq)
{
pwfx->nSamplesPerSec = freq;
pwfx->nAvgBytesPerSec = freq * pwfx->nBlockAlign;
MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
}
CoTaskMemFree(pwfx);
}
else
{
WAVEFORMATEXTENSIBLE wfxe;
wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfxe.Format.nChannels = 2;
wfxe.Format.wBitsPerSample = 32;
wfxe.Format.nBlockAlign = wfxe.Format.nChannels * wfxe.Format.wBitsPerSample/8;
wfxe.Format.nSamplesPerSec = freq;
wfxe.Format.nAvgBytesPerSec = wfxe.Format.nSamplesPerSec * wfxe.Format.nBlockAlign;
wfxe.Format.cbSize = sizeof(wfxe)-sizeof(WAVEFORMATEX);
wfxe.Samples.wValidBitsPerSample = 32;
wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
wfxe.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
pv.vt = VT_BLOB;
pv.u.blob.cbSize = sizeof(wfxe);
pv.u.blob.pBlobData = (BYTE*)&wfxe;
MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_OEMFormat, &pv);
}
}
static int blacklist_pulse;
static int blacklist(const char *dev) {
#ifdef __linux__
if (!strncmp(dev, "OSS ", 4))
return 1;
#endif
if (blacklist_pulse && !strncmp(dev, "PulseAudio ", 11))
return 1;
if (!strncmp(dev, "ALSA ", 5) && strstr(dev, "hw:"))
return 1;
if (!strncmp(dev, "PortAudio ", 10))
return 1;
return 0;
}
static void pulse_fixup(const char *devstr, const char **defstr, int render) {
static int warned;
int default_pulse;
if (render && !blacklist_pulse && !local_contexts)
blacklist_pulse = 1;
if (!blacklist_pulse || !devstr || !*devstr)
return;
default_pulse = !strncmp(*defstr, "PulseAudio ", 11);
while (*devstr && !strncmp(devstr, "PulseAudio ", 11))
devstr += strlen(devstr) + 1;
/* Could still be a newer version, so check for 1.11 if more devices are enabled */
if (render && *devstr) {
ALCdevice *dev = palcOpenDevice(devstr);
ALCcontext *ctx = palcCreateContext(dev, NULL);
if (ctx) {
const char *ver;
setALContext(ctx);
ver = palGetString(AL_VERSION);
popALContext();
palcDestroyContext(ctx);
if (!strcmp(ver, "1.1 ALSOFT 1.11.753")) {
blacklist_pulse = 0;
palcCloseDevice(dev);
return;
}
}
if (dev)
palcCloseDevice(dev);
}
if (!warned++) {
ERR("Disabling pulseaudio because of old openal version\n");
ERR("Please upgrade to openal-soft v1.12 or newer\n");
}
TRACE("New default: %s\n", devstr);
if (default_pulse)
*defstr = devstr;
}
static void openal_scanrender(void)
{
WCHAR name[MAX_PATH];
ALCdevice *dev;
const ALCchar *devstr, *defaultstr;
int defblacklisted;
EnterCriticalSection(&openal_crst);
if (palcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) {
defaultstr = palcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
devstr = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
} else {
defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
}
pulse_fixup(devstr, &defaultstr, 1);
defblacklisted = blacklist(defaultstr);
if (defblacklisted)
WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
if (devstr)
for (; *devstr; devstr += strlen(devstr)+1) {
MMDevice *mmdev;
MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
name, sizeof(name)/sizeof(*name)-1 );
name[sizeof(name)/sizeof(*name)-1] = 0;
/* Only enable blacklist if the default device isn't blacklisted */
if (!defblacklisted && blacklist(devstr)) {
WARN("Not adding %s: device is blacklisted\n", devstr);
continue;
}
TRACE("Adding %s\n", devstr);
dev = palcOpenDevice(devstr);
MMDevice_Create(&mmdev, name, NULL, eRender, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
if (dev)
{
ALint freq = 44100;
palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
openal_setformat(mmdev, freq);
palcCloseDevice(dev);
}
else
WARN("Could not open device: %04x\n", palcGetError(NULL));
}
LeaveCriticalSection(&openal_crst);
}
static void openal_scancapture(void)
{
WCHAR name[MAX_PATH];
ALCdevice *dev;
const ALCchar *devstr, *defaultstr;
int defblacklisted;
EnterCriticalSection(&openal_crst);
devstr = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
defaultstr = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
pulse_fixup(devstr, &defaultstr, 0);
defblacklisted = blacklist(defaultstr);
if (defblacklisted)
WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
if (devstr && *devstr)
for (; *devstr; devstr += strlen(devstr)+1) {
MMDevice *mmdev;
ALint freq = 44100;
MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
name, sizeof(name)/sizeof(*name)-1 );
name[sizeof(name)/sizeof(*name)-1] = 0;
if (!defblacklisted && blacklist(devstr)) {
WARN("Not adding %s: device is blacklisted\n", devstr);
continue;
}
TRACE("Adding %s\n", devstr);
dev = palcCaptureOpenDevice(devstr, freq, AL_FORMAT_MONO16, 65536);
MMDevice_Create(&mmdev, name, NULL, eCapture, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
if (dev) {
openal_setformat(mmdev, freq);
palcCaptureCloseDevice(dev);
} else
WARN("Could not open device: %04x\n", palcGetError(NULL));
}
LeaveCriticalSection(&openal_crst);
}
#endif /*HAVE_OPENAL*/
HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
{
MMDevEnumImpl *This = MMDevEnumerator;
if (!This)
{
DWORD i = 0;
HKEY root, cur;
LONG ret;
DWORD curflow;
This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
*ppv = NULL;
if (!This)
......@@ -934,62 +834,9 @@ HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
MMDevEnumerator = This;
ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
if (ret == ERROR_SUCCESS)
ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
if (ret == ERROR_SUCCESS)
ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
RegCloseKey(root);
cur = key_capture;
curflow = eCapture;
if (ret != ERROR_SUCCESS)
{
RegCloseKey(key_capture);
key_render = key_capture = NULL;
WARN("Couldn't create key: %u\n", ret);
return E_FAIL;
}
else do {
WCHAR guidvalue[39];
GUID guid;
DWORD len;
PROPVARIANT pv = { VT_EMPTY };
len = sizeof(guidvalue)/sizeof(guidvalue[0]);
ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
if (ret == ERROR_NO_MORE_ITEMS)
{
if (cur == key_capture)
{
cur = key_render;
curflow = eRender;
i = 0;
continue;
}
break;
}
if (ret != ERROR_SUCCESS)
continue;
if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
&& SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
&& pv.vt == VT_LPWSTR)
{
MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
DEVICE_STATE_NOTPRESENT, FALSE);
CoTaskMemFree(pv.u.pwszVal);
}
} while (1);
#ifdef HAVE_OPENAL
if (openal_loaded)
{
openal_scanrender();
openal_scancapture();
}
else
FIXME("OpenAL support not enabled, application will not find sound devices\n");
#else
ERR("OpenAL support not compiled in, application will not find sound devices\n");
#endif /*HAVE_OPENAL*/
load_devices_from_reg();
load_driver_devices(eRender);
load_driver_devices(eCapture);
}
return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
}
......
/*
* Copyright 2009 Maarten Lankhorst
* Copyright 2011 Andrew Eikum for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -21,14 +22,6 @@
#include <stdarg.h>
#ifdef HAVE_AL_AL_H
#include <AL/al.h>
#include <AL/alc.h>
#elif defined(HAVE_OPENAL_AL_H)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#endif
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
......@@ -48,274 +41,84 @@
#include "endpointvolume.h"
#include "audiopolicy.h"
#include "devpkey.h"
#include "winreg.h"
#include "mmdevapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
#ifdef HAVE_OPENAL
static HINSTANCE instance;
int local_contexts;
DriverFuncs drvs;
static CRITICAL_SECTION_DEBUG openal_crst_debug =
{
0, 0, &openal_crst,
{ &openal_crst_debug.ProcessLocksList,
&openal_crst_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": openal_crst_debug") }
};
CRITICAL_SECTION openal_crst = { &openal_crst_debug, -1, 0, 0, 0, 0 };
static void *openal_handle = RTLD_DEFAULT;
int openal_loaded;
#ifdef SONAME_LIBOPENAL
LPALCCREATECONTEXT palcCreateContext = NULL;
LPALCMAKECONTEXTCURRENT palcMakeContextCurrent = NULL;
LPALCPROCESSCONTEXT palcProcessContext = NULL;
LPALCSUSPENDCONTEXT palcSuspendContext = NULL;
LPALCDESTROYCONTEXT palcDestroyContext = NULL;
LPALCGETCURRENTCONTEXT palcGetCurrentContext = NULL;
LPALCGETCONTEXTSDEVICE palcGetContextsDevice = NULL;
LPALCOPENDEVICE palcOpenDevice = NULL;
LPALCCLOSEDEVICE palcCloseDevice = NULL;
LPALCGETERROR palcGetError = NULL;
LPALCISEXTENSIONPRESENT palcIsExtensionPresent = NULL;
LPALCGETPROCADDRESS palcGetProcAddress = NULL;
LPALCGETENUMVALUE palcGetEnumValue = NULL;
LPALCGETSTRING palcGetString = NULL;
LPALCGETINTEGERV palcGetIntegerv = NULL;
LPALCCAPTUREOPENDEVICE palcCaptureOpenDevice = NULL;
LPALCCAPTURECLOSEDEVICE palcCaptureCloseDevice = NULL;
LPALCCAPTURESTART palcCaptureStart = NULL;
LPALCCAPTURESTOP palcCaptureStop = NULL;
LPALCCAPTURESAMPLES palcCaptureSamples = NULL;
LPALENABLE palEnable = NULL;
LPALDISABLE palDisable = NULL;
LPALISENABLED palIsEnabled = NULL;
LPALGETSTRING palGetString = NULL;
LPALGETBOOLEANV palGetBooleanv = NULL;
LPALGETINTEGERV palGetIntegerv = NULL;
LPALGETFLOATV palGetFloatv = NULL;
LPALGETDOUBLEV palGetDoublev = NULL;
LPALGETBOOLEAN palGetBoolean = NULL;
LPALGETINTEGER palGetInteger = NULL;
LPALGETFLOAT palGetFloat = NULL;
LPALGETDOUBLE palGetDouble = NULL;
LPALGETERROR palGetError = NULL;
LPALISEXTENSIONPRESENT palIsExtensionPresent = NULL;
LPALGETPROCADDRESS palGetProcAddress = NULL;
LPALGETENUMVALUE palGetEnumValue = NULL;
LPALLISTENERF palListenerf = NULL;
LPALLISTENER3F palListener3f = NULL;
LPALLISTENERFV palListenerfv = NULL;
LPALLISTENERI palListeneri = NULL;
LPALLISTENER3I palListener3i = NULL;
LPALLISTENERIV palListeneriv = NULL;
LPALGETLISTENERF palGetListenerf = NULL;
LPALGETLISTENER3F palGetListener3f = NULL;
LPALGETLISTENERFV palGetListenerfv = NULL;
LPALGETLISTENERI palGetListeneri = NULL;
LPALGETLISTENER3I palGetListener3i = NULL;
LPALGETLISTENERIV palGetListeneriv = NULL;
LPALGENSOURCES palGenSources = NULL;
LPALDELETESOURCES palDeleteSources = NULL;
LPALISSOURCE palIsSource = NULL;
LPALSOURCEF palSourcef = NULL;
LPALSOURCE3F palSource3f = NULL;
LPALSOURCEFV palSourcefv = NULL;
LPALSOURCEI palSourcei = NULL;
LPALSOURCE3I palSource3i = NULL;
LPALSOURCEIV palSourceiv = NULL;
LPALGETSOURCEF palGetSourcef = NULL;
LPALGETSOURCE3F palGetSource3f = NULL;
LPALGETSOURCEFV palGetSourcefv = NULL;
LPALGETSOURCEI palGetSourcei = NULL;
LPALGETSOURCE3I palGetSource3i = NULL;
LPALGETSOURCEIV palGetSourceiv = NULL;
LPALSOURCEPLAYV palSourcePlayv = NULL;
LPALSOURCESTOPV palSourceStopv = NULL;
LPALSOURCEREWINDV palSourceRewindv = NULL;
LPALSOURCEPAUSEV palSourcePausev = NULL;
LPALSOURCEPLAY palSourcePlay = NULL;
LPALSOURCESTOP palSourceStop = NULL;
LPALSOURCEREWIND palSourceRewind = NULL;
LPALSOURCEPAUSE palSourcePause = NULL;
LPALSOURCEQUEUEBUFFERS palSourceQueueBuffers = NULL;
LPALSOURCEUNQUEUEBUFFERS palSourceUnqueueBuffers = NULL;
LPALGENBUFFERS palGenBuffers = NULL;
LPALDELETEBUFFERS palDeleteBuffers = NULL;
LPALISBUFFER palIsBuffer = NULL;
LPALBUFFERF palBufferf = NULL;
LPALBUFFER3F palBuffer3f = NULL;
LPALBUFFERFV palBufferfv = NULL;
LPALBUFFERI palBufferi = NULL;
LPALBUFFER3I palBuffer3i = NULL;
LPALBUFFERIV palBufferiv = NULL;
LPALGETBUFFERF palGetBufferf = NULL;
LPALGETBUFFER3F palGetBuffer3f = NULL;
LPALGETBUFFERFV palGetBufferfv = NULL;
LPALGETBUFFERI palGetBufferi = NULL;
LPALGETBUFFER3I palGetBuffer3i = NULL;
LPALGETBUFFERIV palGetBufferiv = NULL;
LPALBUFFERDATA palBufferData = NULL;
LPALDOPPLERFACTOR palDopplerFactor = NULL;
LPALDOPPLERVELOCITY palDopplerVelocity = NULL;
LPALDISTANCEMODEL palDistanceModel = NULL;
LPALSPEEDOFSOUND palSpeedOfSound = NULL;
#endif
typeof(alcGetCurrentContext) *get_context;
typeof(alcMakeContextCurrent) *set_context;
static void load_libopenal(void)
static BOOL load_driver(const WCHAR *name)
{
DWORD failed = 0;
WCHAR driver_module[264];
static const WCHAR wineW[] = {'w','i','n','e',0};
static const WCHAR dotdrvW[] = {'.','d','r','v',0};
#ifdef SONAME_LIBOPENAL
char error[128];
openal_handle = wine_dlopen(SONAME_LIBOPENAL, RTLD_NOW, error, sizeof(error));
if (!openal_handle)
{
ERR("Couldn't load " SONAME_LIBOPENAL ": %s\n", error);
return;
}
lstrcpyW(driver_module, wineW);
lstrcatW(driver_module, name);
lstrcatW(driver_module, dotdrvW);
#define LOAD_FUNCPTR(f) \
if((p##f = wine_dlsym(openal_handle, #f, NULL, 0)) == NULL) { \
ERR("Couldn't lookup %s in libopenal\n", #f); \
failed = 1; \
}
TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module));
LOAD_FUNCPTR(alcCreateContext);
LOAD_FUNCPTR(alcMakeContextCurrent);
LOAD_FUNCPTR(alcProcessContext);
LOAD_FUNCPTR(alcSuspendContext);
LOAD_FUNCPTR(alcDestroyContext);
LOAD_FUNCPTR(alcGetCurrentContext);
LOAD_FUNCPTR(alcGetContextsDevice);
LOAD_FUNCPTR(alcOpenDevice);
LOAD_FUNCPTR(alcCloseDevice);
LOAD_FUNCPTR(alcGetError);
LOAD_FUNCPTR(alcIsExtensionPresent);
LOAD_FUNCPTR(alcGetProcAddress);
LOAD_FUNCPTR(alcGetEnumValue);
LOAD_FUNCPTR(alcGetString);
LOAD_FUNCPTR(alcGetIntegerv);
LOAD_FUNCPTR(alcCaptureOpenDevice);
LOAD_FUNCPTR(alcCaptureCloseDevice);
LOAD_FUNCPTR(alcCaptureStart);
LOAD_FUNCPTR(alcCaptureStop);
LOAD_FUNCPTR(alcCaptureSamples);
LOAD_FUNCPTR(alEnable);
LOAD_FUNCPTR(alDisable);
LOAD_FUNCPTR(alIsEnabled);
LOAD_FUNCPTR(alGetString);
LOAD_FUNCPTR(alGetBooleanv);
LOAD_FUNCPTR(alGetIntegerv);
LOAD_FUNCPTR(alGetFloatv);
LOAD_FUNCPTR(alGetDoublev);
LOAD_FUNCPTR(alGetBoolean);
LOAD_FUNCPTR(alGetInteger);
LOAD_FUNCPTR(alGetFloat);
LOAD_FUNCPTR(alGetDouble);
LOAD_FUNCPTR(alGetError);
LOAD_FUNCPTR(alIsExtensionPresent);
LOAD_FUNCPTR(alGetProcAddress);
LOAD_FUNCPTR(alGetEnumValue);
LOAD_FUNCPTR(alListenerf);
LOAD_FUNCPTR(alListener3f);
LOAD_FUNCPTR(alListenerfv);
LOAD_FUNCPTR(alListeneri);
LOAD_FUNCPTR(alListener3i);
LOAD_FUNCPTR(alListeneriv);
LOAD_FUNCPTR(alGetListenerf);
LOAD_FUNCPTR(alGetListener3f);
LOAD_FUNCPTR(alGetListenerfv);
LOAD_FUNCPTR(alGetListeneri);
LOAD_FUNCPTR(alGetListener3i);
LOAD_FUNCPTR(alGetListeneriv);
LOAD_FUNCPTR(alGenSources);
LOAD_FUNCPTR(alDeleteSources);
LOAD_FUNCPTR(alIsSource);
LOAD_FUNCPTR(alSourcef);
LOAD_FUNCPTR(alSource3f);
LOAD_FUNCPTR(alSourcefv);
LOAD_FUNCPTR(alSourcei);
LOAD_FUNCPTR(alSource3i);
LOAD_FUNCPTR(alSourceiv);
LOAD_FUNCPTR(alGetSourcef);
LOAD_FUNCPTR(alGetSource3f);
LOAD_FUNCPTR(alGetSourcefv);
LOAD_FUNCPTR(alGetSourcei);
LOAD_FUNCPTR(alGetSource3i);
LOAD_FUNCPTR(alGetSourceiv);
LOAD_FUNCPTR(alSourcePlayv);
LOAD_FUNCPTR(alSourceStopv);
LOAD_FUNCPTR(alSourceRewindv);
LOAD_FUNCPTR(alSourcePausev);
LOAD_FUNCPTR(alSourcePlay);
LOAD_FUNCPTR(alSourceStop);
LOAD_FUNCPTR(alSourceRewind);
LOAD_FUNCPTR(alSourcePause);
LOAD_FUNCPTR(alSourceQueueBuffers);
LOAD_FUNCPTR(alSourceUnqueueBuffers);
LOAD_FUNCPTR(alGenBuffers);
LOAD_FUNCPTR(alDeleteBuffers);
LOAD_FUNCPTR(alIsBuffer);
LOAD_FUNCPTR(alBufferf);
LOAD_FUNCPTR(alBuffer3f);
LOAD_FUNCPTR(alBufferfv);
LOAD_FUNCPTR(alBufferi);
LOAD_FUNCPTR(alBuffer3i);
LOAD_FUNCPTR(alBufferiv);
LOAD_FUNCPTR(alGetBufferf);
LOAD_FUNCPTR(alGetBuffer3f);
LOAD_FUNCPTR(alGetBufferfv);
LOAD_FUNCPTR(alGetBufferi);
LOAD_FUNCPTR(alGetBuffer3i);
LOAD_FUNCPTR(alGetBufferiv);
LOAD_FUNCPTR(alBufferData);
LOAD_FUNCPTR(alDopplerFactor);
LOAD_FUNCPTR(alDopplerVelocity);
LOAD_FUNCPTR(alDistanceModel);
LOAD_FUNCPTR(alSpeedOfSound);
#undef LOAD_FUNCPTR
#endif
if (failed)
{
WARN("Unloading openal\n");
if (openal_handle != RTLD_DEFAULT)
wine_dlclose(openal_handle, NULL, 0);
openal_handle = NULL;
openal_loaded = 0;
drvs.module = LoadLibraryW(driver_module);
if(!drvs.module){
TRACE("Unable to load %s: %u\n", wine_dbgstr_w(driver_module),
GetLastError());
return FALSE;
}
else
{
openal_loaded = 1;
local_contexts = palcIsExtensionPresent(NULL, "ALC_EXT_thread_local_context");
if (local_contexts)
{
set_context = palcGetProcAddress(NULL, "alcSetThreadContext");
get_context = palcGetProcAddress(NULL, "alcGetThreadContext");
if (!set_context || !get_context)
{
ERR("TLS advertised but functions not found, disabling thread local context\n");
local_contexts = 0;
}
}
if (!local_contexts)
{
set_context = palcMakeContextCurrent;
get_context = palcGetCurrentContext;
#define LDFC(n) do { drvs.p##n = (void*)GetProcAddress(drvs.module, #n);\
if(!drvs.p##n) return FALSE; } while(0);
LDFC(GetEndpointIDs);
LDFC(GetAudioEndpoint);
#undef LDFC
TRACE("Successfully loaded %s\n", wine_dbgstr_w(driver_module));
return TRUE;
}
static BOOL init_driver(void)
{
static const WCHAR alsaW[] = {'a','l','s','a',0};
static const WCHAR ossW[] = {'o','s','s',0};
static const WCHAR coreaudioW[] = {'c','o','r','e','a','u','d','i','o',0};
static const WCHAR *default_drivers[] = { alsaW, coreaudioW, ossW };
static const WCHAR drv_key[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s',0};
static const WCHAR drv_value[] = {'A','u','d','i','o',0};
HKEY key;
UINT i;
if(drvs.module)
return TRUE;
if(RegOpenKeyW(HKEY_CURRENT_USER, drv_key, &key) == ERROR_SUCCESS){
WCHAR driver_name[256];
DWORD size = sizeof(driver_name);
if(RegQueryValueExW(key, drv_value, 0, NULL, (BYTE*)driver_name,
&size) == ERROR_SUCCESS){
BOOL ret = load_driver(driver_name);
RegCloseKey(key);
if(!ret)
ERR("Failed to load driver: %s\n", wine_dbgstr_w(driver_name));
return ret;
}
RegCloseKey(key);
}
}
#endif /*HAVE_OPENAL*/
for(i = 0; i < sizeof(default_drivers)/sizeof(*default_drivers); ++i)
if(load_driver(default_drivers[i]))
return TRUE;
static HINSTANCE instance;
return FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
......@@ -326,9 +129,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
case DLL_PROCESS_ATTACH:
instance = hinstDLL;
DisableThreadLibraryCalls(hinstDLL);
#ifdef HAVE_OPENAL
load_libopenal();
#endif /*HAVE_OPENAL*/
break;
case DLL_PROCESS_DETACH:
MMDevEnum_Free();
......@@ -429,6 +229,11 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
int i = 0;
TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
if(!init_driver()){
ERR("Driver initialization failed\n");
return E_FAIL;
}
if (ppv == NULL) {
WARN("invalid parameter\n");
return E_INVALIDARG;
......
......@@ -25,6 +25,22 @@ extern void MMDevEnum_Free(void);
extern HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv);
typedef struct _DriverFuncs {
HMODULE module;
/* ids gets an array of human-friendly endpoint names
* keys gets an array of driver-specific stuff that is used
* in GetAudioEndpoint to identify the endpoint
* it is the caller's responsibility to free both arrays, and
* all of the elements in both arrays with HeapFree() */
HRESULT WINAPI (*pGetEndpointIDs)(EDataFlow flow, WCHAR ***ids,
void ***keys, UINT *num, UINT *default_index);
HRESULT WINAPI (*pGetAudioEndpoint)(void *key, IMMDevice *dev,
EDataFlow dataflow, IAudioClient **out);
} DriverFuncs;
extern DriverFuncs drvs;
typedef struct MMDevice {
IMMDevice IMMDevice_iface;
IMMEndpoint IMMEndpoint_iface;
......@@ -35,252 +51,9 @@ typedef struct MMDevice {
EDataFlow flow;
DWORD state;
GUID devguid;
WCHAR *alname;
void *device, *ctx;
WCHAR *drv_id;
void *key;
} MMDevice;
extern HRESULT AudioClient_Create(MMDevice *parent, IAudioClient **ppv);
extern HRESULT AudioEndpointVolume_Create(MMDevice *parent, IAudioEndpointVolume **ppv);
#ifdef HAVE_OPENAL
#include "alext.h"
/* All openal functions */
extern int openal_loaded;
#ifdef SONAME_LIBOPENAL
extern LPALCCREATECONTEXT palcCreateContext;
extern LPALCMAKECONTEXTCURRENT palcMakeContextCurrent;
extern LPALCPROCESSCONTEXT palcProcessContext;
extern LPALCSUSPENDCONTEXT palcSuspendContext;
extern LPALCDESTROYCONTEXT palcDestroyContext;
extern LPALCGETCURRENTCONTEXT palcGetCurrentContext;
extern LPALCGETCONTEXTSDEVICE palcGetContextsDevice;
extern LPALCOPENDEVICE palcOpenDevice;
extern LPALCCLOSEDEVICE palcCloseDevice;
extern LPALCGETERROR palcGetError;
extern LPALCISEXTENSIONPRESENT palcIsExtensionPresent;
extern LPALCGETPROCADDRESS palcGetProcAddress;
extern LPALCGETENUMVALUE palcGetEnumValue;
extern LPALCGETSTRING palcGetString;
extern LPALCGETINTEGERV palcGetIntegerv;
extern LPALCCAPTUREOPENDEVICE palcCaptureOpenDevice;
extern LPALCCAPTURECLOSEDEVICE palcCaptureCloseDevice;
extern LPALCCAPTURESTART palcCaptureStart;
extern LPALCCAPTURESTOP palcCaptureStop;
extern LPALCCAPTURESAMPLES palcCaptureSamples;
extern LPALENABLE palEnable;
extern LPALDISABLE palDisable;
extern LPALISENABLED palIsEnabled;
extern LPALGETSTRING palGetString;
extern LPALGETBOOLEANV palGetBooleanv;
extern LPALGETINTEGERV palGetIntegerv;
extern LPALGETFLOATV palGetFloatv;
extern LPALGETDOUBLEV palGetDoublev;
extern LPALGETBOOLEAN palGetBoolean;
extern LPALGETINTEGER palGetInteger;
extern LPALGETFLOAT palGetFloat;
extern LPALGETDOUBLE palGetDouble;
extern LPALGETERROR palGetError;
extern LPALISEXTENSIONPRESENT palIsExtensionPresent;
extern LPALGETPROCADDRESS palGetProcAddress;
extern LPALGETENUMVALUE palGetEnumValue;
extern LPALLISTENERF palListenerf;
extern LPALLISTENER3F palListener3f;
extern LPALLISTENERFV palListenerfv;
extern LPALLISTENERI palListeneri;
extern LPALLISTENER3I palListener3i;
extern LPALLISTENERIV palListeneriv;
extern LPALGETLISTENERF palGetListenerf;
extern LPALGETLISTENER3F palGetListener3f;
extern LPALGETLISTENERFV palGetListenerfv;
extern LPALGETLISTENERI palGetListeneri;
extern LPALGETLISTENER3I palGetListener3i;
extern LPALGETLISTENERIV palGetListeneriv;
extern LPALGENSOURCES palGenSources;
extern LPALDELETESOURCES palDeleteSources;
extern LPALISSOURCE palIsSource;
extern LPALSOURCEF palSourcef;
extern LPALSOURCE3F palSource3f;
extern LPALSOURCEFV palSourcefv;
extern LPALSOURCEI palSourcei;
extern LPALSOURCE3I palSource3i;
extern LPALSOURCEIV palSourceiv;
extern LPALGETSOURCEF palGetSourcef;
extern LPALGETSOURCE3F palGetSource3f;
extern LPALGETSOURCEFV palGetSourcefv;
extern LPALGETSOURCEI palGetSourcei;
extern LPALGETSOURCE3I palGetSource3i;
extern LPALGETSOURCEIV palGetSourceiv;
extern LPALSOURCEPLAYV palSourcePlayv;
extern LPALSOURCESTOPV palSourceStopv;
extern LPALSOURCEREWINDV palSourceRewindv;
extern LPALSOURCEPAUSEV palSourcePausev;
extern LPALSOURCEPLAY palSourcePlay;
extern LPALSOURCESTOP palSourceStop;
extern LPALSOURCEREWIND palSourceRewind;
extern LPALSOURCEPAUSE palSourcePause;
extern LPALSOURCEQUEUEBUFFERS palSourceQueueBuffers;
extern LPALSOURCEUNQUEUEBUFFERS palSourceUnqueueBuffers;
extern LPALGENBUFFERS palGenBuffers;
extern LPALDELETEBUFFERS palDeleteBuffers;
extern LPALISBUFFER palIsBuffer;
extern LPALBUFFERF palBufferf;
extern LPALBUFFER3F palBuffer3f;
extern LPALBUFFERFV palBufferfv;
extern LPALBUFFERI palBufferi;
extern LPALBUFFER3I palBuffer3i;
extern LPALBUFFERIV palBufferiv;
extern LPALGETBUFFERF palGetBufferf;
extern LPALGETBUFFER3F palGetBuffer3f;
extern LPALGETBUFFERFV palGetBufferfv;
extern LPALGETBUFFERI palGetBufferi;
extern LPALGETBUFFER3I palGetBuffer3i;
extern LPALGETBUFFERIV palGetBufferiv;
extern LPALBUFFERDATA palBufferData;
extern LPALDOPPLERFACTOR palDopplerFactor;
extern LPALDOPPLERVELOCITY palDopplerVelocity;
extern LPALDISTANCEMODEL palDistanceModel;
extern LPALSPEEDOFSOUND palSpeedOfSound;
#else
#define palcCreateContext alcCreateContext
#define palcMakeContextCurrent alcMakeContextCurrent
#define palcProcessContext alcProcessContext
#define palcSuspendContext alcSuspendContext
#define palcDestroyContext alcDestroyContext
#define palcGetCurrentContext alcGetCurrentContext
#define palcGetContextsDevice alcGetContextsDevice
#define palcOpenDevice alcOpenDevice
#define palcCloseDevice alcCloseDevice
#define palcGetError alcGetError
#define palcIsExtensionPresent alcIsExtensionPresent
#define palcGetProcAddress alcGetProcAddress
#define palcGetEnumValue alcGetEnumValue
#define palcGetString alcGetString
#define palcGetIntegerv alcGetIntegerv
#define palcCaptureOpenDevice alcCaptureOpenDevice
#define palcCaptureCloseDevice alcCaptureCloseDevice
#define palcCaptureStart alcCaptureStart
#define palcCaptureStop alcCaptureStop
#define palcCaptureSamples alcCaptureSamples
#define palEnable alEnable
#define palDisable alDisable
#define palIsEnabled alIsEnabled
#define palGetString alGetString
#define palGetBooleanv alGetBooleanv
#define palGetIntegerv alGetIntegerv
#define palGetFloatv alGetFloatv
#define palGetDoublev alGetDoublev
#define palGetBoolean alGetBoolean
#define palGetInteger alGetInteger
#define palGetFloat alGetFloat
#define palGetDouble alGetDouble
#define palGetError alGetError
#define palIsExtensionPresent alIsExtensionPresent
#define palGetProcAddress alGetProcAddress
#define palGetEnumValue alGetEnumValue
#define palListenerf alListenerf
#define palListener3f alListener3f
#define palListenerfv alListenerfv
#define palListeneri alListeneri
#define palListener3i alListener3i
#define palListeneriv alListeneriv
#define palGetListenerf alGetListenerf
#define palGetListener3f alGetListener3f
#define palGetListenerfv alGetListenerfv
#define palGetListeneri alGetListeneri
#define palGetListener3i alGetListener3i
#define palGetListeneriv alGetListeneriv
#define palGenSources alGenSources
#define palDeleteSources alDeleteSources
#define palIsSource alIsSource
#define palSourcef alSourcef
#define palSource3f alSource3f
#define palSourcefv alSourcefv
#define palSourcei alSourcei
#define palSource3i alSource3i
#define palSourceiv alSourceiv
#define palGetSourcef alGetSourcef
#define palGetSource3f alGetSource3f
#define palGetSourcefv alGetSourcefv
#define palGetSourcei alGetSourcei
#define palGetSource3i alGetSource3i
#define palGetSourceiv alGetSourceiv
#define palSourcePlayv alSourcePlayv
#define palSourceStopv alSourceStopv
#define palSourceRewindv alSourceRewindv
#define palSourcePausev alSourcePausev
#define palSourcePlay alSourcePlay
#define palSourceStop alSourceStop
#define palSourceRewind alSourceRewind
#define palSourcePause alSourcePause
#define palSourceQueueBuffers alSourceQueueBuffers
#define palSourceUnqueueBuffers alSourceUnqueueBuffers
#define palGenBuffers alGenBuffers
#define palDeleteBuffers alDeleteBuffers
#define palIsBuffer alIsBuffer
#define palBufferf alBufferf
#define palBuffer3f alBuffer3f
#define palBufferfv alBufferfv
#define palBufferi alBufferi
#define palBuffer3i alBuffer3i
#define palBufferiv alBufferiv
#define palGetBufferf alGetBufferf
#define palGetBuffer3f alGetBuffer3f
#define palGetBufferfv alGetBufferfv
#define palGetBufferi alGetBufferi
#define palGetBuffer3i alGetBuffer3i
#define palGetBufferiv alGetBufferiv
#define palBufferData alBufferData
#define palDopplerFactor alDopplerFactor
#define palDopplerVelocity alDopplerVelocity
#define palDistanceModel alDistanceModel
#define palSpeedOfSound alSpeedOfSound
#endif
/* OpenAL only allows for 1 single access to the device at the same time */
extern CRITICAL_SECTION openal_crst;
extern int local_contexts;
extern typeof(alcGetCurrentContext) *get_context;
extern typeof(alcMakeContextCurrent) *set_context;
#define getALError() \
do { \
ALenum err = palGetError(); \
if(err != AL_NO_ERROR) \
{ \
ERR(">>>>>>>>>>>> Received AL error %#x on context %p, %s:%u\n", err, get_context(), __FUNCTION__, __LINE__); \
} \
} while (0)
#define getALCError(dev) \
do { \
ALenum err = palcGetError(dev); \
if(err != ALC_NO_ERROR) \
{ \
ERR(">>>>>>>>>>>> Received ALC error %#x on device %p, %s:%u\n", err, dev, __FUNCTION__, __LINE__); \
} \
} while(0)
#define setALContext(actx) \
do { \
ALCcontext *__old_ctx, *cur_ctx = actx ; \
if (!local_contexts) EnterCriticalSection(&openal_crst); \
__old_ctx = get_context(); \
if (__old_ctx != cur_ctx && set_context(cur_ctx) == ALC_FALSE) {\
ERR("Couldn't set current context!!\n"); \
getALCError(palcGetContextsDevice(cur_ctx)); \
}
/* Only restore a NULL context if using global contexts, for TLS contexts always restore */
#define popALContext() \
if (__old_ctx != cur_ctx \
&& (local_contexts || __old_ctx) \
&& set_context(__old_ctx) == ALC_FALSE) { \
ERR("Couldn't restore old context!!\n"); \
getALCError(palcGetContextsDevice(__old_ctx)); \
} \
if (!local_contexts) LeaveCriticalSection(&openal_crst); \
} while (0)
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment