From 54769a823e2a073b12e6dbc8889ff25a172239f7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 May 2011 04:13:37 -0700 Subject: Implement playback using MMDevApi --- Alc/mmdevapi.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 288 insertions(+), 22 deletions(-) (limited to 'Alc/mmdevapi.c') diff --git a/Alc/mmdevapi.c b/Alc/mmdevapi.c index 19856312..21fed0d5 100644 --- a/Alc/mmdevapi.c +++ b/Alc/mmdevapi.c @@ -20,12 +20,14 @@ #include "config.h" +#define COBJMACROS #define _WIN32_WINNT 0x0500 #include #include #include -#include +#include +#include #include #include #ifndef _WAVEFORMATEXTENSIBLE_ @@ -41,29 +43,112 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); -static ALboolean is_loaded = AL_FALSE; +#define MONO SPEAKER_FRONT_CENTER +#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_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 IMMDeviceEnumerator *Enumerator = NULL; typedef struct { + IMMDevice *mmdev; + IAudioClient *client; volatile int killNow; ALvoid *thread; } MMDevApiData; -static const ALCchar mmDevice[] = "MMDevApi Default"; +static const ALCchar mmDevice[] = "WASAPI Default"; static void *MMDevApiLoad(void) { - return (void*)is_loaded; + if(!Enumerator) + { + void *mme = NULL; + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if(SUCCEEDED(hr)) + { + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &mme); + if(FAILED(hr)) + CoUninitialize(); + else + Enumerator = mme; + } + } + return Enumerator; +} + + +static ALuint MMDevApiProc(ALvoid *ptr) +{ + ALCdevice *device = ptr; + MMDevApiData *data = device->ExtraData; + union { + IAudioRenderClient *iface; + void *ptr; + } render; + UINT32 written, len; + BYTE *buffer; + HRESULT hr; + + hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); + if(FAILED(hr)) + { + AL_PRINT("Failed to get AudioRenderClient service: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + + SetRTPriority(); + + while(!data->killNow) + { + hr = IAudioClient_GetCurrentPadding(data->client, &written); + if(FAILED(hr)) + { + AL_PRINT("Failed to get padding: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + + len = device->UpdateSize*device->NumUpdates - written; + if(len < device->UpdateSize) + { + Sleep(10); + continue; + } + len -= len%device->UpdateSize; + + hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer); + if(SUCCEEDED(hr)) + { + aluMixData(device, buffer, len); + hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0); + } + if(FAILED(hr)) + { + AL_PRINT("Failed to buffer data: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + } + + IAudioRenderClient_Release(render.iface); + return 0; } static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) { - MMDevApiData *pData = NULL; - HRESULT hr = E_FAIL; + MMDevApiData *data = NULL; + void *client = NULL; + HRESULT hr; if(!MMDevApiLoad()) return ALC_FALSE; @@ -74,53 +159,232 @@ static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceN return ALC_FALSE; //Initialise requested device - pData = calloc(1, sizeof(MMDevApiData)); - if(!pData) + data = calloc(1, sizeof(MMDevApiData)); + if(!data) { alcSetError(device, ALC_OUT_OF_MEMORY); return ALC_FALSE; } //MMDevApi Init code + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &client); + if(FAILED(hr)) { - free(pData); + if(data->mmdev) + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + free(data); + AL_PRINT("Device init failed: 0x%08lx\n", hr); return ALC_FALSE; } + data->client = client; device->szDeviceName = strdup(deviceName); - device->ExtraData = pData; + device->ExtraData = data; return ALC_TRUE; } static void MMDevApiClosePlayback(ALCdevice *device) { - MMDevApiData *pData = device->ExtraData; + MMDevApiData *data = device->ExtraData; + + IAudioClient_Release(data->client); + data->client = NULL; + + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; - free(pData); + free(data); device->ExtraData = NULL; } static ALCboolean MMDevApiResetPlayback(ALCdevice *device) { - (void)device; - return ALC_FALSE; + MMDevApiData *data = device->ExtraData; + WAVEFORMATEXTENSIBLE OutputType; + WAVEFORMATEX *wfx = NULL; + HRESULT hr; + + hr = IAudioClient_GetMixFormat(data->client, &wfx); + if(FAILED(hr)) + { + AL_PRINT("Failed to get mix format: 0x%08lx\n", hr); + return ALC_FALSE; + } + + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + device->Frequency = wfx->nSamplesPerSec; + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + if(wfx->wFormatTag == WAVE_FORMAT_PCM) + { + if(wfx->nChannels == 1) + device->FmtChans = DevFmtMono; + else if(wfx->nChannels == 2) + device->FmtChans = DevFmtStereo; + else + { + AL_PRINT("Unhandled PCM channels: %d\n", wfx->nChannels); + device->FmtChans = DevFmtStereo; + } + } + else if(wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx; + + if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == MONO) + device->FmtChans = DevFmtMono; + else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == STEREO) + device->FmtChans = DevFmtStereo; + else if(wfe->Format.nChannels == 4 && wfe->dwChannelMask == QUAD) + device->FmtChans = DevFmtQuad; + else if(wfe->Format.nChannels == 6 && wfe->dwChannelMask == X5DOT1) + device->FmtChans = DevFmtX51; + else if(wfe->Format.nChannels == 7 && wfe->dwChannelMask == X6DOT1) + device->FmtChans = DevFmtX61; + else if(wfe->Format.nChannels == 8 && wfe->dwChannelMask == X7DOT1) + device->FmtChans = DevFmtX71; + else + { + AL_PRINT("Unhandled extensible channels: %d -- 0x%08lx\n", wfe->Format.nChannels, wfe->dwChannelMask); + device->FmtChans = DevFmtStereo; + } + } + } + + if(wfx->wFormatTag == WAVE_FORMAT_PCM) + { + if(wfx->wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else if(wfx->wBitsPerSample == 16) + device->FmtType = DevFmtShort; + } + else if(wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx; + + if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) + { + if(wfx->wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else if(wfx->wBitsPerSample == 16) + device->FmtType = DevFmtShort; + } + else if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + device->FmtType = DevFmtFloat; + } + + CoTaskMemFree(wfx); + wfx = NULL; + + SetDefaultWFXChannelOrder(device); + + memset(&OutputType, 0, sizeof(OutputType)); + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + OutputType.Format.wBitsPerSample = 8; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + OutputType.Format.wBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtFloat: + OutputType.Format.wBitsPerSample = 32; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + break; + } + + switch(device->FmtChans) + { + case DevFmtMono: + OutputType.Format.nChannels = 1; + OutputType.dwChannelMask = MONO; + break; + case DevFmtStereo: + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + break; + case DevFmtQuad: + OutputType.Format.nChannels = 4; + OutputType.dwChannelMask = QUAD; + break; + case DevFmtX51: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = X5DOT1; + break; + case DevFmtX61: + OutputType.Format.nChannels = 7; + OutputType.dwChannelMask = X6DOT1; + break; + case DevFmtX71: + OutputType.Format.nChannels = 8; + OutputType.dwChannelMask = X7DOT1; + break; + } + + OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8; + OutputType.Format.nSamplesPerSec = device->Frequency; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign; + OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format); + + + hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, 0, + (ALuint64)device->UpdateSize * 10000000 / + device->Frequency * device->NumUpdates, + 0, &OutputType.Format, NULL); + if(FAILED(hr)) + { + AL_PRINT("Failed to initialize audio client: 0x%08lx\n", hr); + return ALC_FALSE; + } + + + hr = IAudioClient_Start(data->client); + if(FAILED(hr)) + { + AL_PRINT("Failed to start audio client\n"); + return ALC_FALSE; + } + + data->thread = StartThread(MMDevApiProc, device); + if(!data->thread) + { + IAudioClient_Stop(data->client); + AL_PRINT("Failed to start thread\n"); + return ALC_FALSE; + } + + return ALC_TRUE; } static void MMDevApiStopPlayback(ALCdevice *device) { - MMDevApiData *pData = device->ExtraData; + MMDevApiData *data = device->ExtraData; - if(!pData->thread) + if(!data->thread) return; - pData->killNow = 1; - StopThread(pData->thread); - pData->thread = NULL; + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; - pData->killNow = 0; + data->killNow = 0; + IAudioClient_Stop(data->client); } @@ -153,9 +417,11 @@ void alcMMDevApiInit(BackendFuncs *FuncList) void alcMMDevApiDeinit(void) { - if(is_loaded) + if(Enumerator) { - is_loaded = AL_FALSE; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + CoUninitialize(); } } -- cgit v1.2.3