vlc_fourcc_t format; /**< Sample format */
unsigned rate; /**< Sample rate */
unsigned bytes_per_frame;
- UINT32 written; /**< Frames written to the buffer */
+ UINT64 written; /**< Frames written to the buffer */
UINT32 frames; /**< Total buffer size (frames) */
} aout_stream_sys_t;
{
aout_stream_sys_t *sys = s->sys;
void *pv;
- UINT64 pos, qpcpos;
+ UINT64 pos, qpcpos, freq;
HRESULT hr;
hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
- if (SUCCEEDED(hr))
+ if (FAILED(hr))
{
- IAudioClock *clock = pv;
-
- hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
- if (FAILED(hr))
- msg_Err(s, "cannot get position (error 0x%lx)", hr);
- IAudioClock_Release(clock);
- }
- else
msg_Err(s, "cannot get clock (error 0x%lx)", hr);
+ return hr;
+ }
+
+ IAudioClock *clock = pv;
+ hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
if (SUCCEEDED(hr))
+ hr = IAudioClock_GetFrequency(clock, &freq);
+ IAudioClock_Release(clock);
+ if (FAILED(hr))
{
- if (pos != 0)
- {
- *delay = ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
- static_assert((10000000 % CLOCK_FREQ) == 0,
- "Frequency conversion broken");
- }
- else
- {
- *delay = sys->written * CLOCK_FREQ / sys->rate;
- msg_Dbg(s, "extrapolating position: still propagating buffers");
- }
+ msg_Err(s, "cannot get position (error 0x%lx)", hr);
+ return hr;
}
+
+ lldiv_t w = lldiv(sys->written, sys->rate);
+ lldiv_t r = lldiv(pos, freq);
+
+ static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
+
+ *delay = ((w.quot - r.quot) * CLOCK_FREQ)
+ + ((w.rem * CLOCK_FREQ) / sys->rate)
+ - ((r.rem * CLOCK_FREQ) / freq)
+ - ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
+
return hr;
}
break; /* done */
/* Out of buffer space, sleep */
- msleep(AOUT_MIN_PREPARE_TIME
- + block->i_nb_samples * CLOCK_FREQ / sys->rate);
+ msleep(sys->frames * (CLOCK_FREQ / 2) / sys->rate);
}
IAudioRenderClient_Release(render);
out:
IAudioClient_Stop(sys->client);
hr = IAudioClient_Reset(sys->client);
- if (FAILED(hr))
- msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
- else
+ if (SUCCEEDED(hr))
+ {
+ msg_Dbg(s, "reset");
sys->written = 0;
+ }
+ else
+ msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
return hr;
}
{
const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
+ if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ switch (wf->wBitsPerSample)
+ {
+ case 64:
+ audio->i_format = VLC_CODEC_FL64;
+ break;
+ case 32:
+ audio->i_format = VLC_CODEC_FL32;
+ break;
+ default:
+ return -1;
+ }
+ }
+ else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
+ {
+ switch (wf->wBitsPerSample)
+ {
+ case 32:
+ audio->i_format = VLC_CODEC_S32N;
+ break;
+ case 16:
+ audio->i_format = VLC_CODEC_S16N;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (wfe->Samples.wValidBitsPerSample != wf->wBitsPerSample)
+ return -1;
+
for (unsigned i = 0; chans_in[i]; i++)
if (wfe->dwChannelMask & chans_in[i])
audio->i_physical_channels |= pi_vlc_chan_order_wg4[i];
}
+ else
+ return -1;
audio->i_original_channels = audio->i_physical_channels;
aout_FormatPrepare (audio);
msg_Err(s, "cannot get buffer size (error 0x%lx)", hr);
goto error;
}
+ msg_Dbg(s, "buffer size : %"PRIu32" frames", sys->frames);
+
+ REFERENCE_TIME latT, defT, minT;
+ if (SUCCEEDED(IAudioClient_GetStreamLatency(sys->client, &latT))
+ && SUCCEEDED(IAudioClient_GetDevicePeriod(sys->client, &defT, &minT)))
+ {
+ msg_Dbg(s, "maximum latency: %"PRIu64"00 ns", latT);
+ msg_Dbg(s, "default period : %"PRIu64"00 ns", defT);
+ msg_Dbg(s, "minimum period : %"PRIu64"00 ns", minT);
+ }
sys->rate = fmt->i_rate;
sys->bytes_per_frame = fmt->i_bytes_per_frame;
IAudioClient_Stop(sys->client); /* should not be needed */
IAudioClient_Release(sys->client);
+
+ free(sys);
}
vlc_module_begin()
set_shortname("WASAPI")
set_description(N_("Windows Audio Session API output"))
- set_capability("aout stream", /*50*/0)
+ set_capability("aout stream", 50)
set_category(CAT_AUDIO)
set_subcategory(SUBCAT_AUDIO_AOUT)
set_callbacks(Start, Stop)