From 587b3beea15417a73d331a8e9184a3d340c24e39 Mon Sep 17 00:00:00 2001 From: Dmitriy Shilin Date: Sun, 8 Oct 2017 14:48:38 +0300 Subject: [PATCH] roc_audio: adjust reseampler coef --- src/lib/roc/config.h | 3 ++ src/lib/roc/receiver.cpp | 4 ++ src/modules/roc_audio/resampler_updater.cpp | 15 ++++-- src/modules/roc_audio/resampler_updater.h | 7 ++- src/modules/roc_pipeline/config.h | 4 +- src/modules/roc_pipeline/receiver.cpp | 6 +-- src/modules/roc_pipeline/receiver_session.cpp | 6 ++- src/modules/roc_pipeline/receiver_session.h | 1 + .../roc_sndio/target_sox/roc_sndio/player.cpp | 45 ++++++++++++---- .../roc_sndio/target_sox/roc_sndio/player.h | 18 +++++-- .../target_sox/test_player_recorder.cpp | 51 ++++++++++++------- src/tools/roc_recv/main.cpp | 32 ++++++++---- 12 files changed, 138 insertions(+), 54 deletions(-) diff --git a/src/lib/roc/config.h b/src/lib/roc/config.h index 6f1bfaf94..63dd4cc51 100644 --- a/src/lib/roc/config.h +++ b/src/lib/roc/config.h @@ -105,6 +105,9 @@ typedef struct roc_receiver_config { //! A bitmask of ROC_FLAG_* constants. unsigned int flags; + + //! Number of samples per second per channel. + unsigned int sample_rate; } roc_receiver_config; #ifdef __cplusplus diff --git a/src/lib/roc/receiver.cpp b/src/lib/roc/receiver.cpp index 9ba730a82..ee525eb9a 100644 --- a/src/lib/roc/receiver.cpp +++ b/src/lib/roc/receiver.cpp @@ -28,6 +28,10 @@ bool make_receiver_config(pipeline::ReceiverConfig& out, const roc_receiver_conf out.default_session.timeout = in->timeout; out.default_session.samples_per_packet = in->samples_per_packet; + if (in->sample_rate) { + out.sample_rate = in->sample_rate; + } + switch ((unsigned)in->fec_scheme) { case ROC_FEC_RS8M: out.default_session.fec.codec = fec::ReedSolomon8m; diff --git a/src/modules/roc_audio/resampler_updater.cpp b/src/modules/roc_audio/resampler_updater.cpp index 62a76b18d..2fd1ddae5 100644 --- a/src/modules/roc_audio/resampler_updater.cpp +++ b/src/modules/roc_audio/resampler_updater.cpp @@ -22,7 +22,8 @@ const core::nanoseconds_t LogRate = 5000000000; } // namespace ResamplerUpdater::ResamplerUpdater(packet::timestamp_t update_interval, - packet::timestamp_t aim_queue_size) + packet::timestamp_t aim_queue_size, + const float sample_rate_coef) : writer_(NULL) , reader_(NULL) , resampler_(NULL) @@ -34,7 +35,8 @@ ResamplerUpdater::ResamplerUpdater(packet::timestamp_t update_interval, , head_(0) , has_tail_(false) , tail_(0) - , started_(false) { + , started_(false) + , sample_rate_coef_(sample_rate_coef) { } void ResamplerUpdater::set_writer(packet::IWriter& writer) { @@ -96,13 +98,16 @@ bool ResamplerUpdater::update(packet::timestamp_t time) { update_time_ += update_interval_; } + const float adjusted_coef = sample_rate_coef_ * fe_.freq_coeff(); + if (rate_limiter_.allow()) { - roc_log(LogDebug, "resampler updater: queue_size=%lu fe=%.5f", - (unsigned long)queue_size, (double)fe_.freq_coeff()); + roc_log(LogDebug, "resampler updater: queue_size=%lu fe=%.5f, adjust_fe=%.5f", + (unsigned long)queue_size, (double)fe_.freq_coeff(), + (double)adjusted_coef); } roc_panic_if(!resampler_); - return resampler_->set_scaling(fe_.freq_coeff()); + return resampler_->set_scaling(adjusted_coef); } } // namespace audio diff --git a/src/modules/roc_audio/resampler_updater.h b/src/modules/roc_audio/resampler_updater.h index 844912828..350be8bee 100644 --- a/src/modules/roc_audio/resampler_updater.h +++ b/src/modules/roc_audio/resampler_updater.h @@ -34,8 +34,11 @@ class ResamplerUpdater : public packet::IWriter, //! @b Parameters //! - @p update_interval defines how often to call FreqEstimator, in samples //! - @p aim_queue_size defines FreqEstimator target queue size, in samples + //! - @p sample_rate_coef represents a factor for converting session sample rate to + //! output sample rate of a receiver. ResamplerUpdater(packet::timestamp_t update_interval, - packet::timestamp_t aim_queue_size); + packet::timestamp_t aim_queue_size, + const float sample_rate_coef); //! Set output writer. void set_writer(packet::IWriter&); @@ -76,6 +79,8 @@ class ResamplerUpdater : public packet::IWriter, packet::timestamp_t tail_; bool started_; + + const float sample_rate_coef_; }; } // namespace audio diff --git a/src/modules/roc_pipeline/config.h b/src/modules/roc_pipeline/config.h index f4527ab0a..7412f6c04 100644 --- a/src/modules/roc_pipeline/config.h +++ b/src/modules/roc_pipeline/config.h @@ -125,7 +125,7 @@ struct ReceiverConfig { //! Default parameters for session. SessionConfig default_session; - //! Sample rate, number of samples for all channels per second. + //! Number of samples per second per channel. size_t sample_rate; //! Channel mask. @@ -149,7 +149,7 @@ struct SenderConfig { //! Parameters for the port from which repair packets are sent. PortConfig repair_port; - //! Sample rate, number of samples for all channels per second. + //! Number of samples per second per channel. size_t sample_rate; //! Channel mask. diff --git a/src/modules/roc_pipeline/receiver.cpp b/src/modules/roc_pipeline/receiver.cpp index 8f43d61e6..9b54376ed 100644 --- a/src/modules/roc_pipeline/receiver.cpp +++ b/src/modules/roc_pipeline/receiver.cpp @@ -148,9 +148,9 @@ bool Receiver::create_session_(const packet::PacketPtr& packet) { } const packet::Address src_address = packet->udp()->src_addr; - core::SharedPtr sess = new (allocator_) - ReceiverSession(config_.default_session, src_address, format_map_, packet_pool_, - byte_buffer_pool_, sample_buffer_pool_, allocator_); + core::SharedPtr sess = new (allocator_) ReceiverSession( + config_.default_session, config_.sample_rate, src_address, format_map_, + packet_pool_, byte_buffer_pool_, sample_buffer_pool_, allocator_); if (!sess || !sess->valid()) { roc_log(LogError, "receiver: can't create session, initialization failed"); diff --git a/src/modules/roc_pipeline/receiver_session.cpp b/src/modules/roc_pipeline/receiver_session.cpp index 064d56f82..3dcee8e5a 100644 --- a/src/modules/roc_pipeline/receiver_session.cpp +++ b/src/modules/roc_pipeline/receiver_session.cpp @@ -19,6 +19,7 @@ namespace roc { namespace pipeline { ReceiverSession::ReceiverSession(const SessionConfig& config, + const size_t out_sample_rate, const packet::Address& src_address, const rtp::FormatMap& format_map, packet::PacketPool& packet_pool, @@ -28,6 +29,8 @@ ReceiverSession::ReceiverSession(const SessionConfig& config, : src_address_(src_address) , allocator_(allocator) , audio_reader_(NULL) { + roc_panic_if(out_sample_rate == 0); + const rtp::Format* format = format_map.format(config.payload_type); if (!format) { return; @@ -35,7 +38,8 @@ ReceiverSession::ReceiverSession(const SessionConfig& config, if (config.resampling) { resampler_updater_.reset(new (allocator_) audio::ResamplerUpdater( - config.fe_update_interval, config.latency), + config.fe_update_interval, config.latency, + (float)format->sample_rate / out_sample_rate), allocator_); if (!resampler_updater_) { return; diff --git a/src/modules/roc_pipeline/receiver_session.h b/src/modules/roc_pipeline/receiver_session.h index a420e8da2..f581a9aa6 100644 --- a/src/modules/roc_pipeline/receiver_session.h +++ b/src/modules/roc_pipeline/receiver_session.h @@ -49,6 +49,7 @@ class ReceiverSession : public core::RefCnt, public core::ListN public: //! Initialize. ReceiverSession(const SessionConfig& config, + const size_t out_sample_rate, const packet::Address& src_address, const rtp::FormatMap& format_map, packet::PacketPool& packet_pool, diff --git a/src/modules/roc_sndio/target_sox/roc_sndio/player.cpp b/src/modules/roc_sndio/target_sox/roc_sndio/player.cpp index 304738ce5..d2ba4f4bf 100644 --- a/src/modules/roc_sndio/target_sox/roc_sndio/player.cpp +++ b/src/modules/roc_sndio/target_sox/roc_sndio/player.cpp @@ -16,14 +16,12 @@ namespace roc { namespace sndio { -Player::Player(pipeline::IReceiver& input, - core::BufferPool& buffer_pool, +Player::Player(core::BufferPool& buffer_pool, core::IAllocator& allocator, bool oneshot, packet::channel_mask_t channels, size_t sample_rate) : output_(NULL) - , input_(input) , buffer_pool_(buffer_pool) , allocator_(allocator) , clips_(0) @@ -34,10 +32,6 @@ Player::Player(pipeline::IReceiver& input, roc_panic("player: # of channels is zero"); } - if (sample_rate == 0) { - roc_panic("player: sample rate is zero"); - } - memset(&out_signal_, 0, sizeof(out_signal_)); out_signal_.rate = sample_rate; out_signal_.channels = (unsigned)n_channels; @@ -71,6 +65,23 @@ bool Player::open(const char* name, const char* type) { return false; } + unsigned long in_rate = (unsigned long)out_signal_.rate; + unsigned long out_rate = (unsigned long)output_->signal.rate; + + if (in_rate != 0 && in_rate != out_rate) { + roc_log(LogError, + "can't open output file or device with the required sample rate: " + "required=%lu suggested=%lu", + out_rate, in_rate); + return false; + } + + roc_log(LogInfo, + "player:" + " bits=%lu out_rate=%lu in_rate=%lu ch=%lu", + (unsigned long)output_->encoding.bits_per_sample, out_rate, in_rate, + (unsigned long)output_->signal.channels); + return true; } @@ -78,11 +89,27 @@ void Player::stop() { stop_ = 1; } +void Player::start(pipeline::IReceiver& input) { + input_ = &input; + core::Thread::start(); +} + +size_t Player::get_sample_rate() const { + if (!output_) { + roc_panic("player: can't get sample rate for non-open output file or device"); + } + return size_t(output_->signal.rate); +} + void Player::run() { roc_log(LogDebug, "player: starting thread"); + if (!input_) { + roc_panic("player: thread is started not from the start() call"); + } + if (!output_) { - roc_panic("player: thread is started before open() returnes success"); + roc_panic("player: thread is started before open() returned success"); } loop_(); @@ -115,7 +142,7 @@ void Player::loop_() { SOX_SAMPLE_LOCALS; while (!stop_) { - pipeline::IReceiver::Status status = input_.read(frame); + pipeline::IReceiver::Status status = input_->read(frame); if (status == pipeline::IReceiver::Inactive) { if (oneshot_ && n_bufs_ != 0) { diff --git a/src/modules/roc_sndio/target_sox/roc_sndio/player.h b/src/modules/roc_sndio/target_sox/roc_sndio/player.h index e06c83bbb..22969f688 100644 --- a/src/modules/roc_sndio/target_sox/roc_sndio/player.h +++ b/src/modules/roc_sndio/target_sox/roc_sndio/player.h @@ -35,11 +35,9 @@ class Player : public core::Thread { //! Initialize. //! //! @b Parameters - //! - @p input is used to read samples //! - @p channels defines bitmask of enabled channels in input buffers //! - @p sample_rate defines sample rate of input buffers - Player(pipeline::IReceiver& input, - core::BufferPool& buffer_pool, + Player(core::BufferPool& buffer_pool, core::IAllocator& allocator, bool oneshot, packet::channel_mask_t channels, @@ -60,11 +58,23 @@ class Player : public core::Thread { //! Should be called once before calling start(). bool open(const char* name, const char* type = NULL); + //! Start reading samples in a separate thread. + //! + //! @b Parameters + //! - @p input is used to read samples. + void start(pipeline::IReceiver& input); + //! Stop thread. //! @remarks //! Can be called from any thread. void stop(); + //! Get sample rate of an output file or a device. + //! + //! @pre + //! Output file or device should be opened. + size_t get_sample_rate() const; + private: virtual void run(); @@ -76,7 +86,7 @@ class Player : public core::Thread { sox_format_t* output_; sox_signalinfo_t out_signal_; - pipeline::IReceiver& input_; + pipeline::IReceiver* input_; core::BufferPool& buffer_pool_; core::IAllocator& allocator_; diff --git a/src/tests/roc_sndio/target_sox/test_player_recorder.cpp b/src/tests/roc_sndio/target_sox/test_player_recorder.cpp index acf60612c..431c3dd6f 100644 --- a/src/tests/roc_sndio/target_sox/test_player_recorder.cpp +++ b/src/tests/roc_sndio/target_sox/test_player_recorder.cpp @@ -11,6 +11,7 @@ #include "roc_core/buffer_pool.h" #include "roc_core/heap_allocator.h" +#include "roc_core/macros.h" #include "roc_core/stddefs.h" #include "roc_core/temp_file.h" #include "roc_sndio/init.h" @@ -115,41 +116,55 @@ TEST_GROUP(player_recorder) { }; TEST(player_recorder, player_noop) { - MockReceiver receiver; - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); } TEST(player_recorder, player_error) { - MockReceiver receiver; - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); CHECK(!player.open("/bad/file")); } TEST(player_recorder, player_start_stop) { MockReceiver receiver; - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); core::TempFile file("test.wav"); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.stop(); player.join(); } TEST(player_recorder, player_stop_start) { MockReceiver receiver; - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); core::TempFile file("test.wav"); CHECK(player.open(file.path())); player.stop(); - player.start(); + player.start(receiver); player.join(); } +TEST(player_recorder, player_open_file_zero_sample_rate) { + Player player(buffer_pool, allocator, true, ChMask, 0); + + core::TempFile file("test.wav"); + CHECK(player.open(file.path())); + CHECK(player.get_sample_rate() != 0); +} + +TEST(player_recorder, player_open_file_non_zero_sample_rate) { + Player player(buffer_pool, allocator, true, ChMask, SampleRate); + + core::TempFile file("test.wav"); + CHECK(player.open(file.path())); + CHECK(player.get_sample_rate() == SampleRate); +} + TEST(player_recorder, recorder_noop) { MockWriter writer; Recorder recorder(writer, buffer_pool, ChMask, FrameSize, SampleRate); @@ -168,12 +183,12 @@ TEST(player_recorder, recorder_start_stop) { MockReceiver receiver; receiver.add(NumSamples); - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); core::TempFile file("test.wav"); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.join(); MockWriter writer; @@ -192,12 +207,12 @@ TEST(player_recorder, recorder_stop_start) { MockReceiver receiver; receiver.add(NumSamples); - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); core::TempFile file("test.wav"); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.join(); MockWriter writer; @@ -216,12 +231,12 @@ TEST(player_recorder, write_read) { MockReceiver receiver; receiver.add(NumSamples); - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); core::TempFile file("test.wav"); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.join(); CHECK(receiver.num_returned() >= NumSamples - MaxBufSize); @@ -246,10 +261,10 @@ TEST(player_recorder, overwrite) { receiver.add(NumSamples); { - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.join(); } @@ -259,10 +274,10 @@ TEST(player_recorder, overwrite) { CHECK(num_returned1 >= NumSamples - MaxBufSize); { - Player player(receiver, buffer_pool, allocator, true, ChMask, SampleRate); + Player player(buffer_pool, allocator, true, ChMask, SampleRate); CHECK(player.open(file.path())); - player.start(); + player.start(receiver); player.join(); } diff --git a/src/tools/roc_recv/main.cpp b/src/tools/roc_recv/main.cpp index 74f06c160..896591e63 100644 --- a/src/tools/roc_recv/main.cpp +++ b/src/tools/roc_recv/main.cpp @@ -107,11 +107,13 @@ int main(int argc, char** argv) { config.timing = (args.timing_arg == timing_arg_yes); config.default_session.beep = args.beep_flag; + size_t sample_rate = 0; + if (args.rate_given) { if (!check_ge("rate", args.rate_arg, 1)) { return 1; } - config.sample_rate = (size_t)args.rate_arg; + sample_rate = (size_t)args.rate_arg; } if (args.timeout_given) { @@ -148,6 +150,23 @@ int main(int argc, char** argv) { core::BufferPool sample_buffer_pool(allocator, MaxFrameSize, 1); packet::PacketPool packet_pool(allocator, 1); + sndio::Player player(sample_buffer_pool, allocator, args.oneshot_flag, + config.channels, sample_rate); + + if (!player.open(args.output_arg, args.type_arg)) { + roc_log(LogError, "can't open output file or device: %s %s", args.output_arg, + args.type_arg); + return 1; + } + + config.sample_rate = player.get_sample_rate(); + + if (config.sample_rate == 0) { + roc_log(LogError, "can't detect output sample rate, try to set it " + "explicitly with --rate option"); + return 1; + } + rtp::FormatMap format_map; pipeline::Receiver receiver(config, format_map, packet_pool, byte_buffer_pool, @@ -187,18 +206,9 @@ int main(int argc, char** argv) { } } - sndio::Player player(receiver, sample_buffer_pool, allocator, args.oneshot_flag, - config.channels, config.sample_rate); - - if (!player.open(args.output_arg, args.type_arg)) { - roc_log(LogError, "can't open output file or device: %s %s", args.output_arg, - args.type_arg); - return 1; - } - trx.start(); - player.start(); + player.start(receiver); player.join(); trx.stop();