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