diff --git a/libs/audio/include/psemek/audio/effect/all_pass.hpp b/libs/audio/include/psemek/audio/effect/all_pass.hpp new file mode 100644 index 00000000..03f49687 --- /dev/null +++ b/libs/audio/include/psemek/audio/effect/all_pass.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace psemek::audio +{ + + // All-pass filter + // Turns x[n] into y[n] = - gain * x[n] + x[n - delay] + gain * y[n - delay] + stream_ptr all_pass(stream_ptr stream, duration delay, float gain); + +} diff --git a/libs/audio/source/effect/all_pass.cpp b/libs/audio/source/effect/all_pass.cpp new file mode 100644 index 00000000..c93edf05 --- /dev/null +++ b/libs/audio/source/effect/all_pass.cpp @@ -0,0 +1,68 @@ +#include + +#include + +namespace psemek::audio +{ + + namespace + { + + struct all_pass_impl + : stream + { + all_pass_impl(stream_ptr stream, duration delay, float gain) + : stream_(std::move(stream)) + , buffer_(2 * std::max(1l, delay.samples()), 0.f) + , gain_(gain) + {} + + std::optional length() const override + { + return std::nullopt; + } + + // y[n] = (−g·x[n]) + x[n−M] + (g·y[n−M]) + std::size_t read(util::span samples) override + { + auto count = stream_->read(samples); + + for (std::size_t i = 0; i < count; ++i) + { + float input = samples[i]; + samples[i] = - gain_ * input + buffer_[buffer_pos_]; + buffer_[buffer_pos_] = input + gain_ * samples[i]; + ++buffer_pos_; + buffer_pos_ %= buffer_.size(); + } + + for (std::size_t i = count; i < samples.size(); ++i) + { + samples[i] = buffer_[buffer_pos_]; + buffer_[buffer_pos_] = gain_ * buffer_[buffer_pos_]; + ++buffer_pos_; + buffer_pos_ %= buffer_.size(); + } + + return samples.size(); + } + + std::size_t played() const override + { + return stream_->played(); + } + + private: + stream_ptr stream_; + std::vector buffer_; + std::size_t buffer_pos_ = 0; + float gain_; + }; + + } + + stream_ptr all_pass(stream_ptr stream, duration delay, float gain) + { + return std::make_shared(std::move(stream), delay, gain); + } +}