2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * 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.html
24 #define _WIN32_WINNT 0x0500
29 #include <mmdeviceapi.h>
30 #include <audioclient.h>
33 #ifndef _WAVEFORMATEXTENSIBLE_
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_CENTER
47 #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 X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
51 #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)
54 static IMMDeviceEnumerator
*Enumerator
= NULL
;
66 static const ALCchar mmDevice
[] = "WASAPI Default";
69 static void *MMDevApiLoad(void)
74 HRESULT hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
76 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
79 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &mme
);
90 static ALuint
MMDevApiProc(ALvoid
*ptr
)
92 ALCdevice
*device
= ptr
;
93 MMDevApiData
*data
= device
->ExtraData
;
95 IAudioRenderClient
*iface
;
102 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &render
.ptr
);
105 AL_PRINT("Failed to get AudioRenderClient service: 0x%08lx\n", hr
);
106 aluHandleDisconnect(device
);
112 while(!data
->killNow
)
114 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
117 AL_PRINT("Failed to get padding: 0x%08lx\n", hr
);
118 aluHandleDisconnect(device
);
122 len
= device
->UpdateSize
*device
->NumUpdates
- written
;
123 if(len
< device
->UpdateSize
)
128 len
-= len
%device
->UpdateSize
;
130 hr
= IAudioRenderClient_GetBuffer(render
.iface
, len
, &buffer
);
133 aluMixData(device
, buffer
, len
);
134 hr
= IAudioRenderClient_ReleaseBuffer(render
.iface
, len
, 0);
138 AL_PRINT("Failed to buffer data: 0x%08lx\n", hr
);
139 aluHandleDisconnect(device
);
144 IAudioRenderClient_Release(render
.iface
);
149 static ALCboolean
MMDevApiOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
151 MMDevApiData
*data
= NULL
;
159 deviceName
= mmDevice
;
160 else if(strcmp(deviceName
, mmDevice
) != 0)
163 //Initialise requested device
164 data
= calloc(1, sizeof(MMDevApiData
));
167 alcSetError(device
, ALC_OUT_OF_MEMORY
);
172 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &data
->mmdev
);
174 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &client
);
179 IMMDevice_Release(data
->mmdev
);
183 AL_PRINT("Device init failed: 0x%08lx\n", hr
);
186 data
->client
= client
;
188 device
->szDeviceName
= strdup(deviceName
);
189 device
->ExtraData
= data
;
193 static void MMDevApiClosePlayback(ALCdevice
*device
)
195 MMDevApiData
*data
= device
->ExtraData
;
197 IAudioClient_Release(data
->client
);
200 IMMDevice_Release(data
->mmdev
);
204 device
->ExtraData
= NULL
;
207 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
209 MMDevApiData
*data
= device
->ExtraData
;
210 WAVEFORMATEXTENSIBLE OutputType
;
211 WAVEFORMATEX
*wfx
= NULL
;
214 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
217 AL_PRINT("Failed to get mix format: 0x%08lx\n", hr
);
221 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
222 device
->Frequency
= wfx
->nSamplesPerSec
;
223 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
225 if(wfx
->wFormatTag
== WAVE_FORMAT_PCM
)
227 if(wfx
->nChannels
== 1)
228 device
->FmtChans
= DevFmtMono
;
229 else if(wfx
->nChannels
== 2)
230 device
->FmtChans
= DevFmtStereo
;
233 AL_PRINT("Unhandled PCM channels: %d\n", wfx
->nChannels
);
234 device
->FmtChans
= DevFmtStereo
;
237 else if(wfx
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
239 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)wfx
;
241 if(wfe
->Format
.nChannels
== 1 && wfe
->dwChannelMask
== MONO
)
242 device
->FmtChans
= DevFmtMono
;
243 else if(wfe
->Format
.nChannels
== 2 && wfe
->dwChannelMask
== STEREO
)
244 device
->FmtChans
= DevFmtStereo
;
245 else if(wfe
->Format
.nChannels
== 4 && wfe
->dwChannelMask
== QUAD
)
246 device
->FmtChans
= DevFmtQuad
;
247 else if(wfe
->Format
.nChannels
== 6 && wfe
->dwChannelMask
== X5DOT1
)
248 device
->FmtChans
= DevFmtX51
;
249 else if(wfe
->Format
.nChannels
== 7 && wfe
->dwChannelMask
== X6DOT1
)
250 device
->FmtChans
= DevFmtX61
;
251 else if(wfe
->Format
.nChannels
== 8 && wfe
->dwChannelMask
== X7DOT1
)
252 device
->FmtChans
= DevFmtX71
;
255 AL_PRINT("Unhandled extensible channels: %d -- 0x%08lx\n", wfe
->Format
.nChannels
, wfe
->dwChannelMask
);
256 device
->FmtChans
= DevFmtStereo
;
261 if(wfx
->wFormatTag
== WAVE_FORMAT_PCM
)
263 if(wfx
->wBitsPerSample
== 8)
264 device
->FmtType
= DevFmtUByte
;
265 else if(wfx
->wBitsPerSample
== 16)
266 device
->FmtType
= DevFmtShort
;
268 else if(wfx
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
270 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)wfx
;
272 if(IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
274 if(wfx
->wBitsPerSample
== 8)
275 device
->FmtType
= DevFmtUByte
;
276 else if(wfx
->wBitsPerSample
== 16)
277 device
->FmtType
= DevFmtShort
;
279 else if(IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
280 device
->FmtType
= DevFmtFloat
;
286 SetDefaultWFXChannelOrder(device
);
288 memset(&OutputType
, 0, sizeof(OutputType
));
289 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
291 switch(device
->FmtType
)
294 device
->FmtType
= DevFmtUByte
;
297 OutputType
.Format
.wBitsPerSample
= 8;
298 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
301 device
->FmtType
= DevFmtShort
;
304 OutputType
.Format
.wBitsPerSample
= 16;
305 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
308 OutputType
.Format
.wBitsPerSample
= 32;
309 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
313 switch(device
->FmtChans
)
316 OutputType
.Format
.nChannels
= 1;
317 OutputType
.dwChannelMask
= MONO
;
320 OutputType
.Format
.nChannels
= 2;
321 OutputType
.dwChannelMask
= STEREO
;
324 OutputType
.Format
.nChannels
= 4;
325 OutputType
.dwChannelMask
= QUAD
;
328 OutputType
.Format
.nChannels
= 6;
329 OutputType
.dwChannelMask
= X5DOT1
;
332 OutputType
.Format
.nChannels
= 7;
333 OutputType
.dwChannelMask
= X6DOT1
;
336 OutputType
.Format
.nChannels
= 8;
337 OutputType
.dwChannelMask
= X7DOT1
;
341 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*OutputType
.Format
.wBitsPerSample
/8;
342 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
343 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*OutputType
.Format
.nBlockAlign
;
344 OutputType
.Format
.cbSize
= sizeof(OutputType
) - sizeof(OutputType
.Format
);
347 hr
= IAudioClient_Initialize(data
->client
, AUDCLNT_SHAREMODE_SHARED
, 0,
348 (ALuint64
)device
->UpdateSize
* 10000000 /
349 device
->Frequency
* device
->NumUpdates
,
350 0, &OutputType
.Format
, NULL
);
353 AL_PRINT("Failed to initialize audio client: 0x%08lx\n", hr
);
358 hr
= IAudioClient_Start(data
->client
);
361 AL_PRINT("Failed to start audio client\n");
365 data
->thread
= StartThread(MMDevApiProc
, device
);
368 IAudioClient_Stop(data
->client
);
369 AL_PRINT("Failed to start thread\n");
376 static void MMDevApiStopPlayback(ALCdevice
*device
)
378 MMDevApiData
*data
= device
->ExtraData
;
384 StopThread(data
->thread
);
389 IAudioClient_Stop(data
->client
);
393 static ALCboolean
MMDevApiOpenCapture(ALCdevice
*device
, const ALCchar
*deviceName
)
401 static const BackendFuncs MMDevApiFuncs
= {
402 MMDevApiOpenPlayback
,
403 MMDevApiClosePlayback
,
404 MMDevApiResetPlayback
,
405 MMDevApiStopPlayback
,
415 void alcMMDevApiInit(BackendFuncs
*FuncList
)
417 *FuncList
= MMDevApiFuncs
;
420 void alcMMDevApiDeinit(void)
424 IMMDeviceEnumerator_Release(Enumerator
);
430 void alcMMDevApiProbe(int type
)
432 if(!MMDevApiLoad()) return;
434 if(type
== DEVICE_PROBE
)
435 AppendDeviceList(mmDevice
);
436 else if(type
== ALL_DEVICE_PROBE
)
437 AppendAllDeviceList(mmDevice
);