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