Implement stereo audio volume control
This commit is contained in:
parent
acc2fd486e
commit
bd16679b97
5 changed files with 84 additions and 28 deletions
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue