From bd16679b97d203dd671877ed890790a8d7c4a91c Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 6 Oct 2022 16:24:42 +0300 Subject: [PATCH] Implement stereo audio volume control --- examples/audio.cpp | 14 ++---- .../include/psemek/audio/effect/volume.hpp | 13 +++++ .../psemek/audio/effect/volume_base.hpp | 13 +++-- libs/audio/source/effect/volume.cpp | 49 +++++++++++++++++-- libs/audio/source/effect/volume_base.cpp | 23 ++++----- 5 files changed, 84 insertions(+), 28 deletions(-) diff --git a/examples/audio.cpp b/examples/audio.cpp index 8370efc1..aa54d3e1 100644 --- a/examples/audio.cpp +++ b/examples/audio.cpp @@ -63,12 +63,8 @@ struct audio_app : app::app("Audio example") { mixer_ = audio::make_mixer(); - - auto [ dup1, dup2 ] = audio::duplicate(mixer_); - left_volume_ = audio::volume(dup1, 0.5f, 0.1f); - right_volume_ = audio::volume(dup2, 0.5f, 0.1f); - auto result = audio::stereo(left_volume_, right_volume_); - engine_.output()->stream(result); + volume_control_ = audio::volume_stereo(mixer_, 0.5f, 0.5f, 0.1f); + engine_.output()->stream(volume_control_); } void on_key_down(SDL_Keycode key) override @@ -101,14 +97,14 @@ struct audio_app float const time = clock_.count(); float volume = std::sin(time); - left_volume_->gain(0.5f + 0.5f * volume); - right_volume_->gain(0.5f - 0.5f * volume); + volume_control_->gain_left(0.5f + 0.5f * volume); + volume_control_->gain_right(0.5f - 0.5f * volume); } private: audio::engine engine_; audio::mixer_ptr mixer_; - std::shared_ptr left_volume_, right_volume_; + std::shared_ptr volume_control_; std::map channels_; util::clock<> clock_; diff --git a/libs/audio/include/psemek/audio/effect/volume.hpp b/libs/audio/include/psemek/audio/effect/volume.hpp index 43ace74e..45d685d8 100644 --- a/libs/audio/include/psemek/audio/effect/volume.hpp +++ b/libs/audio/include/psemek/audio/effect/volume.hpp @@ -15,6 +15,19 @@ namespace psemek::audio virtual float smoothness(float value) = 0; }; + struct volume_control_stereo + : stream + { + virtual float gain_left() const = 0; + virtual float gain_right() const = 0; + virtual float gain_left(float value) = 0; + virtual float gain_right(float value) = 0; + + virtual float smoothness() const = 0; + virtual float smoothness(float value) = 0; + }; + std::shared_ptr volume(stream_ptr stream, float gain = 1.f, float smoothness = 0.f); + std::shared_ptr volume_stereo(stream_ptr stream, float gain_left = 1.f, float gain_right = 1.f, float smoothness = 0.f); } diff --git a/libs/audio/include/psemek/audio/effect/volume_base.hpp b/libs/audio/include/psemek/audio/effect/volume_base.hpp index c05c2bf4..85b81066 100644 --- a/libs/audio/include/psemek/audio/effect/volume_base.hpp +++ b/libs/audio/include/psemek/audio/effect/volume_base.hpp @@ -9,10 +9,13 @@ namespace psemek::audio struct volume_base { - volume_base(float gain, float smoothness); + volume_base(float gain_left, float gain_right, float smoothness); - float gain() const { return gain_.load(); } - float gain(float value) { return gain_.exchange(value); } + float gain_left() const { return gain_[0].load(); } + float gain_right() const { return gain_[1].load(); } + + float gain_left(float value) { return gain_[0].exchange(value); } + float gain_right(float value) { return gain_[1].exchange(value); } float smoothness() const; float smoothness(float value); @@ -20,9 +23,9 @@ namespace psemek::audio void apply(float * data, std::size_t sample_count); private: - std::atomic gain_; + std::atomic gain_[2]; std::atomic smoothness_multiplier_; - float real_gain_; + float real_gain_[2]; float last_sample_src_[2]; float last_sample_tgt_[2]; }; diff --git a/libs/audio/source/effect/volume.cpp b/libs/audio/source/effect/volume.cpp index 791fc0f2..2beacbc8 100644 --- a/libs/audio/source/effect/volume.cpp +++ b/libs/audio/source/effect/volume.cpp @@ -11,12 +11,50 @@ namespace psemek::audio : volume_control { volume_control_impl(stream_ptr stream, float gain, float smoothness) - : base_(gain, smoothness) + : base_(gain, gain, smoothness) , stream_(std::move(stream)) {} - float gain() const override { return base_.gain(); } - float gain(float value) override { return base_.gain(value); } + float gain() const override { return base_.gain_left(); } + float gain(float value) override { base_.gain_left(value); return base_.gain_right(value); } + + float smoothness() const override { return base_.smoothness(); } + float smoothness(float value) override { return base_.smoothness(value); } + + std::optional length() const override + { + return stream_->length(); + } + + std::size_t played() const override + { + return stream_->played(); + } + + std::size_t read(float * data, std::size_t sample_count) override + { + auto result = stream_->read(data, sample_count); + base_.apply(data, result); + return result; + } + + private: + volume_base base_; + stream_ptr stream_; + }; + + struct volume_control_stereo_impl + : volume_control_stereo + { + volume_control_stereo_impl(stream_ptr stream, float gain_left, float gain_right, float smoothness) + : base_(gain_left, gain_right, smoothness) + , stream_(std::move(stream)) + {} + + float gain_left() const override { return base_.gain_left(); } + float gain_right() const override { return base_.gain_right(); } + float gain_left(float value) override { return base_.gain_left(value); } + float gain_right(float value) override { return base_.gain_right(value); } float smoothness() const override { return base_.smoothness(); } float smoothness(float value) override { return base_.smoothness(value); } @@ -50,4 +88,9 @@ namespace psemek::audio return std::make_shared(std::move(stream), gain, smoothness); } + std::shared_ptr volume_stereo(stream_ptr stream, float gain_left, float gain_right, float smoothness) + { + return std::make_shared(std::move(stream), gain_left, gain_right, smoothness); + } + } diff --git a/libs/audio/source/effect/volume_base.cpp b/libs/audio/source/effect/volume_base.cpp index 3914eb53..eec2eb62 100644 --- a/libs/audio/source/effect/volume_base.cpp +++ b/libs/audio/source/effect/volume_base.cpp @@ -7,10 +7,10 @@ namespace psemek::audio { - volume_base::volume_base(float gain, float smoothness) - : gain_{gain} + volume_base::volume_base(float gain_left, float gain_right, float smoothness) + : gain_{gain_left, gain_right} , smoothness_multiplier_{smoothness_to_multiplier(smoothness)} - , real_gain_{gain} + , real_gain_{gain_left, gain_right} , last_sample_src_{0.f, 0.f} , last_sample_tgt_{0.f, 0.f} {} @@ -28,24 +28,25 @@ namespace psemek::audio void volume_base::apply(float * data, std::size_t sample_count) { - float gain = gain_.load(); + float gain[2] = {gain_[0].load(), gain_[1].load()}; float smoothness_multiplier = smoothness_multiplier_.load(); - auto apply_impl = [this](float & target, float & last_src, float & last_tgt) + auto apply_impl = [this](float & target, int i) { auto old = target; - target = last_tgt + (old - last_src) * real_gain_; - last_src = old; - last_tgt = target; + target = last_sample_tgt_[i] + (old - last_sample_src_[i]) * real_gain_[i]; + last_sample_src_[i] = old; + last_sample_tgt_[i] = target; }; auto end = data + sample_count; for (auto p = data; p < end;) { - apply_impl(*p++, last_sample_src_[0], last_sample_tgt_[0]); - apply_impl(*p++, last_sample_src_[1], last_sample_tgt_[1]); + apply_impl(*p++, 0); + apply_impl(*p++, 1); - smooth_update(real_gain_, gain, smoothness_multiplier); + smooth_update(real_gain_[0], gain[0], smoothness_multiplier); + smooth_update(real_gain_[1], gain[1], smoothness_multiplier); } }