Implement stereo audio volume control

This commit is contained in:
Nikita Lisitsa 2022-10-06 16:24:42 +03:00
parent acc2fd486e
commit bd16679b97
5 changed files with 84 additions and 28 deletions

View file

@ -63,12 +63,8 @@ struct audio_app
: app::app("Audio example") : app::app("Audio example")
{ {
mixer_ = audio::make_mixer(); mixer_ = audio::make_mixer();
volume_control_ = audio::volume_stereo(mixer_, 0.5f, 0.5f, 0.1f);
auto [ dup1, dup2 ] = audio::duplicate(mixer_); engine_.output()->stream(volume_control_);
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);
} }
void on_key_down(SDL_Keycode key) override void on_key_down(SDL_Keycode key) override
@ -101,14 +97,14 @@ struct audio_app
float const time = clock_.count(); float const time = clock_.count();
float volume = std::sin(time); float volume = std::sin(time);
left_volume_->gain(0.5f + 0.5f * volume); volume_control_->gain_left(0.5f + 0.5f * volume);
right_volume_->gain(0.5f - 0.5f * volume); volume_control_->gain_right(0.5f - 0.5f * volume);
} }
private: private:
audio::engine engine_; audio::engine engine_;
audio::mixer_ptr mixer_; audio::mixer_ptr mixer_;
std::shared_ptr<audio::volume_control> left_volume_, right_volume_; std::shared_ptr<audio::volume_control_stereo> volume_control_;
std::map<SDL_Keycode, audio::channel_ptr> channels_; std::map<SDL_Keycode, audio::channel_ptr> channels_;
util::clock<> clock_; util::clock<> clock_;

View file

@ -15,6 +15,19 @@ namespace psemek::audio
virtual float smoothness(float value) = 0; 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_control> volume(stream_ptr stream, float gain = 1.f, float smoothness = 0.f); std::shared_ptr<volume_control> volume(stream_ptr stream, float gain = 1.f, float smoothness = 0.f);
std::shared_ptr<volume_control_stereo> volume_stereo(stream_ptr stream, float gain_left = 1.f, float gain_right = 1.f, float smoothness = 0.f);
} }

View file

@ -9,10 +9,13 @@ namespace psemek::audio
struct volume_base 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_left() const { return gain_[0].load(); }
float gain(float value) { return gain_.exchange(value); } 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() const;
float smoothness(float value); float smoothness(float value);
@ -20,9 +23,9 @@ namespace psemek::audio
void apply(float * data, std::size_t sample_count); void apply(float * data, std::size_t sample_count);
private: private:
std::atomic<float> gain_; std::atomic<float> gain_[2];
std::atomic<float> smoothness_multiplier_; std::atomic<float> smoothness_multiplier_;
float real_gain_; float real_gain_[2];
float last_sample_src_[2]; float last_sample_src_[2];
float last_sample_tgt_[2]; float last_sample_tgt_[2];
}; };

View file

@ -11,12 +11,50 @@ namespace psemek::audio
: volume_control : volume_control
{ {
volume_control_impl(stream_ptr stream, float gain, float smoothness) volume_control_impl(stream_ptr stream, float gain, float smoothness)
: base_(gain, smoothness) : base_(gain, gain, smoothness)
, stream_(std::move(stream)) , stream_(std::move(stream))
{} {}
float gain() const override { return base_.gain(); } float gain() const override { return base_.gain_left(); }
float gain(float value) override { return base_.gain(value); } 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<std::size_t> 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() const override { return base_.smoothness(); }
float smoothness(float value) override { return base_.smoothness(value); } float smoothness(float value) override { return base_.smoothness(value); }
@ -50,4 +88,9 @@ namespace psemek::audio
return std::make_shared<volume_control_impl>(std::move(stream), gain, smoothness); return std::make_shared<volume_control_impl>(std::move(stream), gain, smoothness);
} }
std::shared_ptr<volume_control_stereo> volume_stereo(stream_ptr stream, float gain_left, float gain_right, float smoothness)
{
return std::make_shared<volume_control_stereo_impl>(std::move(stream), gain_left, gain_right, smoothness);
}
} }

View file

@ -7,10 +7,10 @@
namespace psemek::audio namespace psemek::audio
{ {
volume_base::volume_base(float gain, float smoothness) volume_base::volume_base(float gain_left, float gain_right, float smoothness)
: gain_{gain} : gain_{gain_left, gain_right}
, smoothness_multiplier_{smoothness_to_multiplier(smoothness)} , smoothness_multiplier_{smoothness_to_multiplier(smoothness)}
, real_gain_{gain} , real_gain_{gain_left, gain_right}
, last_sample_src_{0.f, 0.f} , last_sample_src_{0.f, 0.f}
, last_sample_tgt_{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) 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(); 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; auto old = target;
target = last_tgt + (old - last_src) * real_gain_; target = last_sample_tgt_[i] + (old - last_sample_src_[i]) * real_gain_[i];
last_src = old; last_sample_src_[i] = old;
last_tgt = target; last_sample_tgt_[i] = target;
}; };
auto end = data + sample_count; auto end = data + sample_count;
for (auto p = data; p < end;) for (auto p = data; p < end;)
{ {
apply_impl(*p++, last_sample_src_[0], last_sample_tgt_[0]); apply_impl(*p++, 0);
apply_impl(*p++, last_sample_src_[1], last_sample_tgt_[1]); 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);
} }
} }