diff options
author | Paul Adenot <[email protected]> | 2019-07-15 18:31:00 +0200 |
---|---|---|
committer | Paul Adenot <[email protected]> | 2019-07-18 15:23:45 +0200 |
commit | c6366e4e2761caa6f851423e7c2941bd5aef3114 (patch) | |
tree | 1ae18a627cd44af02fa4d71a6a428d49030421c1 /tools | |
parent | abbdaa2f8e3da0e447856b9858b17508beab1d21 (diff) | |
download | cubeb-c6366e4e2761caa6f851423e7c2941bd5aef3114.tar.gz cubeb-c6366e4e2761caa6f851423e7c2941bd5aef3114.zip |
Add a rountrip latency testing mode to the tool
Diffstat (limited to 'tools')
-rw-r--r-- | tools/cubeb-test.cpp | 105 |
1 files changed, 90 insertions, 15 deletions
diff --git a/tools/cubeb-test.cpp b/tools/cubeb-test.cpp index 55deaca..e16a9bb 100644 --- a/tools/cubeb-test.cpp +++ b/tools/cubeb-test.cpp @@ -17,7 +17,8 @@ // Default values if none specified #define DEFAULT_RATE 44100 -#define DEFAULT_CHANNELS 2 +#define DEFAULT_OUTPUT_CHANNELS 2 +#define DEFAULT_INPUT_CHANNELS 1 static const char* state_to_string(cubeb_state state) { switch (state) { @@ -53,7 +54,10 @@ public: bool destroy_stream() const; bool destroy(); bool activate_log(cubeb_log_level log_level) const; + void set_latency_testing(bool on); + void set_latency_frames(uint32_t latency_frames); uint64_t get_stream_position() const; + uint32_t get_stream_latency() const; long user_data_cb(cubeb_stream* stm, void* user, const void* input_buffer, void* output_buffer, long nframes); @@ -79,6 +83,9 @@ private: /* Accessed only from client and audio thread. */ std::atomic<uint32_t> _rate = {0}; std::atomic<uint32_t> _channels = {0}; + std::atomic<bool> _latency_testing = {false}; + std::atomic<uint32_t> _latency_frames = {0}; // if !0, override. Else, use min. + /* Accessed only from audio thread. */ uint32_t _total_frames = 0; @@ -124,11 +131,27 @@ bool cubeb_client::init_stream() { _rate = has_output() ? output_params.rate : input_params.rate; _channels = has_output() ? output_params.channels : input_params.channels; - int rv = + cubeb_stream_params params; + params.rate = _rate; + params.channels = 2; + params.format = CUBEB_SAMPLE_FLOAT32NE; + + uint32_t latency = 0; + int rv = cubeb_get_min_latency(context, ¶ms, &latency); + if (rv != CUBEB_OK) { + fprintf(stderr, "Could not get min latency."); + return false; + } + + if (_latency_frames) { + latency = _latency_frames.load(); + printf("Opening a stream with a forced latency of %d frames\n", latency); + } + + rv = cubeb_stream_init(context, &stream, "Stream", input_device, has_input() ? &input_params : nullptr, output_device, - has_output() ? &output_params : nullptr, 512, - user_data_cb_s, user_state_cb_s, this); + has_output() ? &output_params : nullptr, latency, user_data_cb_s, user_state_cb_s, this); if (rv != CUBEB_OK) { fprintf(stderr, "Could not open the stream\n"); return false; @@ -158,12 +181,22 @@ uint64_t cubeb_client::get_stream_position() const { uint64_t pos = 0; int rv = cubeb_stream_get_position(stream, &pos); if (rv != CUBEB_OK) { - fprintf(stderr, "Could not get the position the stream\n"); + fprintf(stderr, "Could not get the position of the stream\n"); return 0; } return pos; } +uint32_t cubeb_client::get_stream_latency() const { + uint32_t latency = 0; + int rv = cubeb_stream_get_latency(stream, &latency); + if (rv != CUBEB_OK) { + fprintf(stderr, "Could not get the latency of the stream\n"); + return 0; + } + return latency; +} + bool cubeb_client::destroy_stream() const { cubeb_stream_destroy(stream); return true; @@ -187,6 +220,14 @@ bool cubeb_client::activate_log(cubeb_log_level log_level) const { return true; } +void cubeb_client::set_latency_testing(bool on) { + _latency_testing = on; +} + +void cubeb_client::set_latency_frames(uint32_t latency_frames) { + _latency_frames = latency_frames; +} + static void fill_with_sine_tone(float* buf, uint32_t num_of_frames, uint32_t num_of_channels, uint32_t frame_rate, uint32_t position) { @@ -206,7 +247,28 @@ long cubeb_client::user_data_cb(cubeb_stream* stm, void* user, if (input_buffer && output_buffer) { const float* in = static_cast<const float*>(input_buffer); float* out = static_cast<float*>(output_buffer); - memcpy(out, in, sizeof(float) * nframes * _channels); + if (_latency_testing) { + for (uint32_t i = 0; i < nframes; i++) { + // Impulses every second, mixed with the input signal fed back at half + // gain, to measure the input-to-output latency via feedback. + uint32_t clock = ((_total_frames + i) % _rate); + if (!clock) { + for (uint32_t j = 0; j < _channels; j++) { + out[i * _channels + j] = 1.0 + in[i] * 0.5; + } + } else { + for (uint32_t j = 0; j < _channels; j++) { + out[i * _channels + j] = 0.0 + in[i] * 0.5; + } + } + } + } else { + for (uint32_t i = 0; i < nframes; i++) { + for (uint32_t j = 0; j < _channels; j++) { + out[i * _channels + j] = in[i]; + } + } + } } else if (output_buffer && !input_buffer) { fill_with_sine_tone(static_cast<float*>(output_buffer), nframes, _channels, _rate, _total_frames); @@ -254,6 +316,7 @@ enum play_mode { RECORD, PLAYBACK, DUPLEX, + LATENCY_TESTING, COLLECTION_CHANGE, }; @@ -292,7 +355,7 @@ bool choose_action(const cubeb_client& cl, operation_data * op, int c) { } if (c == 'q') { - if (op->pm == PLAYBACK || op->pm == RECORD || op->pm == DUPLEX) { + if (op->pm == PLAYBACK || op->pm == RECORD || op->pm == DUPLEX || op->pm == LATENCY_TESTING) { bool res = cl.stop_stream(); if (!res) { fprintf(stderr, "stop_stream failed\n"); @@ -337,7 +400,8 @@ bool choose_action(const cubeb_client& cl, operation_data * op, int c) { } } else if (c == 'c') { uint64_t pos = cl.get_stream_position(); - fprintf(stderr, "stream position %" PRIu64 "\n", pos); + uint64_t latency = cl.get_stream_latency(); + fprintf(stderr, "stream position %" PRIu64 " (latency %" PRIu64 ")\n", pos, latency); } else if (c == 'i') { op->collection_device_type = CUBEB_DEVICE_TYPE_INPUT; fprintf(stderr, "collection device type changed to INPUT\n"); @@ -385,12 +449,18 @@ int main(int argc, char* argv[]) { op.pm = PLAYBACK; } else if ('d' == argv[1][0]) { op.pm = DUPLEX; + } else if ('l' == argv[1][0]) { + op.pm = LATENCY_TESTING; } else if ('c' == argv[1][0]) { op.pm = COLLECTION_CHANGE; } } op.rate = DEFAULT_RATE; - if (argc > 2) { + uint32_t latency_override = 0; + if (op.pm == LATENCY_TESTING && argc > 2) { + latency_override = strtoul(argv[2], NULL, 10); + printf("LATENCY_TESTING %d\n", latency_override); + } else if (argc > 2) { op.rate = strtoul(argv[2], NULL, 0); } @@ -412,13 +482,18 @@ int main(int argc, char* argv[]) { fprintf(stderr, "register_device_collection_changed failed\n"); } } else { - if (op.pm == PLAYBACK || op.pm == DUPLEX) { - cl.output_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_CHANNELS, - CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE}; + if (op.pm == PLAYBACK || op.pm == DUPLEX || op.pm == LATENCY_TESTING) { + cl.output_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_OUTPUT_CHANNELS, + CUBEB_LAYOUT_STEREO, CUBEB_STREAM_PREF_NONE}; } - if (op.pm == RECORD || op.pm == DUPLEX) { - cl.input_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_CHANNELS, - CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE}; + if (op.pm == RECORD || op.pm == DUPLEX || op.pm == LATENCY_TESTING) { + cl.input_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_INPUT_CHANNELS, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE}; + } + if (op.pm == LATENCY_TESTING) { + cl.set_latency_testing(true); + if (latency_override) { + cl.set_latency_frames(latency_override); + } } res = cl.init_stream(); if (!res) { |