Skip to content

Commit

Permalink
Add BiquadTDF2
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiashienzsch committed Feb 24, 2024
1 parent 0405896 commit 1360490
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ if(NOT CMAKE_CROSSCOMPILING)

"lib/grit/audio/envelope/envelope_follower_test.cpp"

"lib/grit/audio/filter/biquad_test.cpp"

"lib/grit/audio/music/note_test.cpp"

"lib/grit/audio/noise/dither_test.cpp"
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ target_sources(gritwave-grit INTERFACE
"grit/audio/envelope/envelope_follower.hpp"

"grit/audio/filter.hpp"
"grit/audio/filter/biquad.hpp"
"grit/audio/filter/dynamic_smoothing.hpp"

"grit/audio/mix.hpp"
Expand Down
1 change: 1 addition & 0 deletions lib/grit/audio/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
/// \defgroup grit-audio-filter Filter
/// \ingroup grit-audio

#include <grit/audio/filter/biquad.hpp>
#include <grit/audio/filter/dynamic_smoothing.hpp>
77 changes: 77 additions & 0 deletions lib/grit/audio/filter/biquad.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <etl/algorithm.hpp>
#include <etl/array.hpp>
#include <etl/concepts.hpp>
#include <etl/span.hpp>

namespace grit {

/// Calculates coefficents for 2nd order biquad filters
/// \see BiquadTDF2
/// \ingroup grit-audio-filter
template<etl::floating_point Float>
struct BiquadCoefficients
{
using value_type = Float;
using SampleType = Float;

[[nodiscard]] static constexpr auto makeBypass() -> etl::array<Float, 6>
{
return {Float(1), Float(0), Float(0), Float(1), Float(0), Float(0)};
}
};

/// \brief 2nd order IIR filter using the transpose direct form 2 structure.
/// \ingroup grit-audio-filter
template<etl::floating_point Float>
struct BiquadTDF2
{
using value_type = Float;
using SampleType = Float;
using Coefficients = BiquadCoefficients<Float>;

constexpr BiquadTDF2() = default;

constexpr auto setCoefficients(etl::span<Float const, 6> coefficients) -> void
{
etl::copy(coefficients.begin(), coefficients.end(), _coefficients.begin());
}

constexpr auto reset() -> void
{
_z[0] = Float(0);
_z[1] = Float(0);
}

[[nodiscard]] constexpr auto operator()(Float x) -> Float
{
auto const b0 = _coefficients[Index::B0];
auto const b1 = _coefficients[Index::B1];
auto const b2 = _coefficients[Index::B2];
auto const a1 = _coefficients[Index::A1];
auto const a2 = _coefficients[Index::A2];

auto const y = b0 * x + _z[0];
_z[0] = b1 * x - a1 * y + _z[1];
_z[1] = b2 * x - a2 * y;
return y;
}

private:
enum Index
{
B0,
B1,
B2,
A0,
A1,
A2,
NumCoefficients,
};

etl::array<Float, NumCoefficients> _coefficients{Coefficients::makeBypass()};
etl::array<Float, 2> _z{};
};

} // namespace grit
53 changes: 53 additions & 0 deletions lib/grit/audio/filter/biquad_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "biquad.hpp"

#include <etl/random.hpp>

#include <catch2/catch_get_random_seed.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>

TEMPLATE_TEST_CASE("audio/filter: BiquadCoefficients::makeBypass", "", float, double)
{
using Float = TestType;
using Coefficients = grit::BiquadCoefficients<Float>;

static constexpr auto bypass = Coefficients::makeBypass();
STATIC_REQUIRE(bypass.size() == 6U);

REQUIRE_THAT(bypass[0], Catch::Matchers::WithinAbs(1.0, 1e-6));
REQUIRE_THAT(bypass[1], Catch::Matchers::WithinAbs(0.0, 1e-6));
REQUIRE_THAT(bypass[2], Catch::Matchers::WithinAbs(0.0, 1e-6));
REQUIRE_THAT(bypass[3], Catch::Matchers::WithinAbs(1.0, 1e-6));
REQUIRE_THAT(bypass[4], Catch::Matchers::WithinAbs(0.0, 1e-6));
REQUIRE_THAT(bypass[5], Catch::Matchers::WithinAbs(0.0, 1e-6));
}

TEMPLATE_TEST_CASE("audio/filter: BiquadTDF2", "", float, double)
{
using Float = TestType;
using Filter = grit::BiquadTDF2<Float>;

static constexpr auto iterations = 1'000;

auto rng = etl::xoshiro128plusplus{Catch::getSeed()};
auto dist = etl::uniform_real_distribution<Float>{Float(-1), Float(+1)};

SECTION("default coefficents are bypass")
{
auto filter = Filter{};
for (auto i{0}; i < iterations; ++i) {
auto const x = dist(rng);
auto const y = filter(x);
REQUIRE_THAT(y, Catch::Matchers::WithinAbs(x, 1e-6));
}

filter.setCoefficients(Filter::Coefficients::makeBypass());
filter.reset();

for (auto i{0}; i < iterations; ++i) {
auto const x = dist(rng);
auto const y = filter(x);
REQUIRE_THAT(y, Catch::Matchers::WithinAbs(x, 1e-6));
}
}
}

0 comments on commit 1360490

Please sign in to comment.