1 /*****************************************************************************
2 * wasapi.c : Windows Audio Session API output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2012 RĂ©mi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
31 #include <audioclient.h>
32 #include <mmdeviceapi.h>
34 #include <vlc_common.h>
38 static LARGE_INTEGER freq; /* performance counters frequency */
40 BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID); /* avoid warning */
42 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved)
49 case DLL_PROCESS_ATTACH:
50 if (!QueryPerformanceFrequency(&freq))
57 static UINT64 GetQPC(void)
59 LARGE_INTEGER counter;
61 if (!QueryPerformanceCounter(&counter))
64 lldiv_t d = lldiv(counter.QuadPart, freq.QuadPart);
65 return (d.quot * 10000000) + ((d.rem * 10000000) / freq.QuadPart);
68 static void Enter(void)
70 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
71 if (unlikely(FAILED(hr)))
75 static void Leave(void)
80 typedef struct aout_stream_sys
84 uint8_t chans_table[AOUT_CHAN_MAX];
85 uint8_t chans_to_reorder;
87 uint8_t bits; /**< Bits per sample */
88 unsigned rate; /**< Sample rate */
89 unsigned bytes_per_frame;
90 UINT32 written; /**< Frames written to the buffer */
91 UINT32 frames; /**< Total buffer size (frames) */
95 /*** VLC audio output callbacks ***/
96 static HRESULT TimeGet(aout_stream_t *s, mtime_t *restrict delay)
98 aout_stream_sys_t *sys = s->sys;
103 hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
106 IAudioClock *clock = pv;
108 hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
110 msg_Err(s, "cannot get position (error 0x%lx)", hr);
111 IAudioClock_Release(clock);
114 msg_Err(s, "cannot get clock (error 0x%lx)", hr);
120 *delay = ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
121 static_assert((10000000 % CLOCK_FREQ) == 0,
122 "Frequency conversion broken");
126 *delay = sys->written * CLOCK_FREQ / sys->rate;
127 msg_Dbg(s, "extrapolating position: still propagating buffers");
133 static HRESULT Play(aout_stream_t *s, block_t *block)
135 aout_stream_sys_t *sys = s->sys;
139 if (sys->chans_to_reorder)
140 aout_ChannelReorder(block->p_buffer, block->i_buffer,
141 sys->chans_to_reorder, sys->chans_table, sys->bits);
143 hr = IAudioClient_GetService(sys->client, &IID_IAudioRenderClient, &pv);
146 msg_Err(s, "cannot get render client (error 0x%lx)", hr);
150 IAudioRenderClient *render = pv;
154 hr = IAudioClient_GetCurrentPadding(sys->client, &frames);
157 msg_Err(s, "cannot get current padding (error 0x%lx)", hr);
161 assert(frames <= sys->frames);
162 frames = sys->frames - frames;
163 if (frames > block->i_nb_samples)
164 frames = block->i_nb_samples;
167 hr = IAudioRenderClient_GetBuffer(render, frames, &dst);
170 msg_Err(s, "cannot get buffer (error 0x%lx)", hr);
174 const size_t copy = frames * sys->bytes_per_frame;
176 memcpy(dst, block->p_buffer, copy);
177 hr = IAudioRenderClient_ReleaseBuffer(render, frames, 0);
180 msg_Err(s, "cannot release buffer (error 0x%lx)", hr);
183 IAudioClient_Start(sys->client);
185 block->p_buffer += copy;
186 block->i_buffer -= copy;
187 block->i_nb_samples -= frames;
188 sys->written += frames;
189 if (block->i_nb_samples == 0)
192 /* Out of buffer space, sleep */
193 msleep(AOUT_MIN_PREPARE_TIME
194 + block->i_nb_samples * CLOCK_FREQ / sys->rate);
196 IAudioRenderClient_Release(render);
198 block_Release(block);
203 static HRESULT Pause(aout_stream_t *s, bool paused)
205 aout_stream_sys_t *sys = s->sys;
209 hr = IAudioClient_Stop(sys->client);
211 hr = IAudioClient_Start(sys->client);
213 msg_Warn(s, "cannot %s stream (error 0x%lx)",
214 paused ? "stop" : "start", hr);
218 static HRESULT Flush(aout_stream_t *s)
220 aout_stream_sys_t *sys = s->sys;
223 IAudioClient_Stop(sys->client);
225 hr = IAudioClient_Reset(sys->client);
227 msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
234 /*** Initialization / deinitialization **/
235 static const uint32_t chans_out[] = {
236 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
237 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
238 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
239 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0
241 static const uint32_t chans_in[] = {
242 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
243 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
244 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
245 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0
248 static void vlc_ToWave(WAVEFORMATEXTENSIBLE *restrict wf,
249 audio_sample_format_t *restrict audio)
251 switch (audio->i_format)
254 audio->i_format = VLC_CODEC_FL32;
256 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
261 audio->i_format = VLC_CODEC_S16N;
263 wf->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
267 audio->i_format = VLC_CODEC_FL32;
268 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
271 aout_FormatPrepare (audio);
273 wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
274 wf->Format.nChannels = audio->i_channels;
275 wf->Format.nSamplesPerSec = audio->i_rate;
276 wf->Format.nAvgBytesPerSec = audio->i_bytes_per_frame * audio->i_rate;
277 wf->Format.nBlockAlign = audio->i_bytes_per_frame;
278 wf->Format.wBitsPerSample = audio->i_bitspersample;
279 wf->Format.cbSize = sizeof (*wf) - sizeof (wf->Format);
281 wf->Samples.wValidBitsPerSample = audio->i_bitspersample;
283 wf->dwChannelMask = 0;
284 for (unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++)
285 if (audio->i_physical_channels & pi_vlc_chan_order_wg4[i])
286 wf->dwChannelMask |= chans_in[i];
289 static int vlc_FromWave(const WAVEFORMATEX *restrict wf,
290 audio_sample_format_t *restrict audio)
292 audio->i_rate = wf->nSamplesPerSec;
293 audio->i_physical_channels = 0;
295 if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
297 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
299 for (unsigned i = 0; chans_in[i]; i++)
300 if (wfe->dwChannelMask & chans_in[i])
301 audio->i_physical_channels |= pi_vlc_chan_order_wg4[i];
304 audio->i_original_channels = audio->i_physical_channels;
305 aout_FormatPrepare (audio);
307 if (wf->nChannels != audio->i_channels)
312 static unsigned vlc_CheckWaveOrder (const WAVEFORMATEX *restrict wf,
313 uint8_t *restrict table)
317 if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
319 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
321 mask = wfe->dwChannelMask;
323 return aout_CheckChannelReorder(chans_in, chans_out, mask, table);
326 static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict fmt,
327 IMMDevice *dev, const GUID *sid)
329 aout_stream_sys_t *sys = malloc(sizeof (*sys));
330 if (unlikely(sys == NULL))
331 return E_OUTOFMEMORY;
338 hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_ALL, NULL, &pv);
342 msg_Err(s, "cannot activate client (error 0x%lx)", hr);
347 /* Configure audio stream */
348 WAVEFORMATEXTENSIBLE wf;
351 vlc_ToWave(&wf, fmt);
352 hr = IAudioClient_IsFormatSupported(sys->client, AUDCLNT_SHAREMODE_SHARED,
356 msg_Err(s, "cannot negotiate audio format (error 0x%lx)", hr);
363 if (vlc_FromWave(pwf, fmt))
366 msg_Err(s, "unsupported audio format");
370 msg_Dbg(s, "modified format");
375 sys->chans_to_reorder = vlc_CheckWaveOrder((hr == S_OK) ? &wf.Format : pwf,
377 sys->bits = fmt->i_bitspersample;
379 hr = IAudioClient_Initialize(sys->client, AUDCLNT_SHAREMODE_SHARED, 0,
380 AOUT_MAX_PREPARE_TIME * 10, 0,
381 (hr == S_OK) ? &wf.Format : pwf, sid);
385 msg_Err(s, "cannot initialize audio client (error 0x%lx)", hr);
389 hr = IAudioClient_GetBufferSize(sys->client, &sys->frames);
392 msg_Err(s, "cannot get buffer size (error 0x%lx)", hr);
396 sys->rate = fmt->i_rate;
397 sys->bytes_per_frame = fmt->i_bytes_per_frame;
400 s->time_get = TimeGet;
406 if (sys->client != NULL)
407 IAudioClient_Release(sys->client);
412 static void Stop(aout_stream_t *s)
414 aout_stream_sys_t *sys = s->sys;
416 IAudioClient_Stop(sys->client); /* should not be needed */
417 IAudioClient_Release(sys->client);
420 #undef aout_stream_Start
421 aout_stream_t *aout_stream_Start(vlc_object_t *parent,
422 audio_sample_format_t *restrict fmt,
423 IMMDevice *dev, const GUID *sid)
425 aout_stream_t *s = vlc_object_create(parent, sizeof (*s));
426 if (unlikely(s == NULL))
429 HRESULT hr = Start(s, fmt, dev, sid);
432 vlc_object_release(s);
438 void aout_stream_Stop(aout_stream_t *s)
441 vlc_object_release(s);