aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorPaul Adenot <[email protected]>2020-04-29 16:23:15 +0200
committerGitHub <[email protected]>2020-04-29 16:23:15 +0200
commit35190a8da650be297edb91d2db778bed622d8691 (patch)
tree4f64189b1c4dd81e2464116521564becd0b1298d
parent616d773441b5355800ce64197a699e6cd6b36172 (diff)
downloadcubeb-35190a8da650be297edb91d2db778bed622d8691.tar.gz
cubeb-35190a8da650be297edb91d2db778bed622d8691.zip
Add a method to get audio input latency on a stream (#583)
-rw-r--r--include/cubeb/cubeb.h10
-rw-r--r--src/cubeb-internal.h1
-rw-r--r--src/cubeb.c14
-rw-r--r--src/cubeb_alsa.c1
-rw-r--r--src/cubeb_audiotrack.c1
-rw-r--r--src/cubeb_audiounit.cpp1
-rw-r--r--src/cubeb_jack.cpp1
-rw-r--r--src/cubeb_kai.c1
-rw-r--r--src/cubeb_opensl.c1
-rw-r--r--src/cubeb_pulse.c1
-rw-r--r--src/cubeb_sun.c1
-rw-r--r--src/cubeb_wasapi.cpp51
-rw-r--r--src/cubeb_winmm.c1
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,