1 #pragma once
2 
3 #include <string>
4 #include <cmath>
5 #include <memory>
6 #include <type_traits>
7 #include "FastMath.h"
8 #include "portable_intrinsics.h"
9 
10 namespace chowdsp
11 {
12 
13 /**
14  * Some SIMD versions of the WDF classes in wdf.h
15  */
16 namespace WDF_SSE
17 {
18 
19 namespace detail
20 {
21 template <typename T>
22 typename std::enable_if<std::is_default_constructible<T>::value, void>::type
default_construct(std::unique_ptr<T> & ptr)23 default_construct(std::unique_ptr<T> &ptr)
24 {
25     ptr = std::make_unique<T>();
26 }
27 
28 template <typename T>
29 typename std::enable_if<!std::is_default_constructible<T>::value, void>::type
default_construct(std::unique_ptr<T> & ptr)30 default_construct(std::unique_ptr<T> &ptr)
31 {
32     ptr.reset(nullptr);
33 }
34 } // namespace detail
35 
36 /** Wave digital filter base class */
37 class WDF
38 {
39   public:
WDF(std::string type)40     WDF(std::string type) : type(type) {}
~WDF()41     virtual ~WDF() {}
42 
43     /** Sub-classes override this function to recompute
44      * the impedance of this element.
45      */
46     virtual void calcImpedance() = 0;
47 
48     /** Sub-classes override this function to propogate
49      * an impedance change to the upstream elements in
50      * the WDF tree.
51      */
52     virtual void propagateImpedance() = 0;
53 
54     /** Sub-classes override this function to accept an incident wave. */
55     virtual void incident(__m128 x) noexcept = 0;
56 
57     /** Sub-classes override this function to propogate a reflected wave. */
58     virtual __m128 reflected() noexcept = 0;
59 
60     /** Probe the voltage across this circuit element. */
voltage()61     inline __m128 voltage() const noexcept { return vMul(vAdd(a, b), vLoad1(0.5f)); }
62 
63     /**Probe the current through this circuit element. */
current()64     inline __m128 current() const noexcept { return vMul(vSub(a, b), vMul(vLoad1(0.5f), G)); }
65 
66     // These classes need access to a,b
67     friend class YParameter;
68     friend class WDFParallel;
69     friend class WDFSeries;
70 
71     template <typename Port1Type, typename Port2Type> friend class WDFParallelT;
72 
73     template <typename Port1Type, typename Port2Type> friend class WDFSeriesT;
74 
75     __m128 R; // impedance
76     __m128 G; // admittance
77 
78   protected:
79     __m128 a = vZero; // incident wave
80     __m128 b = vZero; // reflected wave
81 
82   private:
83     const std::string type;
84 };
85 
86 /** WDF node base class */
87 class WDFNode : public WDF
88 {
89   public:
WDFNode(std::string type)90     WDFNode(std::string type) : WDF(type) {}
~WDFNode()91     virtual ~WDFNode() {}
92 
93     /** Connects this WDF node to an upstream node in the WDF tree. */
connectToNode(WDF * node)94     void connectToNode(WDF *node) { next = node; }
95 
96     /** When this function is called from a downstream
97      * element in the WDF tree, the impedance is recomputed
98      * and then propogated upstream to the next element in the
99      * WDF tree.
100      */
propagateImpedance()101     inline void propagateImpedance() override
102     {
103         calcImpedance();
104 
105         if (next != nullptr)
106             next->propagateImpedance();
107     }
108 
109   protected:
110     WDF *next = nullptr;
111 };
112 
113 /** WDF Resistor Node */
114 class Resistor : public WDFNode
115 {
116   public:
117     /** Creates a new WDF Resistor with a given resistance.
118      * @param value: resistance in Ohms
119      */
Resistor(float value)120     Resistor(float value) : WDFNode("Resistor"), R_value(vLoad1(value)) { calcImpedance(); }
~Resistor()121     virtual ~Resistor() {}
122 
123     // @TODO
124     /** Sets the resistance value of the WDF resistor, in Ohms.
125     // void setResistanceValue(float newR)
126     // {
127     //     if (newR == R_value)
128     //         return;
129 
130     //     R_value = newR;
131     //     propagateImpedance();
132     // }*/
133 
134     /** Computes the impedance of the WDF resistor, Z_R = R. */
calcImpedance()135     inline void calcImpedance() override
136     {
137         R = R_value;
138         G = _mm_div_ps(vLoad1(1.0f), R);
139     }
140 
141     /** Accepts an incident wave into a WDF resistor. */
incident(__m128 x)142     inline void incident(__m128 x) noexcept override { a = x; }
143 
144     /** Propogates a reflected wave from a WDF resistor. */
reflected()145     inline __m128 reflected() noexcept override
146     {
147         b = vLoad1(0.0f);
148         return b;
149     }
150 
151   private:
152     __m128 R_value = vLoad1(1.0e-9f);
153 };
154 
155 /** WDF Capacitor Node */
156 class Capacitor : public WDFNode
157 {
158   public:
159     /** Creates a new WDF Capacitor.
160      * @param value: Capacitance value in Farads
161      * @param fs: WDF sample rate
162      * @param alpha: alpha value to be used for the alpha transform,
163      *               use 0 for Backwards Euler, use 1 for Bilinear Transform.
164      */
165     Capacitor(float value, float fs, float alpha = 1.0f)
166         : WDFNode("Capacitor"), C_value(vLoad1(value)), fs(vLoad1(fs)), alpha(vLoad1(alpha)),
167           b_coef(vLoad1((1.0f - alpha) / 2.0f)), a_coef(vLoad1((1.0f + alpha) / 2.0f))
168     {
169         calcImpedance();
170     }
~Capacitor()171     virtual ~Capacitor() {}
172 
173     // @TODO
174     /** Sets the capacitance value of the WDF capacitor, in Farads.
175     void setCapacitanceValue(double newC)
176     {
177         if (newC == C_value)
178             return;
179 
180         C_value = newC;
181         propagateImpedance();
182     } */
183 
184     /** Computes the impedance of the WDF capacitor,
185      *                 1
186      * Z_C = ---------------------
187      *       (1 + alpha) * f_s * C
188      */
calcImpedance()189     inline void calcImpedance() override
190     {
191         R = _mm_div_ps(vLoad1(1.0f), vMul(vMul(vAdd(vLoad1(1.0f), alpha), C_value), fs));
192         G = _mm_div_ps(vLoad1(1.0f), R);
193     }
194 
195     /** Accepts an incident wave into a WDF capacitor. */
incident(__m128 x)196     inline void incident(__m128 x) noexcept override
197     {
198         a = x;
199         z = a;
200     }
201 
202     /** Propogates a reflected wave from a WDF capacitor. */
reflected()203     inline __m128 reflected() noexcept override
204     {
205         b = vAdd(vMul(b_coef, b), vMul(a_coef, z));
206         return b;
207     }
208 
209   private:
210     __m128 C_value;
211     __m128 z = vZero;
212 
213     const __m128 fs;
214     const __m128 alpha;
215 
216     const __m128 b_coef;
217     const __m128 a_coef;
218 };
219 
220 /** WDF Voltage Polarity Inverter */
221 template <typename PortType> class PolarityInverterT : public WDFNode
222 {
223   public:
224     /** Creates a new WDF polarity inverter */
PolarityInverterT()225     PolarityInverterT() : WDFNode("Polarity Inverter") { detail::default_construct(port1); }
~PolarityInverterT()226     virtual ~PolarityInverterT() {}
227 
initialise()228     void initialise()
229     {
230         port1->connectToNode(this);
231         calcImpedance();
232     }
233 
234     /** Calculates the impedance of the WDF inverter
235      * (same impedance as the connected node).
236      */
calcImpedance()237     inline void calcImpedance() override
238     {
239         R = port1->R;
240         G = _mm_div_ps(vLoad1(1.0f), R);
241     }
242 
243     /** Accepts an incident wave into a WDF inverter. */
incident(__m128 x)244     inline void incident(__m128 x) noexcept override
245     {
246         a = x;
247         port1->incident(vNeg(x));
248     }
249 
250     /** Propogates a reflected wave from a WDF inverter. */
reflected()251     inline __m128 reflected() noexcept override
252     {
253         b = vNeg(port1->reflected());
254         return b;
255     }
256 
257     std::unique_ptr<PortType> port1;
258 };
259 
260 /** WDF 3-port parallel adaptor */
261 template <typename Port1Type, typename Port2Type> class WDFParallelT : public WDFNode
262 {
263   public:
264     /** Creates a new WDF parallel adaptor from two connected ports. */
WDFParallelT()265     WDFParallelT() : WDFNode("Parallel")
266     {
267         detail::default_construct(port1);
268         detail::default_construct(port2);
269     }
~WDFParallelT()270     virtual ~WDFParallelT() {}
271 
initialise()272     void initialise()
273     {
274         port1->connectToNode(this);
275         port2->connectToNode(this);
276         calcImpedance();
277     }
278 
279     /** Computes the impedance for a WDF parallel adaptor.
280      *  1     1     1
281      * --- = --- + ---
282      * Z_p   Z_1   Z_2
283      */
calcImpedance()284     inline void calcImpedance() override
285     {
286         G = vAdd(port1->G, port2->G);
287         R = _mm_div_ps(vLoad1(1.0f), G);
288         port1Reflect = _mm_div_ps(port1->G, G);
289         port2Reflect = _mm_div_ps(port2->G, G);
290     }
291 
292     /** Accepts an incident wave into a WDF parallel adaptor. */
incident(__m128 x)293     inline void incident(__m128 x) noexcept override
294     {
295         port1->incident(vAdd(x, vMul(vSub(port2->b, port1->b), port2Reflect)));
296         port2->incident(vAdd(x, vMul(vSub(port2->b, port1->b), vNeg(port1Reflect))));
297         a = x;
298     }
299 
300     /** Propogates a reflected wave from a WDF parallel adaptor. */
reflected()301     inline __m128 reflected() noexcept override
302     {
303         b = vAdd(vMul(port1Reflect, port1->reflected()), vMul(port2Reflect, port2->reflected()));
304         return b;
305     }
306 
307     std::unique_ptr<Port1Type> port1;
308     std::unique_ptr<Port2Type> port2;
309 
310   private:
311     __m128 port1Reflect;
312     __m128 port2Reflect;
313 };
314 
315 /** WDF 3-port series adaptor */
316 template <typename Port1Type, typename Port2Type> class WDFSeriesT : public WDFNode
317 {
318   public:
319     /** Creates a new WDF series adaptor from two connected ports. */
WDFSeriesT()320     WDFSeriesT() : WDFNode("Series")
321     {
322         detail::default_construct(port1);
323         detail::default_construct(port2);
324     }
~WDFSeriesT()325     virtual ~WDFSeriesT() {}
326 
initialise()327     void initialise()
328     {
329         port1->connectToNode(this);
330         port2->connectToNode(this);
331         calcImpedance();
332     }
333 
334     /** Computes the impedance for a WDF parallel adaptor.
335      * Z_s = Z_1 + Z_2
336      */
calcImpedance()337     inline void calcImpedance() override
338     {
339         R = vAdd(port1->R, port2->R);
340         G = _mm_div_ps(vLoad1(1.0f), R);
341         port1Reflect = _mm_div_ps(port1->R, R);
342         port2Reflect = _mm_div_ps(port2->R, R);
343     }
344 
345     /** Accepts an incident wave into a WDF series adaptor. */
incident(__m128 x)346     inline void incident(__m128 x) noexcept override
347     {
348         port1->incident(vSub(port1->b, vMul(port1Reflect, vAdd(x, vAdd(port1->b, port2->b)))));
349         port2->incident(vSub(port2->b, vMul(port2Reflect, vAdd(x, vAdd(port1->b, port2->b)))));
350 
351         a = x;
352     }
353 
354     /** Propogates a reflected wave from a WDF series adaptor. */
reflected()355     inline __m128 reflected() noexcept override
356     {
357         b = vNeg(vAdd(port1->reflected(), port2->reflected()));
358         return b;
359     }
360 
361     std::unique_ptr<Port1Type> port1;
362     std::unique_ptr<Port2Type> port2;
363 
364   private:
365     __m128 port1Reflect;
366     __m128 port2Reflect;
367 };
368 
369 /** WDF Voltage source with series resistance */
370 class ResistiveVoltageSource : public WDFNode
371 {
372   public:
373     /** Creates a new resistive voltage source.
374      * @param value: initial resistance value, in Ohms
375      */
376     ResistiveVoltageSource(float value = 1.0e-9f)
377         : WDFNode("Resistive Voltage"), R_value(vLoad1(value))
378     {
379         calcImpedance();
380     }
~ResistiveVoltageSource()381     virtual ~ResistiveVoltageSource() {}
382 
383     // @TODO
384     /** Sets the resistance value of the series resistor, in Ohms.
385     void setResistanceValue(double newR)
386     {
387         if (newR == R_value)
388             return;
389 
390         R_value = newR;
391         propagateImpedance();
392     } */
393 
394     /** Computes the impedance for a WDF resistive voltage souce
395      * Z_Vr = Z_R
396      */
calcImpedance()397     inline void calcImpedance() override
398     {
399         R = R_value;
400         G = _mm_div_ps(vLoad1(1.0f), R);
401     }
402 
403     /** Sets the voltage of the voltage source, in Volts */
setVoltage(__m128 newV)404     void setVoltage(__m128 newV) { Vs = newV; }
405 
406     /** Accepts an incident wave into a WDF resistive voltage source. */
incident(__m128 x)407     inline void incident(__m128 x) noexcept override { a = x; }
408 
409     /** Propogates a reflected wave from a WDF resistive voltage source. */
reflected()410     inline __m128 reflected() noexcept override
411     {
412         b = Vs;
413         return b;
414     }
415 
416   private:
417     __m128 Vs;
418     __m128 R_value;
419 };
420 
421 /** WDF Current source with parallel resistance */
422 class ResistiveCurrentSource : public WDFNode
423 {
424   public:
425     /** Creates a new resistive current source.
426      * @param value: initial resistance value, in Ohms
427      */
428     ResistiveCurrentSource(float value = 1.0e9f)
429         : WDFNode("Resistive Current"), R_value(vLoad1(value))
430     {
431         calcImpedance();
432     }
~ResistiveCurrentSource()433     virtual ~ResistiveCurrentSource() {}
434 
435     // @TODO
436     /** Sets the resistance value of the parallel resistor, in Ohms.
437     void setResistanceValue(double newR)
438     {
439         if (newR == R_value)
440             return;
441 
442         R_value = newR;
443         propagateImpedance();
444     } */
445 
446     /** Computes the impedance for a WDF resistive current souce
447      * Z_Ir = Z_R
448      */
calcImpedance()449     inline void calcImpedance() override
450     {
451         R = R_value;
452         G = _mm_div_ps(vLoad1(1.0f), R);
453     }
454 
455     /** Sets the current of the current source, in Amps */
setCurrent(__m128 newI)456     void setCurrent(__m128 newI) { Is = newI; }
457 
458     /** Accepts an incident wave into a WDF resistive current source. */
incident(__m128 x)459     inline void incident(__m128 x) noexcept override { a = x; }
460 
461     /** Propogates a reflected wave from a WDF resistive current source. */
reflected()462     inline __m128 reflected() noexcept override
463     {
464         b = vMul(vLoad1(2.0f), vMul(R, Is));
465         return b;
466     }
467 
468   private:
469     __m128 Is;
470     __m128 R_value;
471 };
472 
473 } // namespace WDF_SSE
474 
475 } // namespace chowdsp
476