Add all-pass echo filter

This commit is contained in:
Nikita Lisitsa 2023-10-04 14:34:25 +03:00
parent 9e0babfd2c
commit 51a5bcd3bc
2 changed files with 81 additions and 0 deletions

View file

@ -0,0 +1,13 @@
#pragma once
#include <psemek/audio/stream.hpp>
#include <psemek/audio/duration.hpp>
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);
}

View file

@ -0,0 +1,68 @@
#include <psemek/audio/effect/all_pass.hpp>
#include <vector>
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<std::size_t> length() const override
{
return std::nullopt;
}
// y[n] = (g·x[n]) + x[nM] + (g·y[nM])
std::size_t read(util::span<float> 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<float> buffer_;
std::size_t buffer_pos_ = 0;
float gain_;
};
}
stream_ptr all_pass(stream_ptr stream, duration delay, float gain)
{
return std::make_shared<all_pass_impl>(std::move(stream), delay, gain);
}
}