kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

commit 0902232812a92dcdcf78d2bbb05f9187cdde6270
parent dfb5ea94d8c96e4d3384a1c7dc9fac109881a4a6
Author: Stephen Larew <stephen@slarew.net>
Date:   Thu, 20 Feb 2020 12:30:28 -0800

factor out delay_state from delay expression

Diffstat:
Minclude/kfr/dsp/delay.hpp | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Minclude/kfr/dsp/fir.hpp | 21+--------------------
Ainclude/kfr/dsp/state_holder.hpp | 41+++++++++++++++++++++++++++++++++++++++++
Mtests/dsp_test.cpp | 15+++++++++++++++
4 files changed, 131 insertions(+), 40 deletions(-)

diff --git a/include/kfr/dsp/delay.hpp b/include/kfr/dsp/delay.hpp @@ -25,42 +25,73 @@ */ #pragma once +#include "../base/basic_expressions.hpp" #include "../base/expression.hpp" #include "../base/univector.hpp" +#include "state_holder.hpp" namespace kfr { inline namespace CMT_ARCH_NAME { +template <typename T, size_t samples, univector_tag Tag = samples> +struct delay_state +{ + template <size_t S2 = samples, KFR_ENABLE_IF(S2 == Tag)> + delay_state() : data({ 0 }), cursor(0) + { + } + + template <size_t S2 = samples, KFR_ENABLE_IF(S2 != Tag)> + delay_state() : data(samples), cursor(0) + { + } + + mutable univector<T, Tag> data; + mutable size_t cursor; +}; + +template <typename T> +struct delay_state<T, 1, 1> +{ + mutable T data = T(0); +}; + namespace internal { -template <size_t delay, typename E> + +template <size_t delay, typename E, bool stateless, univector_tag STag> struct expression_delay : expression_with_arguments<E> { using value_type = value_type_of<E>; using T = value_type; using expression_with_arguments<E>::expression_with_arguments; + expression_delay(E&& e, const delay_state<T, delay, STag>& state) + : expression_with_arguments<E>(std::forward<E>(e)), state(state) + { + } + template <size_t N, KFR_ENABLE_IF(N <= delay)> friend KFR_INTRINSIC vec<T, N> get_elements(const expression_delay& self, cinput_t cinput, size_t index, vec_shape<T, N>) { vec<T, N> out; - size_t c = self.cursor; - self.data.ringbuf_read(c, out); + size_t c = self.state.s.cursor; + self.state.s.data.ringbuf_read(c, out); const vec<T, N> in = self.argument_first(cinput, index, vec_shape<T, N>()); - self.data.ringbuf_write(self.cursor, in); + self.state.s.data.ringbuf_write(self.state.s.cursor, in); return out; } friend vec<T, 1> get_elements(const expression_delay& self, cinput_t cinput, size_t index, vec_shape<T, 1>) { T out; - size_t c = self.cursor; - self.data.ringbuf_read(c, out); + size_t c = self.state.s.cursor; + self.state.s.data.ringbuf_read(c, out); const T in = self.argument_first(cinput, index, vec_shape<T, 1>())[0]; - self.data.ringbuf_write(self.cursor, in); + self.state.s.data.ringbuf_write(self.state.s.cursor, in); return out; } template <size_t N, KFR_ENABLE_IF(N > delay)> @@ -68,34 +99,38 @@ struct expression_delay : expression_with_arguments<E> vec_shape<T, N>) { vec<T, delay> out; - size_t c = self.cursor; - self.data.ringbuf_read(c, out); + size_t c = self.state.s.cursor; + self.state.s.data.ringbuf_read(c, out); const vec<T, N> in = self.argument_first(cinput, index, vec_shape<T, N>()); - self.data.ringbuf_write(self.cursor, slice<N - delay, delay>(in)); + self.state.s.data.ringbuf_write(self.state.s.cursor, slice<N - delay, delay>(in)); return concat_and_slice<0, N>(out, in); } - mutable univector<value_type, delay> data = scalar(value_type(0)); - mutable size_t cursor = 0; + state_holder<delay_state<T, delay, STag>, stateless> state; }; -template <typename E> -struct expression_delay<1, E> : expression_with_arguments<E> +template <typename E, bool stateless, univector_tag STag> +struct expression_delay<1, E, stateless, STag> : expression_with_arguments<E> { using value_type = value_type_of<E>; using T = value_type; using expression_with_arguments<E>::expression_with_arguments; + expression_delay(E&& e, const delay_state<T, 1, STag>& state) + : expression_with_arguments<E>(std::forward<E>(e)), state(state) + { + } + template <size_t N> friend KFR_INTRINSIC vec<T, N> get_elements(const expression_delay& self, cinput_t cinput, size_t index, vec_shape<T, N>) { const vec<T, N> in = self.argument_first(cinput, index, vec_shape<T, N>()); - const vec<T, N> out = insertleft(self.data, in); - self.data = in[N - 1]; + const vec<T, N> out = insertleft(self.state.s.data, in); + self.state.s.data = in[N - 1]; return out; } - mutable value_type data = value_type(0); + state_holder<delay_state<T, 1, STag>, stateless> state; }; } // namespace internal @@ -108,11 +143,30 @@ struct expression_delay<1, E> : expression_with_arguments<E> * auto d = delay(v, csize<4>); * @endcode */ -template <size_t samples = 1, typename E1> -KFR_INTRINSIC internal::expression_delay<samples, E1> delay(E1&& e1, csize_t<samples> = csize_t<samples>()) +template <size_t samples = 1, typename E1, typename T = value_type_of<E1>> +KFR_INTRINSIC internal::expression_delay<samples, E1, false, samples> delay(E1&& e1) { static_assert(samples >= 1 && samples < 1024, ""); - return internal::expression_delay<samples, E1>(std::forward<E1>(e1)); + return internal::expression_delay<samples, E1, false, samples>(std::forward<E1>(e1), + delay_state<T, samples>()); +} + +/** + * @brief Returns template expression that applies delay to the input (uses ring buffer in state) + * @param state delay filter state + * @param e1 an input expression + * @code + * univector<double, 10> v = counter(); + * delay_state<double, 4> state; + * auto d = delay(state, v); + * @endcode + */ +template <size_t samples, typename T, typename E1, univector_tag STag> +KFR_INTRINSIC internal::expression_delay<samples, E1, true, STag> delay(delay_state<T, samples, STag>& state, + E1&& e1) +{ + static_assert(STag == tag_dynamic_vector || (samples >= 1 && samples < 1024), ""); + return internal::expression_delay<samples, E1, true, STag>(std::forward<E1>(e1), state); } } // namespace CMT_ARCH_NAME } // namespace kfr diff --git a/include/kfr/dsp/fir.hpp b/include/kfr/dsp/fir.hpp @@ -31,6 +31,7 @@ #include "../base/reduce.hpp" #include "../base/univector.hpp" #include "../simd/vec.hpp" +#include "state_holder.hpp" namespace kfr { @@ -73,26 +74,6 @@ struct fir_state namespace internal { -template <typename T, bool stateless> -struct state_holder -{ - state_holder() = delete; - state_holder(const state_holder&) = default; - state_holder(state_holder&&) = default; - constexpr state_holder(const T& state) CMT_NOEXCEPT : s(state) {} - T s; -}; - -template <typename T> -struct state_holder<T, true> -{ - state_holder() = delete; - state_holder(const state_holder&) = default; - state_holder(state_holder&&) = default; - constexpr state_holder(const T& state) CMT_NOEXCEPT : s(state) {} - const T& s; -}; - template <size_t tapcount, typename T, typename U, typename E1, bool stateless = false> struct expression_short_fir : expression_with_arguments<E1> { diff --git a/include/kfr/dsp/state_holder.hpp b/include/kfr/dsp/state_holder.hpp @@ -0,0 +1,41 @@ +/** @addtogroup fir + * @{ + */ +/** + * KFR (http://kfrlib.com) + * Copyright (C) 2016 D Levin + * See LICENSE.txt for details + */ +#pragma once + +#include "../cident.h" + +namespace kfr +{ +inline namespace CMT_ARCH_NAME +{ +namespace internal +{ + +template <typename T, bool stateless> +struct state_holder +{ + state_holder() = delete; + state_holder(const state_holder&) = default; + state_holder(state_holder&&) = default; + constexpr state_holder(const T& state) CMT_NOEXCEPT : s(state) {} + T s; +}; + +template <typename T> +struct state_holder<T, true> +{ + state_holder() = delete; + state_holder(const state_holder&) = default; + state_holder(state_holder&&) = default; + constexpr state_holder(const T& state) CMT_NOEXCEPT : s(state) {} + const T& s; +}; +} // namespace internal +} // namespace CMT_ARCH_NAME +} // namespace kfr diff --git a/tests/dsp_test.cpp b/tests/dsp_test.cpp @@ -287,6 +287,12 @@ TEST(delay) CHECK_EXPRESSION(delay(v1), 33, [](size_t i) { return i < 1 ? 0.f : (i - 1) + 100.f; }); CHECK_EXPRESSION(delay<3>(v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); + + delay_state<float, 3> state1; + CHECK_EXPRESSION(delay(state1, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); + + delay_state<float, 3, tag_dynamic_vector> state2; + CHECK_EXPRESSION(delay(state2, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); } TEST(fracdelay) @@ -396,6 +402,15 @@ TEST(fir) return result; }); + fir_state state(taps.ref()); + + CHECK_EXPRESSION(fir(state, data), 100, [&](size_t index) -> T { + T result = 0; + for (size_t i = 0; i < taps.size(); i++) + result += data.get(index - i, 0) * taps[i]; + return result; + }); + CHECK_EXPRESSION(short_fir(data, taps), 100, [&](size_t index) -> T { T result = 0; for (size_t i = 0; i < taps.size(); i++)