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