annotate 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
rev   line source
rlm@0 1 /**
rlm@0 2 * OpenAL cross platform audio library
rlm@0 3 * Copyright (C) 2011 by authors.
rlm@0 4 * This library is free software; you can redistribute it and/or
rlm@0 5 * modify it under the terms of the GNU Library General Public
rlm@0 6 * License as published by the Free Software Foundation; either
rlm@0 7 * version 2 of the License, or (at your option) any later version.
rlm@0 8 *
rlm@0 9 * This library is distributed in the hope that it will be useful,
rlm@0 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@0 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rlm@0 12 * Library General Public License for more details.
rlm@0 13 *
rlm@0 14 * You should have received a copy of the GNU Library General Public
rlm@0 15 * License along with this library; if not, write to the
rlm@0 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rlm@0 17 * Boston, MA 02111-1307, USA.
rlm@0 18 * Or go to http://www.gnu.org/copyleft/lgpl.html
rlm@0 19 */
rlm@0 20
rlm@0 21 #include "config.h"
rlm@0 22
rlm@0 23 #define COBJMACROS
rlm@0 24 #define _WIN32_WINNT 0x0500
rlm@0 25 #include <stdlib.h>
rlm@0 26 #include <stdio.h>
rlm@0 27 #include <memory.h>
rlm@0 28
rlm@0 29 #include <mmdeviceapi.h>
rlm@0 30 #include <audioclient.h>
rlm@0 31 #include <cguid.h>
rlm@0 32 #include <mmreg.h>
rlm@0 33 #ifndef _WAVEFORMATEXTENSIBLE_
rlm@0 34 #include <ks.h>
rlm@0 35 #include <ksmedia.h>
rlm@0 36 #endif
rlm@0 37
rlm@0 38 #include "alMain.h"
rlm@0 39 #include "AL/al.h"
rlm@0 40 #include "AL/alc.h"
rlm@0 41
rlm@0 42
rlm@0 43 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
rlm@0 44 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
rlm@0 45
rlm@0 46 #define MONO SPEAKER_FRONT_CENTER
rlm@0 47 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
rlm@0 48 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
rlm@0 49 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
rlm@0 50 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
rlm@0 51 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
rlm@0 52 #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)
rlm@0 53
rlm@0 54
rlm@0 55 typedef struct {
rlm@0 56 IMMDevice *mmdev;
rlm@0 57 IAudioClient *client;
rlm@0 58 HANDLE hNotifyEvent;
rlm@0 59
rlm@0 60 HANDLE MsgEvent;
rlm@0 61
rlm@0 62 volatile int killNow;
rlm@0 63 ALvoid *thread;
rlm@0 64 } MMDevApiData;
rlm@0 65
rlm@0 66
rlm@0 67 static const ALCchar mmDevice[] = "WASAPI Default";
rlm@0 68
rlm@0 69
rlm@0 70 static HANDLE ThreadHdl;
rlm@0 71 static DWORD ThreadID;
rlm@0 72
rlm@0 73 typedef struct {
rlm@0 74 HANDLE FinishedEvt;
rlm@0 75 HRESULT result;
rlm@0 76 } ThreadRequest;
rlm@0 77
rlm@0 78 #define WM_USER_OpenDevice (WM_USER+0)
rlm@0 79 #define WM_USER_ResetDevice (WM_USER+1)
rlm@0 80 #define WM_USER_StopDevice (WM_USER+2)
rlm@0 81 #define WM_USER_CloseDevice (WM_USER+3)
rlm@0 82
rlm@0 83 static HRESULT WaitForResponse(ThreadRequest *req)
rlm@0 84 {
rlm@0 85 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
rlm@0 86 return req->result;
rlm@0 87 ERR("Message response error: %lu\n", GetLastError());
rlm@0 88 return E_FAIL;
rlm@0 89 }
rlm@0 90
rlm@0 91
rlm@0 92 static ALuint MMDevApiProc(ALvoid *ptr)
rlm@0 93 {
rlm@0 94 ALCdevice *device = ptr;
rlm@0 95 MMDevApiData *data = device->ExtraData;
rlm@0 96 union {
rlm@0 97 IAudioRenderClient *iface;
rlm@0 98 void *ptr;
rlm@0 99 } render;
rlm@0 100 UINT32 written, len;
rlm@0 101 BYTE *buffer;
rlm@0 102 HRESULT hr;
rlm@0 103
rlm@0 104 hr = CoInitialize(NULL);
rlm@0 105 if(FAILED(hr))
rlm@0 106 {
rlm@0 107 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
rlm@0 108 aluHandleDisconnect(device);
rlm@0 109 return 0;
rlm@0 110 }
rlm@0 111
rlm@0 112 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
rlm@0 113 if(FAILED(hr))
rlm@0 114 {
rlm@0 115 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
rlm@0 116 aluHandleDisconnect(device);
rlm@0 117 return 0;
rlm@0 118 }
rlm@0 119
rlm@0 120 SetRTPriority();
rlm@0 121
rlm@0 122 while(!data->killNow)
rlm@0 123 {
rlm@0 124 hr = IAudioClient_GetCurrentPadding(data->client, &written);
rlm@0 125 if(FAILED(hr))
rlm@0 126 {
rlm@0 127 ERR("Failed to get padding: 0x%08lx\n", hr);
rlm@0 128 aluHandleDisconnect(device);
rlm@0 129 break;
rlm@0 130 }
rlm@0 131
rlm@0 132 len = device->UpdateSize*device->NumUpdates - written;
rlm@0 133 if(len < device->UpdateSize)
rlm@0 134 {
rlm@0 135 DWORD res;
rlm@0 136 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
rlm@0 137 if(res != WAIT_OBJECT_0)
rlm@0 138 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
rlm@0 139 continue;
rlm@0 140 }
rlm@0 141 len -= len%device->UpdateSize;
rlm@0 142
rlm@0 143 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
rlm@0 144 if(SUCCEEDED(hr))
rlm@0 145 {
rlm@0 146 aluMixData(device, buffer, len);
rlm@0 147 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
rlm@0 148 }
rlm@0 149 if(FAILED(hr))
rlm@0 150 {
rlm@0 151 ERR("Failed to buffer data: 0x%08lx\n", hr);
rlm@0 152 aluHandleDisconnect(device);
rlm@0 153 break;
rlm@0 154 }
rlm@0 155 }
rlm@0 156
rlm@0 157 IAudioRenderClient_Release(render.iface);
rlm@0 158
rlm@0 159 CoUninitialize();
rlm@0 160 return 0;
rlm@0 161 }
rlm@0 162
rlm@0 163
rlm@0 164 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
rlm@0 165 {
rlm@0 166 memset(out, 0, sizeof(*out));
rlm@0 167 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
rlm@0 168 *out = *(WAVEFORMATEXTENSIBLE*)in;
rlm@0 169 else if(in->wFormatTag == WAVE_FORMAT_PCM)
rlm@0 170 {
rlm@0 171 out->Format = *in;
rlm@0 172 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
rlm@0 173 out->Format.cbSize = sizeof(*out) - sizeof(*in);
rlm@0 174 if(out->Format.nChannels == 1)
rlm@0 175 out->dwChannelMask = MONO;
rlm@0 176 else if(out->Format.nChannels == 2)
rlm@0 177 out->dwChannelMask = STEREO;
rlm@0 178 else
rlm@0 179 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
rlm@0 180 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
rlm@0 181 }
rlm@0 182 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
rlm@0 183 {
rlm@0 184 out->Format = *in;
rlm@0 185 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
rlm@0 186 out->Format.cbSize = sizeof(*out) - sizeof(*in);
rlm@0 187 if(out->Format.nChannels == 1)
rlm@0 188 out->dwChannelMask = MONO;
rlm@0 189 else if(out->Format.nChannels == 2)
rlm@0 190 out->dwChannelMask = STEREO;
rlm@0 191 else
rlm@0 192 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
rlm@0 193 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
rlm@0 194 }
rlm@0 195 else
rlm@0 196 {
rlm@0 197 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
rlm@0 198 return ALC_FALSE;
rlm@0 199 }
rlm@0 200 return ALC_TRUE;
rlm@0 201 }
rlm@0 202
rlm@0 203 static HRESULT DoReset(ALCdevice *device)
rlm@0 204 {
rlm@0 205 MMDevApiData *data = device->ExtraData;
rlm@0 206 WAVEFORMATEXTENSIBLE OutputType;
rlm@0 207 WAVEFORMATEX *wfx = NULL;
rlm@0 208 REFERENCE_TIME min_per;
rlm@0 209 UINT32 buffer_len, min_len;
rlm@0 210 HRESULT hr;
rlm@0 211
rlm@0 212 hr = IAudioClient_GetMixFormat(data->client, &wfx);
rlm@0 213 if(FAILED(hr))
rlm@0 214 {
rlm@0 215 ERR("Failed to get mix format: 0x%08lx\n", hr);
rlm@0 216 return hr;
rlm@0 217 }
rlm@0 218
rlm@0 219 if(!MakeExtensible(&OutputType, wfx))
rlm@0 220 {
rlm@0 221 CoTaskMemFree(wfx);
rlm@0 222 return E_FAIL;
rlm@0 223 }
rlm@0 224 CoTaskMemFree(wfx);
rlm@0 225 wfx = NULL;
rlm@0 226
rlm@0 227 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 228 device->Frequency = OutputType.Format.nSamplesPerSec;
rlm@0 229 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 230 {
rlm@0 231 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
rlm@0 232 device->FmtChans = DevFmtMono;
rlm@0 233 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
rlm@0 234 device->FmtChans = DevFmtStereo;
rlm@0 235 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
rlm@0 236 device->FmtChans = DevFmtQuad;
rlm@0 237 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
rlm@0 238 device->FmtChans = DevFmtX51;
rlm@0 239 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
rlm@0 240 device->FmtChans = DevFmtX51Side;
rlm@0 241 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
rlm@0 242 device->FmtChans = DevFmtX61;
rlm@0 243 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
rlm@0 244 device->FmtChans = DevFmtX71;
rlm@0 245 else
rlm@0 246 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
rlm@0 247 }
rlm@0 248
rlm@0 249 switch(device->FmtChans)
rlm@0 250 {
rlm@0 251 case DevFmtMono:
rlm@0 252 OutputType.Format.nChannels = 1;
rlm@0 253 OutputType.dwChannelMask = MONO;
rlm@0 254 break;
rlm@0 255 case DevFmtStereo:
rlm@0 256 OutputType.Format.nChannels = 2;
rlm@0 257 OutputType.dwChannelMask = STEREO;
rlm@0 258 break;
rlm@0 259 case DevFmtQuad:
rlm@0 260 OutputType.Format.nChannels = 4;
rlm@0 261 OutputType.dwChannelMask = QUAD;
rlm@0 262 break;
rlm@0 263 case DevFmtX51:
rlm@0 264 OutputType.Format.nChannels = 6;
rlm@0 265 OutputType.dwChannelMask = X5DOT1;
rlm@0 266 break;
rlm@0 267 case DevFmtX51Side:
rlm@0 268 OutputType.Format.nChannels = 6;
rlm@0 269 OutputType.dwChannelMask = X5DOT1SIDE;
rlm@0 270 break;
rlm@0 271 case DevFmtX61:
rlm@0 272 OutputType.Format.nChannels = 7;
rlm@0 273 OutputType.dwChannelMask = X6DOT1;
rlm@0 274 break;
rlm@0 275 case DevFmtX71:
rlm@0 276 OutputType.Format.nChannels = 8;
rlm@0 277 OutputType.dwChannelMask = X7DOT1;
rlm@0 278 break;
rlm@0 279 }
rlm@0 280 switch(device->FmtType)
rlm@0 281 {
rlm@0 282 case DevFmtByte:
rlm@0 283 device->FmtType = DevFmtUByte;
rlm@0 284 /* fall-through */
rlm@0 285 case DevFmtUByte:
rlm@0 286 OutputType.Format.wBitsPerSample = 8;
rlm@0 287 OutputType.Samples.wValidBitsPerSample = 8;
rlm@0 288 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
rlm@0 289 break;
rlm@0 290 case DevFmtUShort:
rlm@0 291 device->FmtType = DevFmtShort;
rlm@0 292 /* fall-through */
rlm@0 293 case DevFmtShort:
rlm@0 294 OutputType.Format.wBitsPerSample = 16;
rlm@0 295 OutputType.Samples.wValidBitsPerSample = 16;
rlm@0 296 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
rlm@0 297 break;
rlm@0 298 case DevFmtFloat:
rlm@0 299 OutputType.Format.wBitsPerSample = 32;
rlm@0 300 OutputType.Samples.wValidBitsPerSample = 32;
rlm@0 301 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
rlm@0 302 break;
rlm@0 303 }
rlm@0 304 OutputType.Format.nSamplesPerSec = device->Frequency;
rlm@0 305
rlm@0 306 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
rlm@0 307 OutputType.Format.wBitsPerSample / 8;
rlm@0 308 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
rlm@0 309 OutputType.Format.nBlockAlign;
rlm@0 310
rlm@0 311 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
rlm@0 312 if(FAILED(hr))
rlm@0 313 {
rlm@0 314 ERR("Failed to check format support: 0x%08lx\n", hr);
rlm@0 315 hr = IAudioClient_GetMixFormat(data->client, &wfx);
rlm@0 316 }
rlm@0 317 if(FAILED(hr))
rlm@0 318 {
rlm@0 319 ERR("Failed to find a supported format: 0x%08lx\n", hr);
rlm@0 320 return hr;
rlm@0 321 }
rlm@0 322
rlm@0 323 if(wfx != NULL)
rlm@0 324 {
rlm@0 325 if(!MakeExtensible(&OutputType, wfx))
rlm@0 326 {
rlm@0 327 CoTaskMemFree(wfx);
rlm@0 328 return E_FAIL;
rlm@0 329 }
rlm@0 330 CoTaskMemFree(wfx);
rlm@0 331 wfx = NULL;
rlm@0 332
rlm@0 333 if(device->Frequency != OutputType.Format.nSamplesPerSec)
rlm@0 334 {
rlm@0 335 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 336 ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec);
rlm@0 337 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 338 device->Frequency = OutputType.Format.nSamplesPerSec;
rlm@0 339 }
rlm@0 340
rlm@0 341 if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) ||
rlm@0 342 (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) ||
rlm@0 343 (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) ||
rlm@0 344 (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) ||
rlm@0 345 (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) ||
rlm@0 346 (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) ||
rlm@0 347 (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)))
rlm@0 348 {
rlm@0 349 if((device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 350 ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask);
rlm@0 351 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
rlm@0 352
rlm@0 353 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
rlm@0 354 device->FmtChans = DevFmtMono;
rlm@0 355 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
rlm@0 356 device->FmtChans = DevFmtStereo;
rlm@0 357 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
rlm@0 358 device->FmtChans = DevFmtQuad;
rlm@0 359 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
rlm@0 360 device->FmtChans = DevFmtX51;
rlm@0 361 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
rlm@0 362 device->FmtChans = DevFmtX51Side;
rlm@0 363 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
rlm@0 364 device->FmtChans = DevFmtX61;
rlm@0 365 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
rlm@0 366 device->FmtChans = DevFmtX71;
rlm@0 367 else
rlm@0 368 {
rlm@0 369 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
rlm@0 370 device->FmtChans = DevFmtStereo;
rlm@0 371 OutputType.Format.nChannels = 2;
rlm@0 372 OutputType.dwChannelMask = STEREO;
rlm@0 373 }
rlm@0 374 }
rlm@0 375
rlm@0 376 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
rlm@0 377 {
rlm@0 378 if(OutputType.Samples.wValidBitsPerSample == 0)
rlm@0 379 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
rlm@0 380 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
rlm@0 381 !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) ||
rlm@0 382 (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16)))
rlm@0 383 {
rlm@0 384 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
rlm@0 385 if(OutputType.Format.wBitsPerSample == 8)
rlm@0 386 device->FmtType = DevFmtUByte;
rlm@0 387 else if(OutputType.Format.wBitsPerSample == 16)
rlm@0 388 device->FmtType = DevFmtShort;
rlm@0 389 else
rlm@0 390 {
rlm@0 391 device->FmtType = DevFmtShort;
rlm@0 392 OutputType.Format.wBitsPerSample = 16;
rlm@0 393 }
rlm@0 394 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
rlm@0 395 }
rlm@0 396 }
rlm@0 397 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
rlm@0 398 {
rlm@0 399 if(OutputType.Samples.wValidBitsPerSample == 0)
rlm@0 400 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
rlm@0 401 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
rlm@0 402 !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32)))
rlm@0 403 {
rlm@0 404 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
rlm@0 405 if(OutputType.Format.wBitsPerSample != 32)
rlm@0 406 {
rlm@0 407 device->FmtType = DevFmtFloat;
rlm@0 408 OutputType.Format.wBitsPerSample = 32;
rlm@0 409 }
rlm@0 410 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
rlm@0 411 }
rlm@0 412 }
rlm@0 413 else
rlm@0 414 {
rlm@0 415 ERR("Unhandled format sub-type\n");
rlm@0 416 device->FmtType = DevFmtShort;
rlm@0 417 OutputType.Format.wBitsPerSample = 16;
rlm@0 418 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
rlm@0 419 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
rlm@0 420 }
rlm@0 421 }
rlm@0 422
rlm@0 423 SetDefaultWFXChannelOrder(device);
rlm@0 424
rlm@0 425 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
rlm@0 426 if(SUCCEEDED(hr))
rlm@0 427 {
rlm@0 428 min_len = (min_per*device->Frequency + 10000000-1) / 10000000;
rlm@0 429 if(min_len < device->UpdateSize)
rlm@0 430 min_len *= (device->UpdateSize + min_len/2)/min_len;
rlm@0 431
rlm@0 432 device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) /
rlm@0 433 min_len;
rlm@0 434 device->NumUpdates = maxu(device->NumUpdates, 2);
rlm@0 435 device->UpdateSize = min_len;
rlm@0 436
rlm@0 437 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
rlm@0 438 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
rlm@0 439 ((REFERENCE_TIME)device->UpdateSize*
rlm@0 440 device->NumUpdates*10000000 +
rlm@0 441 device->Frequency-1) / device->Frequency,
rlm@0 442 0, &OutputType.Format, NULL);
rlm@0 443 }
rlm@0 444 if(FAILED(hr))
rlm@0 445 {
rlm@0 446 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
rlm@0 447 return hr;
rlm@0 448 }
rlm@0 449
rlm@0 450 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
rlm@0 451 if(FAILED(hr))
rlm@0 452 {
rlm@0 453 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
rlm@0 454 return hr;
rlm@0 455 }
rlm@0 456
rlm@0 457 device->NumUpdates = buffer_len / device->UpdateSize;
rlm@0 458 if(device->NumUpdates <= 1)
rlm@0 459 {
rlm@0 460 device->NumUpdates = 1;
rlm@0 461 ERR("Audio client returned buffer_len < period*2; expect break up\n");
rlm@0 462 }
rlm@0 463
rlm@0 464 ResetEvent(data->hNotifyEvent);
rlm@0 465 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
rlm@0 466 if(SUCCEEDED(hr))
rlm@0 467 hr = IAudioClient_Start(data->client);
rlm@0 468 if(FAILED(hr))
rlm@0 469 {
rlm@0 470 ERR("Failed to start audio client: 0x%08lx\n", hr);
rlm@0 471 return hr;
rlm@0 472 }
rlm@0 473
rlm@0 474 data->thread = StartThread(MMDevApiProc, device);
rlm@0 475 if(!data->thread)
rlm@0 476 {
rlm@0 477 IAudioClient_Stop(data->client);
rlm@0 478 ERR("Failed to start thread\n");
rlm@0 479 return E_FAIL;
rlm@0 480 }
rlm@0 481
rlm@0 482 return hr;
rlm@0 483 }
rlm@0 484
rlm@0 485
rlm@0 486 static DWORD CALLBACK MessageProc(void *ptr)
rlm@0 487 {
rlm@0 488 ThreadRequest *req = ptr;
rlm@0 489 IMMDeviceEnumerator *Enumerator;
rlm@0 490 MMDevApiData *data;
rlm@0 491 ALCdevice *device;
rlm@0 492 HRESULT hr;
rlm@0 493 MSG msg;
rlm@0 494
rlm@0 495 TRACE("Starting message thread\n");
rlm@0 496
rlm@0 497 hr = CoInitialize(NULL);
rlm@0 498 if(FAILED(hr))
rlm@0 499 {
rlm@0 500 WARN("Failed to initialize COM: 0x%08lx\n", hr);
rlm@0 501 req->result = hr;
rlm@0 502 SetEvent(req->FinishedEvt);
rlm@0 503 return 0;
rlm@0 504 }
rlm@0 505
rlm@0 506 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
rlm@0 507 if(FAILED(hr))
rlm@0 508 {
rlm@0 509 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
rlm@0 510 CoUninitialize();
rlm@0 511 req->result = hr;
rlm@0 512 SetEvent(req->FinishedEvt);
rlm@0 513 return 0;
rlm@0 514 }
rlm@0 515 Enumerator = ptr;
rlm@0 516 IMMDeviceEnumerator_Release(Enumerator);
rlm@0 517 Enumerator = NULL;
rlm@0 518
rlm@0 519 req->result = S_OK;
rlm@0 520 SetEvent(req->FinishedEvt);
rlm@0 521
rlm@0 522 TRACE("Starting message loop\n");
rlm@0 523 while(GetMessage(&msg, NULL, 0, 0))
rlm@0 524 {
rlm@0 525 TRACE("Got message %u\n", msg.message);
rlm@0 526 switch(msg.message)
rlm@0 527 {
rlm@0 528 case WM_USER_OpenDevice:
rlm@0 529 req = (ThreadRequest*)msg.wParam;
rlm@0 530 device = (ALCdevice*)msg.lParam;
rlm@0 531 data = device->ExtraData;
rlm@0 532
rlm@0 533 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
rlm@0 534 if(SUCCEEDED(hr))
rlm@0 535 {
rlm@0 536 Enumerator = ptr;
rlm@0 537 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
rlm@0 538 IMMDeviceEnumerator_Release(Enumerator);
rlm@0 539 Enumerator = NULL;
rlm@0 540 }
rlm@0 541 if(SUCCEEDED(hr))
rlm@0 542 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
rlm@0 543 if(SUCCEEDED(hr))
rlm@0 544 data->client = ptr;
rlm@0 545
rlm@0 546 if(FAILED(hr))
rlm@0 547 {
rlm@0 548 if(data->mmdev)
rlm@0 549 IMMDevice_Release(data->mmdev);
rlm@0 550 data->mmdev = NULL;
rlm@0 551 }
rlm@0 552
rlm@0 553 req->result = hr;
rlm@0 554 SetEvent(req->FinishedEvt);
rlm@0 555 continue;
rlm@0 556
rlm@0 557 case WM_USER_ResetDevice:
rlm@0 558 req = (ThreadRequest*)msg.wParam;
rlm@0 559 device = (ALCdevice*)msg.lParam;
rlm@0 560
rlm@0 561 req->result = DoReset(device);
rlm@0 562 SetEvent(req->FinishedEvt);
rlm@0 563 continue;
rlm@0 564
rlm@0 565 case WM_USER_StopDevice:
rlm@0 566 req = (ThreadRequest*)msg.wParam;
rlm@0 567 device = (ALCdevice*)msg.lParam;
rlm@0 568 data = device->ExtraData;
rlm@0 569
rlm@0 570 if(data->thread)
rlm@0 571 {
rlm@0 572 data->killNow = 1;
rlm@0 573 StopThread(data->thread);
rlm@0 574 data->thread = NULL;
rlm@0 575
rlm@0 576 data->killNow = 0;
rlm@0 577
rlm@0 578 IAudioClient_Stop(data->client);
rlm@0 579 }
rlm@0 580
rlm@0 581 req->result = S_OK;
rlm@0 582 SetEvent(req->FinishedEvt);
rlm@0 583 continue;
rlm@0 584
rlm@0 585 case WM_USER_CloseDevice:
rlm@0 586 req = (ThreadRequest*)msg.wParam;
rlm@0 587 device = (ALCdevice*)msg.lParam;
rlm@0 588 data = device->ExtraData;
rlm@0 589
rlm@0 590 IAudioClient_Release(data->client);
rlm@0 591 data->client = NULL;
rlm@0 592
rlm@0 593 IMMDevice_Release(data->mmdev);
rlm@0 594 data->mmdev = NULL;
rlm@0 595
rlm@0 596 req->result = S_OK;
rlm@0 597 SetEvent(req->FinishedEvt);
rlm@0 598 continue;
rlm@0 599
rlm@0 600 default:
rlm@0 601 ERR("Unexpected message: %u\n", msg.message);
rlm@0 602 continue;
rlm@0 603 }
rlm@0 604 }
rlm@0 605 TRACE("Message loop finished\n");
rlm@0 606
rlm@0 607 CoUninitialize();
rlm@0 608 return 0;
rlm@0 609 }
rlm@0 610
rlm@0 611
rlm@0 612 static BOOL MMDevApiLoad(void)
rlm@0 613 {
rlm@0 614 static HRESULT InitResult;
rlm@0 615 if(!ThreadHdl)
rlm@0 616 {
rlm@0 617 ThreadRequest req;
rlm@0 618 InitResult = E_FAIL;
rlm@0 619
rlm@0 620 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
rlm@0 621 if(req.FinishedEvt == NULL)
rlm@0 622 ERR("Failed to create event: %lu\n", GetLastError());
rlm@0 623 else
rlm@0 624 {
rlm@0 625 ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID);
rlm@0 626 if(ThreadHdl != NULL)
rlm@0 627 InitResult = WaitForResponse(&req);
rlm@0 628 CloseHandle(req.FinishedEvt);
rlm@0 629 }
rlm@0 630 }
rlm@0 631 return SUCCEEDED(InitResult);
rlm@0 632 }
rlm@0 633
rlm@0 634
rlm@0 635 static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
rlm@0 636 {
rlm@0 637 MMDevApiData *data = NULL;
rlm@0 638 HRESULT hr;
rlm@0 639
rlm@0 640 if(!deviceName)
rlm@0 641 deviceName = mmDevice;
rlm@0 642 else if(strcmp(deviceName, mmDevice) != 0)
rlm@0 643 return ALC_FALSE;
rlm@0 644
rlm@0 645 //Initialise requested device
rlm@0 646 data = calloc(1, sizeof(MMDevApiData));
rlm@0 647 if(!data)
rlm@0 648 {
rlm@0 649 alcSetError(device, ALC_OUT_OF_MEMORY);
rlm@0 650 return ALC_FALSE;
rlm@0 651 }
rlm@0 652 device->ExtraData = data;
rlm@0 653
rlm@0 654 hr = S_OK;
rlm@0 655 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
rlm@0 656 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
rlm@0 657 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
rlm@0 658 hr = E_FAIL;
rlm@0 659
rlm@0 660 if(SUCCEEDED(hr))
rlm@0 661 {
rlm@0 662 ThreadRequest req = { data->MsgEvent, 0 };
rlm@0 663
rlm@0 664 hr = E_FAIL;
rlm@0 665 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
rlm@0 666 hr = WaitForResponse(&req);
rlm@0 667 }
rlm@0 668
rlm@0 669 if(FAILED(hr))
rlm@0 670 {
rlm@0 671 if(data->hNotifyEvent != NULL)
rlm@0 672 CloseHandle(data->hNotifyEvent);
rlm@0 673 data->hNotifyEvent = NULL;
rlm@0 674 if(data->MsgEvent != NULL)
rlm@0 675 CloseHandle(data->MsgEvent);
rlm@0 676 data->MsgEvent = NULL;
rlm@0 677
rlm@0 678 free(data);
rlm@0 679 device->ExtraData = NULL;
rlm@0 680
rlm@0 681 ERR("Device init failed: 0x%08lx\n", hr);
rlm@0 682 return ALC_FALSE;
rlm@0 683 }
rlm@0 684
rlm@0 685 device->szDeviceName = strdup(deviceName);
rlm@0 686 return ALC_TRUE;
rlm@0 687 }
rlm@0 688
rlm@0 689 static void MMDevApiClosePlayback(ALCdevice *device)
rlm@0 690 {
rlm@0 691 MMDevApiData *data = device->ExtraData;
rlm@0 692 ThreadRequest req = { data->MsgEvent, 0 };
rlm@0 693
rlm@0 694 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
rlm@0 695 (void)WaitForResponse(&req);
rlm@0 696
rlm@0 697 CloseHandle(data->MsgEvent);
rlm@0 698 data->MsgEvent = NULL;
rlm@0 699
rlm@0 700 CloseHandle(data->hNotifyEvent);
rlm@0 701 data->hNotifyEvent = NULL;
rlm@0 702
rlm@0 703 free(data);
rlm@0 704 device->ExtraData = NULL;
rlm@0 705 }
rlm@0 706
rlm@0 707 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
rlm@0 708 {
rlm@0 709 MMDevApiData *data = device->ExtraData;
rlm@0 710 ThreadRequest req = { data->MsgEvent, 0 };
rlm@0 711 HRESULT hr = E_FAIL;
rlm@0 712
rlm@0 713 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
rlm@0 714 hr = WaitForResponse(&req);
rlm@0 715
rlm@0 716 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
rlm@0 717 }
rlm@0 718
rlm@0 719 static void MMDevApiStopPlayback(ALCdevice *device)
rlm@0 720 {
rlm@0 721 MMDevApiData *data = device->ExtraData;
rlm@0 722 ThreadRequest req = { data->MsgEvent, 0 };
rlm@0 723
rlm@0 724 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
rlm@0 725 (void)WaitForResponse(&req);
rlm@0 726 }
rlm@0 727
rlm@0 728
rlm@0 729 static const BackendFuncs MMDevApiFuncs = {
rlm@0 730 MMDevApiOpenPlayback,
rlm@0 731 MMDevApiClosePlayback,
rlm@0 732 MMDevApiResetPlayback,
rlm@0 733 MMDevApiStopPlayback,
rlm@0 734 NULL,
rlm@0 735 NULL,
rlm@0 736 NULL,
rlm@0 737 NULL,
rlm@0 738 NULL,
rlm@0 739 NULL
rlm@0 740 };
rlm@0 741
rlm@0 742
rlm@0 743 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
rlm@0 744 {
rlm@0 745 if(!MMDevApiLoad())
rlm@0 746 return ALC_FALSE;
rlm@0 747 *FuncList = MMDevApiFuncs;
rlm@0 748 return ALC_TRUE;
rlm@0 749 }
rlm@0 750
rlm@0 751 void alcMMDevApiDeinit(void)
rlm@0 752 {
rlm@0 753 if(ThreadHdl)
rlm@0 754 {
rlm@0 755 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
rlm@0 756 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
rlm@0 757 CloseHandle(ThreadHdl);
rlm@0 758 ThreadHdl = NULL;
rlm@0 759 }
rlm@0 760 }
rlm@0 761
rlm@0 762 void alcMMDevApiProbe(enum DevProbe type)
rlm@0 763 {
rlm@0 764 switch(type)
rlm@0 765 {
rlm@0 766 case DEVICE_PROBE:
rlm@0 767 AppendDeviceList(mmDevice);
rlm@0 768 break;
rlm@0 769 case ALL_DEVICE_PROBE:
rlm@0 770 AppendAllDeviceList(mmDevice);
rlm@0 771 break;
rlm@0 772 case CAPTURE_DEVICE_PROBE:
rlm@0 773 break;
rlm@0 774 }
rlm@0 775 }