1 /** @addtogroup fir
2 * @{
3 */
4 /*
5 Copyright (C) 2016 D Levin (https://www.kfrlib.com)
6 This file is part of KFR
7
8 KFR is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 KFR is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with KFR.
20
21 If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
22 Buying a commercial license is mandatory as soon as you develop commercial activities without
23 disclosing the source code of your own applications.
24 See https://www.kfrlib.com for details.
25 */
26 #pragma once
27
28 #include "../base/basic_expressions.hpp"
29 #include "../base/filter.hpp"
30 #include "../base/memory.hpp"
31 #include "../base/reduce.hpp"
32 #include "../base/univector.hpp"
33 #include "../simd/vec.hpp"
34 #include "state_holder.hpp"
35
36 namespace kfr
37 {
38 inline namespace CMT_ARCH_NAME
39 {
40
41 template <typename T, size_t Size>
42 using fir_taps = univector<T, Size>;
43
44 template <size_t tapcount, typename T, typename U = T>
45 struct short_fir_state
46 {
47 template <size_t N>
short_fir_statekfr::CMT_ARCH_NAME::short_fir_state48 short_fir_state(const univector<T, N>& taps)
49 : taps(widen<tapcount>(read<N>(taps.data()), T(0))), delayline(0)
50 {
51 }
52 template <size_t N>
short_fir_statekfr::CMT_ARCH_NAME::short_fir_state53 short_fir_state(const univector<const T, N>& taps)
54 : taps(widen<tapcount>(read<N>(taps.data()), T(0))), delayline(0)
55 {
56 }
57 vec<T, tapcount> taps;
58 mutable vec<U, tapcount - 1> delayline;
59 };
60
61 template <typename T, typename U = T>
62 struct fir_state
63 {
fir_statekfr::CMT_ARCH_NAME::fir_state64 fir_state(const array_ref<const T>& taps)
65 : taps(taps.size()), delayline(taps.size(), U(0)), delayline_cursor(0)
66 {
67 this->taps = reverse(make_univector(taps.data(), taps.size()));
68 }
69 univector<T> taps;
70 mutable univector<U> delayline;
71 mutable size_t delayline_cursor;
72 };
73
74 template <typename U, univector_tag Tag = tag_dynamic_vector>
75 struct moving_sum_state
76 {
moving_sum_statekfr::CMT_ARCH_NAME::moving_sum_state77 moving_sum_state() : delayline({ 0 }), head_cursor(0), tail_cursor(1) {}
78 mutable univector<U, Tag> delayline;
79 mutable size_t head_cursor, tail_cursor;
80 };
81 template <typename U>
82 struct moving_sum_state<U, tag_dynamic_vector>
83 {
moving_sum_statekfr::CMT_ARCH_NAME::moving_sum_state84 moving_sum_state(size_t sum_length) : delayline(sum_length, U(0)), head_cursor(0), tail_cursor(1) {}
85 mutable univector<U> delayline;
86 mutable size_t head_cursor, tail_cursor;
87 };
88
89 namespace internal
90 {
91
92 template <size_t tapcount, typename T, typename U, typename E1, bool stateless = false>
93 struct expression_short_fir : expression_with_arguments<E1>
94 {
95 using value_type = U;
96
expression_short_firkfr::CMT_ARCH_NAME::internal::expression_short_fir97 expression_short_fir(E1&& e1, const short_fir_state<tapcount, T, U>& state)
98 : expression_with_arguments<E1>(std::forward<E1>(e1)), state(state)
99 {
100 }
101
102 template <size_t N>
get_elements(const expression_short_fir & self,cinput_t cinput,size_t index,vec_shape<U,N> x)103 KFR_INTRINSIC friend vec<U, N> get_elements(const expression_short_fir& self, cinput_t cinput,
104 size_t index, vec_shape<U, N> x)
105 {
106 vec<U, N> in = self.argument_first(cinput, index, x);
107
108 vec<U, N> out = in * self.state.s.taps.front();
109 cforeach(csizeseq<tapcount - 1, 1>, [&](auto I) {
110 out = out +
111 concat_and_slice<tapcount - 1 - I, N>(self.state.s.delayline, in) * self.state.s.taps[I];
112 });
113 self.state.s.delayline = concat_and_slice<N, tapcount - 1>(self.state.s.delayline, in);
114
115 return out;
116 }
117 state_holder<short_fir_state<tapcount, T, U>, stateless> state;
118 };
119
120 template <typename T, typename U, typename E1, bool stateless = false>
121 struct expression_fir : expression_with_arguments<E1>
122 {
123 using value_type = U;
124
expression_firkfr::CMT_ARCH_NAME::internal::expression_fir125 expression_fir(E1&& e1, const fir_state<T, U>& state)
126 : expression_with_arguments<E1>(std::forward<E1>(e1)), state(state)
127 {
128 }
129
130 template <size_t N>
get_elements(const expression_fir & self,cinput_t cinput,size_t index,vec_shape<U,N> x)131 KFR_INTRINSIC friend vec<U, N> get_elements(const expression_fir& self, cinput_t cinput, size_t index,
132 vec_shape<U, N> x)
133 {
134 const size_t tapcount = self.state.s.taps.size();
135 const vec<U, N> input = self.argument_first(cinput, index, x);
136
137 vec<U, N> output;
138 size_t cursor = self.state.s.delayline_cursor;
139 CMT_LOOP_NOUNROLL
140 for (size_t i = 0; i < N; i++)
141 {
142 self.state.s.delayline.ringbuf_write(cursor, input[i]);
143 output[i] =
144 dotproduct(self.state.s.taps, self.state.s.delayline.slice(cursor) /*, tapcount - cursor*/) +
145 dotproduct(self.state.s.taps.slice(tapcount - cursor), self.state.s.delayline /*, cursor*/);
146 }
147 self.state.s.delayline_cursor = cursor;
148 return output;
149 }
150 state_holder<fir_state<T, U>, stateless> state;
151 };
152
153 template <typename U, typename E1, univector_tag STag, bool stateless = false>
154 struct expression_moving_sum : expression_with_arguments<E1>
155 {
156 using value_type = U;
157
expression_moving_sumkfr::CMT_ARCH_NAME::internal::expression_moving_sum158 expression_moving_sum(E1&& e1, const moving_sum_state<U, STag>& state)
159 : expression_with_arguments<E1>(std::forward<E1>(e1)), state(state)
160 {
161 }
162
163 template <size_t N>
get_elements(const expression_moving_sum & self,cinput_t cinput,size_t index,vec_shape<U,N> x)164 KFR_INTRINSIC friend vec<U, N> get_elements(const expression_moving_sum& self, cinput_t cinput,
165 size_t index, vec_shape<U, N> x)
166 {
167 static_assert(N >= 1, "");
168
169 const vec<U, N> input = self.argument_first(cinput, index, x);
170
171 vec<U, N> output;
172 size_t wcursor = self.state.s.head_cursor;
173 size_t rcursor = self.state.s.tail_cursor;
174
175 // initial summation
176 self.state.s.delayline.ringbuf_write(wcursor, input[0]);
177 auto s = sum(self.state.s.delayline);
178 output[0] = s;
179
180 CMT_LOOP_NOUNROLL
181 for (size_t i = 1; i < N; i++)
182 {
183 U nextout;
184 self.state.s.delayline.ringbuf_read(rcursor, nextout);
185 U const nextin = input[i];
186 self.state.s.delayline.ringbuf_write(wcursor, nextin);
187 s += nextin - nextout;
188 output[i] = s;
189 }
190 self.state.s.delayline.ringbuf_step(rcursor, 1);
191 self.state.s.head_cursor = wcursor;
192 self.state.s.tail_cursor = rcursor;
193 return output;
194 }
195 state_holder<moving_sum_state<U, STag>, stateless> state;
196 };
197 } // namespace internal
198
199 /**
200 * @brief Returns template expression that applies FIR filter to the input
201 * @param e1 an input expression
202 * @param taps coefficients for the FIR filter
203 */
204 template <typename T, typename E1, univector_tag Tag>
fir(E1 && e1,const univector<T,Tag> & taps)205 KFR_INTRINSIC internal::expression_fir<T, value_type_of<E1>, E1> fir(E1&& e1, const univector<T, Tag>& taps)
206 {
207 return internal::expression_fir<T, value_type_of<E1>, E1>(std::forward<E1>(e1), taps.ref());
208 }
209
210 /**
211 * @brief Returns template expression that applies FIR filter to the input
212 * @param state FIR filter state
213 * @param e1 an input expression
214 */
215 template <typename T, typename U, typename E1>
fir(fir_state<T,U> & state,E1 && e1)216 KFR_INTRINSIC internal::expression_fir<T, U, E1, true> fir(fir_state<T, U>& state, E1&& e1)
217 {
218 return internal::expression_fir<T, U, E1, true>(std::forward<E1>(e1), state);
219 }
220
221 /**
222 * @brief Returns template expression that performs moving sum on the input
223 * @param state moving sum state
224 * @param e1 an input expression
225 */
226 template <size_t sum_length, typename E1>
moving_sum(E1 && e1)227 KFR_INTRINSIC internal::expression_moving_sum<value_type_of<E1>, E1, tag_dynamic_vector> moving_sum(E1&& e1)
228 {
229 return internal::expression_moving_sum<value_type_of<E1>, E1, tag_dynamic_vector>(std::forward<E1>(e1),
230 sum_length);
231 }
232
233 /**
234 * @brief Returns template expression that performs moving sum on the input
235 * @param state moving sum state
236 * @param e1 an input expression
237 */
238 template <typename U, typename E1, univector_tag STag>
moving_sum(moving_sum_state<U,STag> & state,E1 && e1)239 KFR_INTRINSIC internal::expression_moving_sum<U, E1, STag, true> moving_sum(moving_sum_state<U, STag>& state,
240 E1&& e1)
241 {
242 return internal::expression_moving_sum<U, E1, STag, true>(std::forward<E1>(e1), state);
243 }
244
245 /**
246 * @brief Returns template expression that applies FIR filter to the input (count of coefficients must be in
247 * range 2..32)
248 * @param e1 an input expression
249 * @param taps coefficients for the FIR filter
250 */
251 template <typename T, size_t TapCount, typename E1>
252 KFR_INTRINSIC internal::expression_short_fir<next_poweroftwo(TapCount - 1) + 1, T, value_type_of<E1>, E1>
short_fir(E1 && e1,const univector<T,TapCount> & taps)253 short_fir(E1&& e1, const univector<T, TapCount>& taps)
254 {
255 static_assert(TapCount >= 2 && TapCount <= 33, "Use short_fir only for small FIR filters");
256 return internal::expression_short_fir<next_poweroftwo(TapCount - 1) + 1, T, value_type_of<E1>, E1>(
257 std::forward<E1>(e1), taps);
258 }
259
260 /**
261 * @brief Returns template expression that applies FIR filter to the input (count of coefficients must be in
262 * range 2..32)
263 * @param state FIR filter state
264 * @param e1 an input expression
265 */
266 template <size_t TapCount, typename T, typename U, typename E1>
267 KFR_INTRINSIC internal::expression_short_fir<next_poweroftwo(TapCount - 1) + 1, T, value_type_of<E1>, E1,
268 true>
short_fir(short_fir_state<next_poweroftwo (TapCount-1)+1,T,U> & state,E1 && e1)269 short_fir(short_fir_state<next_poweroftwo(TapCount - 1) + 1, T, U>& state, E1&& e1)
270 {
271 static_assert(TapCount >= 2 && TapCount <= 33, "Use short_fir only for small FIR filters");
272 return internal::expression_short_fir<next_poweroftwo(TapCount - 1) + 1, T, value_type_of<E1>, E1, true>(
273 std::forward<E1>(e1), state);
274 }
275
276 template <typename T, typename U = T>
277 class fir_filter : public filter<U>
278 {
279 public:
fir_filter(const univector_ref<const T> & taps)280 fir_filter(const univector_ref<const T>& taps) : state(taps) {}
281
set_taps(const univector_ref<const T> & taps)282 void set_taps(const univector_ref<const T>& taps) { state = fir_state<T, U>(taps); }
283
284 /// Reset internal filter state
reset()285 void reset() final
286 {
287 state.delayline = scalar(0);
288 state.delayline_cursor = 0;
289 }
290
291 protected:
process_buffer(U * dest,const U * src,size_t size)292 void process_buffer(U* dest, const U* src, size_t size) final
293 {
294 make_univector(dest, size) = fir(state, make_univector(src, size));
295 }
process_expression(U * dest,const expression_pointer<U> & src,size_t size)296 void process_expression(U* dest, const expression_pointer<U>& src, size_t size) final
297 {
298 make_univector(dest, size) = fir(state, src);
299 }
300
301 private:
302 fir_state<T, U> state;
303 };
304
305 template <typename T, typename U = T>
306 using filter_fir = fir_filter<T, U>;
307
308 } // namespace CMT_ARCH_NAME
309
CMT_MULTI_PROTO(template<typename U,typename T> filter<U> * make_fir_filter (const univector_ref<const T> & taps);)310 CMT_MULTI_PROTO(template <typename U, typename T>
311 filter<U>* make_fir_filter(const univector_ref<const T>& taps);)
312
313 #ifdef CMT_MULTI
314 template <typename U, typename T>
315 KFR_FUNCTION filter<U>* make_fir_filter(cpu_t cpu, const univector_ref<const T>& taps)
316 {
317 CMT_MULTI_PROTO_GATE(make_fir_filter<U>(taps))
318 }
319 #endif
320 } // namespace kfr
321