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")
{
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<audio::volume_control> left_volume_, right_volume_;
std::shared_ptr<audio::volume_control_stereo> volume_control_;
std::map<SDL_Keycode, audio::channel_ptr> channels_;
util::clock<> clock_;

View file

@ -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_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
{
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<float> gain_;
std::atomic<float> gain_[2];
std::atomic<float> smoothness_multiplier_;
float real_gain_;
float real_gain_[2];
float last_sample_src_[2];
float last_sample_tgt_[2];
};

View file

@ -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<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(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);
}
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
{
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);
}
}