diff --git a/libs/audio/include/psemek/audio/effect/filter.hpp b/libs/audio/include/psemek/audio/effect/filter.hpp new file mode 100644 index 00000000..72f4081e --- /dev/null +++ b/libs/audio/include/psemek/audio/effect/filter.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace psemek::audio +{ + + // Turns x[n] into a0*x[n] + a1*x[n-1] + stream_ptr feedforward_filter(stream_ptr stream, float a0, float a1); + + stream_ptr low_pass_filter(stream_ptr stream); + stream_ptr high_pass_filter(stream_ptr stream); + +} diff --git a/libs/audio/source/effect/filter.cpp b/libs/audio/source/effect/filter.cpp new file mode 100644 index 00000000..5ed8d9f4 --- /dev/null +++ b/libs/audio/source/effect/filter.cpp @@ -0,0 +1,68 @@ +#include + +namespace psemek::audio +{ + + namespace + { + + struct feedforward_filter_impl + : stream + { + feedforward_filter_impl(stream_ptr stream, float a0, float a1) + : stream_(std::move(stream)) + , a0_(a0) + , a1_(a1) + {} + + std::optional length() const override + { + return stream_->length(); + } + + std::size_t read(float * data, std::size_t sample_count) override + { + std::size_t count = stream_->read(data, sample_count); + + for (std::size_t i = 0; i < count; i += 2) + { + std::swap(prev_[0], data[i + 0]); + std::swap(prev_[1], data[i + 1]); + + data[i + 0] = prev_[0] * a0_ + data[i + 0] * a1_; + data[i + 1] = prev_[1] * a0_ + data[i + 1] * a1_; + } + + return count; + } + + std::size_t played() const override + { + return stream_->played(); + } + + private: + stream_ptr stream_; + float a0_; + float a1_; + float prev_[2]{0.f}; + }; + + } + + stream_ptr feedforward_filter(stream_ptr stream, float a0, float a1) + { + return std::make_shared(std::move(stream), a0, a1); + } + + stream_ptr low_pass_filter(stream_ptr stream) + { + return feedforward_filter(std::move(stream), 1.f, 1.f); + } + + stream_ptr high_pass_filter(stream_ptr stream) + { + return feedforward_filter(std::move(stream), 1.f, -1.f); + } + +}