diff options
author | Paul Adenot <[email protected]> | 2020-04-29 16:23:15 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-29 16:23:15 +0200 |
commit | 35190a8da650be297edb91d2db778bed622d8691 (patch) | |
tree | 4f64189b1c4dd81e2464116521564becd0b1298d | |
parent | 616d773441b5355800ce64197a699e6cd6b36172 (diff) | |
download | cubeb-35190a8da650be297edb91d2db778bed622d8691.tar.gz cubeb-35190a8da650be297edb91d2db778bed622d8691.zip |
Add a method to get audio input latency on a stream (#583)
-rw-r--r-- | include/cubeb/cubeb.h | 10 | ||||
-rw-r--r-- | src/cubeb-internal.h | 1 | ||||
-rw-r--r-- | src/cubeb.c | 14 | ||||
-rw-r--r-- | src/cubeb_alsa.c | 1 | ||||
-rw-r--r-- | src/cubeb_audiotrack.c | 1 | ||||
-rw-r--r-- | src/cubeb_audiounit.cpp | 1 | ||||
-rw-r--r-- | src/cubeb_jack.cpp | 1 | ||||
-rw-r--r-- | src/cubeb_kai.c | 1 | ||||
-rw-r--r-- | src/cubeb_opensl.c | 1 | ||||
-rw-r--r-- | src/cubeb_pulse.c | 1 | ||||
-rw-r--r-- | src/cubeb_sun.c | 1 | ||||
-rw-r--r-- | src/cubeb_wasapi.cpp | 51 | ||||
-rw-r--r-- | src/cubeb_winmm.c | 1 |
13 files changed, 84 insertions, 1 deletions
diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index b3fc56d..f372831 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -559,6 +559,16 @@ CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * pos @retval CUBEB_ERROR */ CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency); +/** Get the input latency for this stream, in frames. This is the number of + frames between the time the audio input devices records the data, and they + are available in the data callback. + This returns CUBEB_ERROR when the stream is output-only. + @param stream + @param latency Current approximate stream latency in frames. + @retval CUBEB_OK + @retval CUBEB_ERROR_NOT_SUPPORTED + @retval CUBEB_ERROR */ +CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency); /** Set the volume for a stream. @param stream the stream for which to adjust the volume. @param volume a float between 0.0 (muted) and 1.0 (maximum volume) diff --git a/src/cubeb-internal.h b/src/cubeb-internal.h index 312a9ea..4aee68f 100644 --- a/src/cubeb-internal.h +++ b/src/cubeb-internal.h @@ -63,6 +63,7 @@ struct cubeb_ops { int (* stream_reset_default_device)(cubeb_stream * stream); int (* stream_get_position)(cubeb_stream * stream, uint64_t * position); int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency); + int (* stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency); int (* stream_set_volume)(cubeb_stream * stream, float volumes); int (* stream_get_current_device)(cubeb_stream * stream, cubeb_device ** const device); diff --git a/src/cubeb.c b/src/cubeb.c index f73bd0c..3320fcc 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -421,6 +421,20 @@ cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency) } int +cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency) +{ + if (!stream || !latency) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + + if (!stream->context->ops->stream_get_input_latency) { + return CUBEB_ERROR_NOT_SUPPORTED; + } + + return stream->context->ops->stream_get_input_latency(stream, latency); +} + +int cubeb_stream_set_volume(cubeb_stream * stream, float volume) { if (!stream || volume > 1.0 || volume < 0.0) { diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c index a564fbf..4b479dc 100644 --- a/src/cubeb_alsa.c +++ b/src/cubeb_alsa.c @@ -1444,6 +1444,7 @@ static struct cubeb_ops const alsa_ops = { .stream_reset_default_device = NULL, .stream_get_position = alsa_stream_get_position, .stream_get_latency = alsa_stream_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = alsa_stream_set_volume, .stream_get_current_device = NULL, .stream_device_destroy = NULL, diff --git a/src/cubeb_audiotrack.c b/src/cubeb_audiotrack.c index 22f1fe0..352473d 100644 --- a/src/cubeb_audiotrack.c +++ b/src/cubeb_audiotrack.c @@ -433,6 +433,7 @@ static struct cubeb_ops const audiotrack_ops = { .stream_reset_default_device = NULL, .stream_get_position = audiotrack_stream_get_position, .stream_get_latency = audiotrack_stream_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = audiotrack_stream_set_volume, .stream_get_current_device = NULL, .stream_device_destroy = NULL, diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 1abadb4..4b506d0 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -3621,6 +3621,7 @@ cubeb_ops const audiounit_ops = { /*.stream_reset_default_device =*/ nullptr, /*.stream_get_position =*/ audiounit_stream_get_position, /*.stream_get_latency =*/ audiounit_stream_get_latency, + /*.stream_get_input_latency =*/ NULL, /*.stream_set_volume =*/ audiounit_stream_set_volume, /*.stream_get_current_device =*/ audiounit_stream_get_current_device, /*.stream_device_destroy =*/ audiounit_stream_device_destroy, diff --git a/src/cubeb_jack.cpp b/src/cubeb_jack.cpp index 9fc97a4..2320b73 100644 --- a/src/cubeb_jack.cpp +++ b/src/cubeb_jack.cpp @@ -132,6 +132,7 @@ static struct cubeb_ops const cbjack_ops = { .stream_reset_default_device = NULL, .stream_get_position = cbjack_stream_get_position, .stream_get_latency = cbjack_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = cbjack_stream_set_volume, .stream_get_current_device = cbjack_stream_get_current_device, .stream_device_destroy = cbjack_stream_device_destroy, diff --git a/src/cubeb_kai.c b/src/cubeb_kai.c index 5333968..0a621c9 100644 --- a/src/cubeb_kai.c +++ b/src/cubeb_kai.c @@ -361,6 +361,7 @@ static struct cubeb_ops const kai_ops = { /*.stream_reset_default_device =*/ NULL, /*.stream_get_position =*/ kai_stream_get_position, /*.stream_get_latency = */ kai_stream_get_latency, + /*.stream_get_input_latency = */ NULL, /*.stream_set_volume =*/ kai_stream_set_volume, /*.stream_get_current_device =*/ NULL, /*.stream_device_destroy =*/ NULL, diff --git a/src/cubeb_opensl.c b/src/cubeb_opensl.c index 021390e..59cecfe 100644 --- a/src/cubeb_opensl.c +++ b/src/cubeb_opensl.c @@ -1752,6 +1752,7 @@ static struct cubeb_ops const opensl_ops = { .stream_reset_default_device = NULL, .stream_get_position = opensl_stream_get_position, .stream_get_latency = opensl_stream_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = opensl_stream_set_volume, .stream_get_current_device = NULL, .stream_device_destroy = NULL, diff --git a/src/cubeb_pulse.c b/src/cubeb_pulse.c index 94ead3a..a393b66 100644 --- a/src/cubeb_pulse.c +++ b/src/cubeb_pulse.c @@ -1597,6 +1597,7 @@ static struct cubeb_ops const pulse_ops = { .stream_reset_default_device = NULL, .stream_get_position = pulse_stream_get_position, .stream_get_latency = pulse_stream_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = pulse_stream_set_volume, .stream_get_current_device = pulse_stream_get_current_device, .stream_device_destroy = pulse_stream_device_destroy, diff --git a/src/cubeb_sun.c b/src/cubeb_sun.c index 45483fc..3fafa57 100644 --- a/src/cubeb_sun.c +++ b/src/cubeb_sun.c @@ -721,6 +721,7 @@ static struct cubeb_ops const sun_ops = { .stream_reset_default_device = NULL, .stream_get_position = sun_stream_get_position, .stream_get_latency = sun_stream_get_latency, + .stream_get_input_latency = NULL, .stream_set_volume = sun_stream_set_volume, .stream_get_current_device = sun_get_current_device, .stream_device_destroy = sun_stream_device_destroy, diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp index a153255..b205507 100644 --- a/src/cubeb_wasapi.cpp +++ b/src/cubeb_wasapi.cpp @@ -89,6 +89,9 @@ DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e #endif namespace { + +const int64_t LATENCY_NOT_AVAILABLE_YET = -1; + struct com_heap_ptr_deleter { void operator()(void * ptr) const noexcept { CoTaskMemFree(ptr); @@ -211,6 +214,7 @@ struct cubeb { /* Collection changed for output (render) devices. */ cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr; void * output_collection_changed_user_ptr = nullptr; + UINT64 performance_counter_frequency; }; class wasapi_endpoint_notification_client; @@ -334,6 +338,9 @@ struct cubeb_stream { std::atomic<std::atomic<bool>*> emergency_bailout { nullptr }; /* Synchronizes render thread start to ensure safe access to emergency_bailout. */ HANDLE thread_ready_event = 0; + /* This needs an active audio input stream to be known, and is updated in the + * first audio input callback. */ + std::atomic<int64_t> input_latency_hns { LATENCY_NOT_AVAILABLE_YET }; }; class monitor_device_notifications { @@ -856,6 +863,7 @@ bool get_input_buffer(cubeb_stream * stm) BYTE * input_packet = NULL; DWORD flags; UINT64 dev_pos; + UINT64 pc_position; UINT32 next; /* Get input packets until we have captured enough frames, and put them in a * contiguous buffer. */ @@ -885,13 +893,25 @@ bool get_input_buffer(cubeb_stream * stm) &frames, &flags, &dev_pos, - NULL); + &pc_position); + if (FAILED(hr)) { LOG("GetBuffer failed for capture: %lx", hr); return false; } XASSERT(frames == next); + if (stm->context->performance_counter_frequency) { + LARGE_INTEGER now; + UINT64 now_hns; + // See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, section "Remarks". + QueryPerformanceCounter(&now); + now_hns = 10000000 * now.QuadPart / stm->context->performance_counter_frequency; + if (now_hns >= pc_position) { + stm->input_latency_hns = now_hns - pc_position; + } + } + UINT32 input_stream_samples = frames * stm->input_stream_params.channels; // We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY // flag. There a two primary (non exhaustive) scenarios we anticipate this @@ -1527,6 +1547,14 @@ int wasapi_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } + LARGE_INTEGER frequency; + if (QueryPerformanceFrequency(&frequency)) { + LOG("Failed getting performance counter frequency, latency reporting will be inacurate"); + ctx->performance_counter_frequency = 0; + } else { + ctx->performance_counter_frequency = frequency.QuadPart; + } + *context = ctx; return CUBEB_OK; @@ -2614,6 +2642,26 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) return CUBEB_OK; } + +int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) +{ + XASSERT(stm && latency); + + if (!has_input(stm)) { + return CUBEB_ERROR; + } + + auto_lock lock(stm->stream_reset_lock); + + if (stm->input_latency_hns == LATENCY_NOT_AVAILABLE_YET) { + return CUBEB_ERROR; + } + + *latency = hns_to_frames(stm, stm->input_latency_hns); + + return CUBEB_OK; +} + int wasapi_stream_set_volume(cubeb_stream * stm, float volume) { auto_lock lock(stm->stream_reset_lock); @@ -2973,6 +3021,7 @@ cubeb_ops const wasapi_ops = { /*.stream_reset_default_device =*/ wasapi_stream_reset_default_device, /*.stream_get_position =*/ wasapi_stream_get_position, /*.stream_get_latency =*/ wasapi_stream_get_latency, + /*.stream_get_input_latency =*/ wasapi_stream_get_input_latency, /*.stream_set_volume =*/ wasapi_stream_set_volume, /*.stream_get_current_device =*/ NULL, /*.stream_device_destroy =*/ NULL, diff --git a/src/cubeb_winmm.c b/src/cubeb_winmm.c index e064ca0..363fcd4 100644 --- a/src/cubeb_winmm.c +++ b/src/cubeb_winmm.c @@ -1059,6 +1059,7 @@ static struct cubeb_ops const winmm_ops = { /*.stream_reset_default_device =*/ NULL, /*.stream_get_position =*/ winmm_stream_get_position, /*.stream_get_latency = */ winmm_stream_get_latency, + /*.stream_get_input_latency = */ NULL, /*.stream_set_volume =*/ winmm_stream_set_volume, /*.stream_get_current_device =*/ NULL, /*.stream_device_destroy =*/ NULL, |