aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2011-05-16 04:13:37 -0700
committerChris Robinson <[email protected]>2011-05-16 04:13:37 -0700
commit54769a823e2a073b12e6dbc8889ff25a172239f7 (patch)
tree6969dc5730781c406ede7c386280ac79bcd48d20
parentcf3287f8ec955ea87e06897bf000b8608bc4ffc3 (diff)
Implement playback using MMDevApi
-rw-r--r--Alc/mmdevapi.c310
1 files changed, 288 insertions, 22 deletions
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 <stdlib.h>
#include <stdio.h>
#include <memory.h>
-#include <dsound.h>
+#include <mmdeviceapi.h>
+#include <audioclient.h>
#include <cguid.h>
#include <mmreg.h>
#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();
}
}