diff --git a/examples/audio.cpp b/examples/audio.cpp index 97891ff8..057f57f8 100644 --- a/examples/audio.cpp +++ b/examples/audio.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,9 @@ struct audio_app { mixer_ = audio::make_mixer(); volume_control_ = audio::volume_stereo(mixer_, 0.5f, 0.5f, 0.1f); - engine_.output()->stream(audio::compressor(volume_control_, audio::from_db(-2.f), 0.95f, 0.002f, 1.f, audio::from_db(1.f))); + auto compressor = audio::compressor(volume_control_, audio::from_db(-2.f), 0.95f, 0.002f, 1.f, audio::from_db(1.f)); + pause_control_ = audio::pause(compressor, false, 0.01f); + engine_.output()->stream(pause_control_); } void on_key_down(SDL_Keycode key) override @@ -80,7 +83,12 @@ struct audio_app { int midi = key_to_midi.at(key); auto tone = audio::sine_wave(440.f * std::pow(2.f, (midi - 69) / 12.f)); - channels_[key] = mixer_->add(audio::fade_in(tone, 0.01f)); + channels_[key] = mixer_->add(audio::fade_in(tone, 0.002f)); + } + + if (key == SDLK_SPACE) + { + pause_control_->paused(!pause_control_->paused()); } } @@ -131,6 +139,7 @@ private: audio::engine engine_; audio::mixer_ptr mixer_; std::shared_ptr volume_control_; + std::shared_ptr pause_control_; std::map channels_; util::clock<> clock_; diff --git a/libs/audio/include/psemek/audio/effect/pause.hpp b/libs/audio/include/psemek/audio/effect/pause.hpp new file mode 100644 index 00000000..76564050 --- /dev/null +++ b/libs/audio/include/psemek/audio/effect/pause.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace psemek::audio +{ + + struct pause_control + : stream + { + virtual bool paused() const = 0; + virtual bool paused(bool value) = 0; + + virtual void pause() { paused(true); } + virtual void resume() { paused(false); } + }; + + std::shared_ptr pause(stream_ptr stream, bool paused = false, duration length = 0.01f); + +} diff --git a/libs/audio/source/effect/pause.cpp b/libs/audio/source/effect/pause.cpp new file mode 100644 index 00000000..5ac8eba0 --- /dev/null +++ b/libs/audio/source/effect/pause.cpp @@ -0,0 +1,95 @@ +#include +#include + +#include + +namespace psemek::audio +{ + + namespace + { + + struct pause_control_impl + : pause_control + { + pause_control_impl(stream_ptr stream, bool paused, duration length) + : stream_(std::move(stream)) + , paused_{paused} + , length_(length) + , level_(paused ? 0 : length_.samples()) + {} + + bool paused() const override + { + return paused_.load(); + } + + bool paused(bool value) override + { + return paused_.exchange(value); + } + + std::optional length() const override + { + return stream_->length(); + } + + std::size_t read(float * data, std::size_t sample_count) override + { + bool const paused = paused_.load(); + + if (paused) + { + auto result = stream_->read(data, std::min(sample_count, level_)); + + for (std::size_t i = 0; i < result; i += 2) + { + float gain = static_cast(level_) / length_.samples(); + data[i + 0] *= gain; + data[i + 1] *= gain; + level_ -= 2; + } + + std::fill(data + result, data + sample_count, 0.f); + return sample_count; + } + else + { + auto result = stream_->read(data, sample_count); + + auto const max_level = static_cast(length_.samples()); + + for (std::size_t i = 0; i < result; i += 2) + { + float gain = static_cast(level_) / max_level; + data[i + 0] *= gain; + data[i + 1] *= gain; + + if (level_ < max_level) + level_ += 2; + } + + return result; + } + } + + std::size_t played() const override + { + return stream_->played(); + } + + private: + stream_ptr stream_; + std::atomic paused_; + duration length_; + std::size_t level_; + }; + + } + + std::shared_ptr pause(stream_ptr stream, bool paused, duration length) + { + return std::make_shared(std::move(stream), paused, length); + } + +}