Mercurial > audio-send
diff Alc/backends/mmdevapi.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alc/backends/mmdevapi.c Tue Oct 25 13:02:31 2011 -0700 1.3 @@ -0,0 +1,775 @@ 1.4 +/** 1.5 + * OpenAL cross platform audio library 1.6 + * Copyright (C) 2011 by authors. 1.7 + * This library is free software; you can redistribute it and/or 1.8 + * modify it under the terms of the GNU Library General Public 1.9 + * License as published by the Free Software Foundation; either 1.10 + * version 2 of the License, or (at your option) any later version. 1.11 + * 1.12 + * This library is distributed in the hope that it will be useful, 1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.15 + * Library General Public License for more details. 1.16 + * 1.17 + * You should have received a copy of the GNU Library General Public 1.18 + * License along with this library; if not, write to the 1.19 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 1.20 + * Boston, MA 02111-1307, USA. 1.21 + * Or go to http://www.gnu.org/copyleft/lgpl.html 1.22 + */ 1.23 + 1.24 +#include "config.h" 1.25 + 1.26 +#define COBJMACROS 1.27 +#define _WIN32_WINNT 0x0500 1.28 +#include <stdlib.h> 1.29 +#include <stdio.h> 1.30 +#include <memory.h> 1.31 + 1.32 +#include <mmdeviceapi.h> 1.33 +#include <audioclient.h> 1.34 +#include <cguid.h> 1.35 +#include <mmreg.h> 1.36 +#ifndef _WAVEFORMATEXTENSIBLE_ 1.37 +#include <ks.h> 1.38 +#include <ksmedia.h> 1.39 +#endif 1.40 + 1.41 +#include "alMain.h" 1.42 +#include "AL/al.h" 1.43 +#include "AL/alc.h" 1.44 + 1.45 + 1.46 +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 1.47 +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 1.48 + 1.49 +#define MONO SPEAKER_FRONT_CENTER 1.50 +#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) 1.51 +#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) 1.52 +#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) 1.53 +#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) 1.54 +#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) 1.55 +#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) 1.56 + 1.57 + 1.58 +typedef struct { 1.59 + IMMDevice *mmdev; 1.60 + IAudioClient *client; 1.61 + HANDLE hNotifyEvent; 1.62 + 1.63 + HANDLE MsgEvent; 1.64 + 1.65 + volatile int killNow; 1.66 + ALvoid *thread; 1.67 +} MMDevApiData; 1.68 + 1.69 + 1.70 +static const ALCchar mmDevice[] = "WASAPI Default"; 1.71 + 1.72 + 1.73 +static HANDLE ThreadHdl; 1.74 +static DWORD ThreadID; 1.75 + 1.76 +typedef struct { 1.77 + HANDLE FinishedEvt; 1.78 + HRESULT result; 1.79 +} ThreadRequest; 1.80 + 1.81 +#define WM_USER_OpenDevice (WM_USER+0) 1.82 +#define WM_USER_ResetDevice (WM_USER+1) 1.83 +#define WM_USER_StopDevice (WM_USER+2) 1.84 +#define WM_USER_CloseDevice (WM_USER+3) 1.85 + 1.86 +static HRESULT WaitForResponse(ThreadRequest *req) 1.87 +{ 1.88 + if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) 1.89 + return req->result; 1.90 + ERR("Message response error: %lu\n", GetLastError()); 1.91 + return E_FAIL; 1.92 +} 1.93 + 1.94 + 1.95 +static ALuint MMDevApiProc(ALvoid *ptr) 1.96 +{ 1.97 + ALCdevice *device = ptr; 1.98 + MMDevApiData *data = device->ExtraData; 1.99 + union { 1.100 + IAudioRenderClient *iface; 1.101 + void *ptr; 1.102 + } render; 1.103 + UINT32 written, len; 1.104 + BYTE *buffer; 1.105 + HRESULT hr; 1.106 + 1.107 + hr = CoInitialize(NULL); 1.108 + if(FAILED(hr)) 1.109 + { 1.110 + ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); 1.111 + aluHandleDisconnect(device); 1.112 + return 0; 1.113 + } 1.114 + 1.115 + hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); 1.116 + if(FAILED(hr)) 1.117 + { 1.118 + ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr); 1.119 + aluHandleDisconnect(device); 1.120 + return 0; 1.121 + } 1.122 + 1.123 + SetRTPriority(); 1.124 + 1.125 + while(!data->killNow) 1.126 + { 1.127 + hr = IAudioClient_GetCurrentPadding(data->client, &written); 1.128 + if(FAILED(hr)) 1.129 + { 1.130 + ERR("Failed to get padding: 0x%08lx\n", hr); 1.131 + aluHandleDisconnect(device); 1.132 + break; 1.133 + } 1.134 + 1.135 + len = device->UpdateSize*device->NumUpdates - written; 1.136 + if(len < device->UpdateSize) 1.137 + { 1.138 + DWORD res; 1.139 + res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE); 1.140 + if(res != WAIT_OBJECT_0) 1.141 + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); 1.142 + continue; 1.143 + } 1.144 + len -= len%device->UpdateSize; 1.145 + 1.146 + hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer); 1.147 + if(SUCCEEDED(hr)) 1.148 + { 1.149 + aluMixData(device, buffer, len); 1.150 + hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0); 1.151 + } 1.152 + if(FAILED(hr)) 1.153 + { 1.154 + ERR("Failed to buffer data: 0x%08lx\n", hr); 1.155 + aluHandleDisconnect(device); 1.156 + break; 1.157 + } 1.158 + } 1.159 + 1.160 + IAudioRenderClient_Release(render.iface); 1.161 + 1.162 + CoUninitialize(); 1.163 + return 0; 1.164 +} 1.165 + 1.166 + 1.167 +static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) 1.168 +{ 1.169 + memset(out, 0, sizeof(*out)); 1.170 + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) 1.171 + *out = *(WAVEFORMATEXTENSIBLE*)in; 1.172 + else if(in->wFormatTag == WAVE_FORMAT_PCM) 1.173 + { 1.174 + out->Format = *in; 1.175 + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 1.176 + out->Format.cbSize = sizeof(*out) - sizeof(*in); 1.177 + if(out->Format.nChannels == 1) 1.178 + out->dwChannelMask = MONO; 1.179 + else if(out->Format.nChannels == 2) 1.180 + out->dwChannelMask = STEREO; 1.181 + else 1.182 + ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); 1.183 + out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 1.184 + } 1.185 + else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) 1.186 + { 1.187 + out->Format = *in; 1.188 + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 1.189 + out->Format.cbSize = sizeof(*out) - sizeof(*in); 1.190 + if(out->Format.nChannels == 1) 1.191 + out->dwChannelMask = MONO; 1.192 + else if(out->Format.nChannels == 2) 1.193 + out->dwChannelMask = STEREO; 1.194 + else 1.195 + ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); 1.196 + out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; 1.197 + } 1.198 + else 1.199 + { 1.200 + ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); 1.201 + return ALC_FALSE; 1.202 + } 1.203 + return ALC_TRUE; 1.204 +} 1.205 + 1.206 +static HRESULT DoReset(ALCdevice *device) 1.207 +{ 1.208 + MMDevApiData *data = device->ExtraData; 1.209 + WAVEFORMATEXTENSIBLE OutputType; 1.210 + WAVEFORMATEX *wfx = NULL; 1.211 + REFERENCE_TIME min_per; 1.212 + UINT32 buffer_len, min_len; 1.213 + HRESULT hr; 1.214 + 1.215 + hr = IAudioClient_GetMixFormat(data->client, &wfx); 1.216 + if(FAILED(hr)) 1.217 + { 1.218 + ERR("Failed to get mix format: 0x%08lx\n", hr); 1.219 + return hr; 1.220 + } 1.221 + 1.222 + if(!MakeExtensible(&OutputType, wfx)) 1.223 + { 1.224 + CoTaskMemFree(wfx); 1.225 + return E_FAIL; 1.226 + } 1.227 + CoTaskMemFree(wfx); 1.228 + wfx = NULL; 1.229 + 1.230 + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) 1.231 + device->Frequency = OutputType.Format.nSamplesPerSec; 1.232 + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) 1.233 + { 1.234 + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) 1.235 + device->FmtChans = DevFmtMono; 1.236 + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) 1.237 + device->FmtChans = DevFmtStereo; 1.238 + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) 1.239 + device->FmtChans = DevFmtQuad; 1.240 + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) 1.241 + device->FmtChans = DevFmtX51; 1.242 + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) 1.243 + device->FmtChans = DevFmtX51Side; 1.244 + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) 1.245 + device->FmtChans = DevFmtX61; 1.246 + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) 1.247 + device->FmtChans = DevFmtX71; 1.248 + else 1.249 + ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); 1.250 + } 1.251 + 1.252 + switch(device->FmtChans) 1.253 + { 1.254 + case DevFmtMono: 1.255 + OutputType.Format.nChannels = 1; 1.256 + OutputType.dwChannelMask = MONO; 1.257 + break; 1.258 + case DevFmtStereo: 1.259 + OutputType.Format.nChannels = 2; 1.260 + OutputType.dwChannelMask = STEREO; 1.261 + break; 1.262 + case DevFmtQuad: 1.263 + OutputType.Format.nChannels = 4; 1.264 + OutputType.dwChannelMask = QUAD; 1.265 + break; 1.266 + case DevFmtX51: 1.267 + OutputType.Format.nChannels = 6; 1.268 + OutputType.dwChannelMask = X5DOT1; 1.269 + break; 1.270 + case DevFmtX51Side: 1.271 + OutputType.Format.nChannels = 6; 1.272 + OutputType.dwChannelMask = X5DOT1SIDE; 1.273 + break; 1.274 + case DevFmtX61: 1.275 + OutputType.Format.nChannels = 7; 1.276 + OutputType.dwChannelMask = X6DOT1; 1.277 + break; 1.278 + case DevFmtX71: 1.279 + OutputType.Format.nChannels = 8; 1.280 + OutputType.dwChannelMask = X7DOT1; 1.281 + break; 1.282 + } 1.283 + switch(device->FmtType) 1.284 + { 1.285 + case DevFmtByte: 1.286 + device->FmtType = DevFmtUByte; 1.287 + /* fall-through */ 1.288 + case DevFmtUByte: 1.289 + OutputType.Format.wBitsPerSample = 8; 1.290 + OutputType.Samples.wValidBitsPerSample = 8; 1.291 + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 1.292 + break; 1.293 + case DevFmtUShort: 1.294 + device->FmtType = DevFmtShort; 1.295 + /* fall-through */ 1.296 + case DevFmtShort: 1.297 + OutputType.Format.wBitsPerSample = 16; 1.298 + OutputType.Samples.wValidBitsPerSample = 16; 1.299 + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 1.300 + break; 1.301 + case DevFmtFloat: 1.302 + OutputType.Format.wBitsPerSample = 32; 1.303 + OutputType.Samples.wValidBitsPerSample = 32; 1.304 + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; 1.305 + break; 1.306 + } 1.307 + OutputType.Format.nSamplesPerSec = device->Frequency; 1.308 + 1.309 + OutputType.Format.nBlockAlign = OutputType.Format.nChannels * 1.310 + OutputType.Format.wBitsPerSample / 8; 1.311 + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * 1.312 + OutputType.Format.nBlockAlign; 1.313 + 1.314 + hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); 1.315 + if(FAILED(hr)) 1.316 + { 1.317 + ERR("Failed to check format support: 0x%08lx\n", hr); 1.318 + hr = IAudioClient_GetMixFormat(data->client, &wfx); 1.319 + } 1.320 + if(FAILED(hr)) 1.321 + { 1.322 + ERR("Failed to find a supported format: 0x%08lx\n", hr); 1.323 + return hr; 1.324 + } 1.325 + 1.326 + if(wfx != NULL) 1.327 + { 1.328 + if(!MakeExtensible(&OutputType, wfx)) 1.329 + { 1.330 + CoTaskMemFree(wfx); 1.331 + return E_FAIL; 1.332 + } 1.333 + CoTaskMemFree(wfx); 1.334 + wfx = NULL; 1.335 + 1.336 + if(device->Frequency != OutputType.Format.nSamplesPerSec) 1.337 + { 1.338 + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) 1.339 + ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec); 1.340 + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; 1.341 + device->Frequency = OutputType.Format.nSamplesPerSec; 1.342 + } 1.343 + 1.344 + if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) || 1.345 + (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) || 1.346 + (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) || 1.347 + (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) || 1.348 + (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) || 1.349 + (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) || 1.350 + (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1))) 1.351 + { 1.352 + if((device->Flags&DEVICE_CHANNELS_REQUEST)) 1.353 + ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask); 1.354 + device->Flags &= ~DEVICE_CHANNELS_REQUEST; 1.355 + 1.356 + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) 1.357 + device->FmtChans = DevFmtMono; 1.358 + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) 1.359 + device->FmtChans = DevFmtStereo; 1.360 + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) 1.361 + device->FmtChans = DevFmtQuad; 1.362 + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) 1.363 + device->FmtChans = DevFmtX51; 1.364 + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) 1.365 + device->FmtChans = DevFmtX51Side; 1.366 + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) 1.367 + device->FmtChans = DevFmtX61; 1.368 + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) 1.369 + device->FmtChans = DevFmtX71; 1.370 + else 1.371 + { 1.372 + ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); 1.373 + device->FmtChans = DevFmtStereo; 1.374 + OutputType.Format.nChannels = 2; 1.375 + OutputType.dwChannelMask = STEREO; 1.376 + } 1.377 + } 1.378 + 1.379 + if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) 1.380 + { 1.381 + if(OutputType.Samples.wValidBitsPerSample == 0) 1.382 + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; 1.383 + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || 1.384 + !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) || 1.385 + (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16))) 1.386 + { 1.387 + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); 1.388 + if(OutputType.Format.wBitsPerSample == 8) 1.389 + device->FmtType = DevFmtUByte; 1.390 + else if(OutputType.Format.wBitsPerSample == 16) 1.391 + device->FmtType = DevFmtShort; 1.392 + else 1.393 + { 1.394 + device->FmtType = DevFmtShort; 1.395 + OutputType.Format.wBitsPerSample = 16; 1.396 + } 1.397 + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; 1.398 + } 1.399 + } 1.400 + else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) 1.401 + { 1.402 + if(OutputType.Samples.wValidBitsPerSample == 0) 1.403 + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; 1.404 + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || 1.405 + !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32))) 1.406 + { 1.407 + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); 1.408 + if(OutputType.Format.wBitsPerSample != 32) 1.409 + { 1.410 + device->FmtType = DevFmtFloat; 1.411 + OutputType.Format.wBitsPerSample = 32; 1.412 + } 1.413 + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; 1.414 + } 1.415 + } 1.416 + else 1.417 + { 1.418 + ERR("Unhandled format sub-type\n"); 1.419 + device->FmtType = DevFmtShort; 1.420 + OutputType.Format.wBitsPerSample = 16; 1.421 + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; 1.422 + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 1.423 + } 1.424 + } 1.425 + 1.426 + SetDefaultWFXChannelOrder(device); 1.427 + 1.428 + hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL); 1.429 + if(SUCCEEDED(hr)) 1.430 + { 1.431 + min_len = (min_per*device->Frequency + 10000000-1) / 10000000; 1.432 + if(min_len < device->UpdateSize) 1.433 + min_len *= (device->UpdateSize + min_len/2)/min_len; 1.434 + 1.435 + device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) / 1.436 + min_len; 1.437 + device->NumUpdates = maxu(device->NumUpdates, 2); 1.438 + device->UpdateSize = min_len; 1.439 + 1.440 + hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, 1.441 + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 1.442 + ((REFERENCE_TIME)device->UpdateSize* 1.443 + device->NumUpdates*10000000 + 1.444 + device->Frequency-1) / device->Frequency, 1.445 + 0, &OutputType.Format, NULL); 1.446 + } 1.447 + if(FAILED(hr)) 1.448 + { 1.449 + ERR("Failed to initialize audio client: 0x%08lx\n", hr); 1.450 + return hr; 1.451 + } 1.452 + 1.453 + hr = IAudioClient_GetBufferSize(data->client, &buffer_len); 1.454 + if(FAILED(hr)) 1.455 + { 1.456 + ERR("Failed to get audio buffer info: 0x%08lx\n", hr); 1.457 + return hr; 1.458 + } 1.459 + 1.460 + device->NumUpdates = buffer_len / device->UpdateSize; 1.461 + if(device->NumUpdates <= 1) 1.462 + { 1.463 + device->NumUpdates = 1; 1.464 + ERR("Audio client returned buffer_len < period*2; expect break up\n"); 1.465 + } 1.466 + 1.467 + ResetEvent(data->hNotifyEvent); 1.468 + hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent); 1.469 + if(SUCCEEDED(hr)) 1.470 + hr = IAudioClient_Start(data->client); 1.471 + if(FAILED(hr)) 1.472 + { 1.473 + ERR("Failed to start audio client: 0x%08lx\n", hr); 1.474 + return hr; 1.475 + } 1.476 + 1.477 + data->thread = StartThread(MMDevApiProc, device); 1.478 + if(!data->thread) 1.479 + { 1.480 + IAudioClient_Stop(data->client); 1.481 + ERR("Failed to start thread\n"); 1.482 + return E_FAIL; 1.483 + } 1.484 + 1.485 + return hr; 1.486 +} 1.487 + 1.488 + 1.489 +static DWORD CALLBACK MessageProc(void *ptr) 1.490 +{ 1.491 + ThreadRequest *req = ptr; 1.492 + IMMDeviceEnumerator *Enumerator; 1.493 + MMDevApiData *data; 1.494 + ALCdevice *device; 1.495 + HRESULT hr; 1.496 + MSG msg; 1.497 + 1.498 + TRACE("Starting message thread\n"); 1.499 + 1.500 + hr = CoInitialize(NULL); 1.501 + if(FAILED(hr)) 1.502 + { 1.503 + WARN("Failed to initialize COM: 0x%08lx\n", hr); 1.504 + req->result = hr; 1.505 + SetEvent(req->FinishedEvt); 1.506 + return 0; 1.507 + } 1.508 + 1.509 + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); 1.510 + if(FAILED(hr)) 1.511 + { 1.512 + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); 1.513 + CoUninitialize(); 1.514 + req->result = hr; 1.515 + SetEvent(req->FinishedEvt); 1.516 + return 0; 1.517 + } 1.518 + Enumerator = ptr; 1.519 + IMMDeviceEnumerator_Release(Enumerator); 1.520 + Enumerator = NULL; 1.521 + 1.522 + req->result = S_OK; 1.523 + SetEvent(req->FinishedEvt); 1.524 + 1.525 + TRACE("Starting message loop\n"); 1.526 + while(GetMessage(&msg, NULL, 0, 0)) 1.527 + { 1.528 + TRACE("Got message %u\n", msg.message); 1.529 + switch(msg.message) 1.530 + { 1.531 + case WM_USER_OpenDevice: 1.532 + req = (ThreadRequest*)msg.wParam; 1.533 + device = (ALCdevice*)msg.lParam; 1.534 + data = device->ExtraData; 1.535 + 1.536 + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); 1.537 + if(SUCCEEDED(hr)) 1.538 + { 1.539 + Enumerator = ptr; 1.540 + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); 1.541 + IMMDeviceEnumerator_Release(Enumerator); 1.542 + Enumerator = NULL; 1.543 + } 1.544 + if(SUCCEEDED(hr)) 1.545 + hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); 1.546 + if(SUCCEEDED(hr)) 1.547 + data->client = ptr; 1.548 + 1.549 + if(FAILED(hr)) 1.550 + { 1.551 + if(data->mmdev) 1.552 + IMMDevice_Release(data->mmdev); 1.553 + data->mmdev = NULL; 1.554 + } 1.555 + 1.556 + req->result = hr; 1.557 + SetEvent(req->FinishedEvt); 1.558 + continue; 1.559 + 1.560 + case WM_USER_ResetDevice: 1.561 + req = (ThreadRequest*)msg.wParam; 1.562 + device = (ALCdevice*)msg.lParam; 1.563 + 1.564 + req->result = DoReset(device); 1.565 + SetEvent(req->FinishedEvt); 1.566 + continue; 1.567 + 1.568 + case WM_USER_StopDevice: 1.569 + req = (ThreadRequest*)msg.wParam; 1.570 + device = (ALCdevice*)msg.lParam; 1.571 + data = device->ExtraData; 1.572 + 1.573 + if(data->thread) 1.574 + { 1.575 + data->killNow = 1; 1.576 + StopThread(data->thread); 1.577 + data->thread = NULL; 1.578 + 1.579 + data->killNow = 0; 1.580 + 1.581 + IAudioClient_Stop(data->client); 1.582 + } 1.583 + 1.584 + req->result = S_OK; 1.585 + SetEvent(req->FinishedEvt); 1.586 + continue; 1.587 + 1.588 + case WM_USER_CloseDevice: 1.589 + req = (ThreadRequest*)msg.wParam; 1.590 + device = (ALCdevice*)msg.lParam; 1.591 + data = device->ExtraData; 1.592 + 1.593 + IAudioClient_Release(data->client); 1.594 + data->client = NULL; 1.595 + 1.596 + IMMDevice_Release(data->mmdev); 1.597 + data->mmdev = NULL; 1.598 + 1.599 + req->result = S_OK; 1.600 + SetEvent(req->FinishedEvt); 1.601 + continue; 1.602 + 1.603 + default: 1.604 + ERR("Unexpected message: %u\n", msg.message); 1.605 + continue; 1.606 + } 1.607 + } 1.608 + TRACE("Message loop finished\n"); 1.609 + 1.610 + CoUninitialize(); 1.611 + return 0; 1.612 +} 1.613 + 1.614 + 1.615 +static BOOL MMDevApiLoad(void) 1.616 +{ 1.617 + static HRESULT InitResult; 1.618 + if(!ThreadHdl) 1.619 + { 1.620 + ThreadRequest req; 1.621 + InitResult = E_FAIL; 1.622 + 1.623 + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); 1.624 + if(req.FinishedEvt == NULL) 1.625 + ERR("Failed to create event: %lu\n", GetLastError()); 1.626 + else 1.627 + { 1.628 + ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID); 1.629 + if(ThreadHdl != NULL) 1.630 + InitResult = WaitForResponse(&req); 1.631 + CloseHandle(req.FinishedEvt); 1.632 + } 1.633 + } 1.634 + return SUCCEEDED(InitResult); 1.635 +} 1.636 + 1.637 + 1.638 +static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) 1.639 +{ 1.640 + MMDevApiData *data = NULL; 1.641 + HRESULT hr; 1.642 + 1.643 + if(!deviceName) 1.644 + deviceName = mmDevice; 1.645 + else if(strcmp(deviceName, mmDevice) != 0) 1.646 + return ALC_FALSE; 1.647 + 1.648 + //Initialise requested device 1.649 + data = calloc(1, sizeof(MMDevApiData)); 1.650 + if(!data) 1.651 + { 1.652 + alcSetError(device, ALC_OUT_OF_MEMORY); 1.653 + return ALC_FALSE; 1.654 + } 1.655 + device->ExtraData = data; 1.656 + 1.657 + hr = S_OK; 1.658 + data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 1.659 + data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 1.660 + if(data->hNotifyEvent == NULL || data->MsgEvent == NULL) 1.661 + hr = E_FAIL; 1.662 + 1.663 + if(SUCCEEDED(hr)) 1.664 + { 1.665 + ThreadRequest req = { data->MsgEvent, 0 }; 1.666 + 1.667 + hr = E_FAIL; 1.668 + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) 1.669 + hr = WaitForResponse(&req); 1.670 + } 1.671 + 1.672 + if(FAILED(hr)) 1.673 + { 1.674 + if(data->hNotifyEvent != NULL) 1.675 + CloseHandle(data->hNotifyEvent); 1.676 + data->hNotifyEvent = NULL; 1.677 + if(data->MsgEvent != NULL) 1.678 + CloseHandle(data->MsgEvent); 1.679 + data->MsgEvent = NULL; 1.680 + 1.681 + free(data); 1.682 + device->ExtraData = NULL; 1.683 + 1.684 + ERR("Device init failed: 0x%08lx\n", hr); 1.685 + return ALC_FALSE; 1.686 + } 1.687 + 1.688 + device->szDeviceName = strdup(deviceName); 1.689 + return ALC_TRUE; 1.690 +} 1.691 + 1.692 +static void MMDevApiClosePlayback(ALCdevice *device) 1.693 +{ 1.694 + MMDevApiData *data = device->ExtraData; 1.695 + ThreadRequest req = { data->MsgEvent, 0 }; 1.696 + 1.697 + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) 1.698 + (void)WaitForResponse(&req); 1.699 + 1.700 + CloseHandle(data->MsgEvent); 1.701 + data->MsgEvent = NULL; 1.702 + 1.703 + CloseHandle(data->hNotifyEvent); 1.704 + data->hNotifyEvent = NULL; 1.705 + 1.706 + free(data); 1.707 + device->ExtraData = NULL; 1.708 +} 1.709 + 1.710 +static ALCboolean MMDevApiResetPlayback(ALCdevice *device) 1.711 +{ 1.712 + MMDevApiData *data = device->ExtraData; 1.713 + ThreadRequest req = { data->MsgEvent, 0 }; 1.714 + HRESULT hr = E_FAIL; 1.715 + 1.716 + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) 1.717 + hr = WaitForResponse(&req); 1.718 + 1.719 + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; 1.720 +} 1.721 + 1.722 +static void MMDevApiStopPlayback(ALCdevice *device) 1.723 +{ 1.724 + MMDevApiData *data = device->ExtraData; 1.725 + ThreadRequest req = { data->MsgEvent, 0 }; 1.726 + 1.727 + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) 1.728 + (void)WaitForResponse(&req); 1.729 +} 1.730 + 1.731 + 1.732 +static const BackendFuncs MMDevApiFuncs = { 1.733 + MMDevApiOpenPlayback, 1.734 + MMDevApiClosePlayback, 1.735 + MMDevApiResetPlayback, 1.736 + MMDevApiStopPlayback, 1.737 + NULL, 1.738 + NULL, 1.739 + NULL, 1.740 + NULL, 1.741 + NULL, 1.742 + NULL 1.743 +}; 1.744 + 1.745 + 1.746 +ALCboolean alcMMDevApiInit(BackendFuncs *FuncList) 1.747 +{ 1.748 + if(!MMDevApiLoad()) 1.749 + return ALC_FALSE; 1.750 + *FuncList = MMDevApiFuncs; 1.751 + return ALC_TRUE; 1.752 +} 1.753 + 1.754 +void alcMMDevApiDeinit(void) 1.755 +{ 1.756 + if(ThreadHdl) 1.757 + { 1.758 + TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); 1.759 + PostThreadMessage(ThreadID, WM_QUIT, 0, 0); 1.760 + CloseHandle(ThreadHdl); 1.761 + ThreadHdl = NULL; 1.762 + } 1.763 +} 1.764 + 1.765 +void alcMMDevApiProbe(enum DevProbe type) 1.766 +{ 1.767 + switch(type) 1.768 + { 1.769 + case DEVICE_PROBE: 1.770 + AppendDeviceList(mmDevice); 1.771 + break; 1.772 + case ALL_DEVICE_PROBE: 1.773 + AppendAllDeviceList(mmDevice); 1.774 + break; 1.775 + case CAPTURE_DEVICE_PROBE: 1.776 + break; 1.777 + } 1.778 +}