1 /**
2 * Copyright 2017 Cut Through Recordings
3 * License: MIT License
4 * Author(s): Ethan Reker
5 */
6 module ddsp.effect.effect;
7
8 import dplug.core.alignedbuffer;
9 import dplug.core.nogc;
10
11 /**
12 * Should be inherited by all Audio Effect classes to allow for batch processing
13 */
14 abstract class AudioEffect
15 {
16 public:
17
18 /**
19 * Process a sample that is passed to the processor, and return the next sample.
20 */
21 abstract float getNextSample(const float input) nothrow @nogc;
22
23
24
25 /**
26 * Should be used to free any delay elements or do any setup before play begins.
27 */
28 abstract void reset() nothrow @nogc;
29
30 /**
31 *
32 */
33 void setSampleRate(float sampleRate) nothrow @nogc
34 {
35 _sampleRate = sampleRate;
36 reset();
37 }
38
39 float SampleRate() @property { return _sampleRate; }
40
41 protected:
42 float _sampleRate;
43 }
44
45 /// Holds a list of AudioEffects. getNextSample(input)
46 /// call getNextSample(input) on each effect in the chain
47 class FXChain : AudioEffect
48 {
49 public:
50
51 this()
52 {
53 _fxChain = makeVec!AudioEffect();
54 }
55
56 /// Adds an effect to the end of the FX Chain
57 void addEffect(AudioEffect effect)
58 {
59 _fxChain.pushBack(effect);
60 }
61
62 override void setSampleRate(float sampleRate)
63 {
64 foreach(effect; _fxChain)
65 {
66 effect.setSampleRate(sampleRate);
67 }
68 }
69
70 /// Override from AudioEffect. Processes the input through each effect
71 /// and passes the result to the next effect.
72 override float getNextSample(const float input) nothrow @nogc
73 {
74 float output = input;
75 foreach(AudioEffect e; _fxChain)
76 {
77 output = e.getNextSample(output);
78 }
79 return output;
80 }
81
82 /// Resets each effect in the chain. To clear buffers
83 /// and recalculate coefficients.
84 override void reset() nothrow @nogc
85 {
86 foreach(AudioEffect e; _fxChain){
87 e.reset();
88 }
89 }
90
91 private:
92
93 /// Vector of audio effects.
94 Vec!AudioEffect _fxChain;
95 }
96
97 /**
98 * This function should only be called in a unittest block.
99 * AudioEffect effect : Effect to be tested.
100 * string name : name that will be written to output.
101 * size_t bufferSize : number of samples to be processed.
102 * bool outputResults : determines if output should be printed.
103 */
104 void testEffect(AudioEffect effect, string name, size_t bufferSize = 20000, bool outputResults = false)
105 {
106 import std.stdio;
107 import std.random;
108
109 Random gen;
110
111 if(outputResults)
112 {
113 writefln("Testing %s..", name);
114 writefln("Initial State: %s", effect.toString());
115 }
116
117 float[] outputs;
118 string[] stringResults;
119
120 for(int i = 0; i < bufferSize; ++i){
121 float sample = uniform(0.0L, 1.0L, gen);
122 float val = effect.getNextSample(sample);
123 if(i%1000 == 0){
124 outputs ~= val;
125 stringResults ~= effect.toString();
126 }
127 }
128
129 if(outputResults)
130 {
131 for(int i = 0; i < outputs.length && i < stringResults.length; ++i)
132 {
133 writefln("Output: %s ||| String: %s ", outputs[i], stringResults[i]);
134 }
135 writefln("End %s test..", name);
136 }
137
138 }
139
140 unittest
141 {
142 import std.stdio;
143 import ddsp.effect.digitaldelay;
144
145 /*auto fxchain = mallocEmplace!FXChain();
146
147 auto d = mallocEmplace!DigitalDelay();
148 d.initialize(44100, 2000, 500, 0.5, 0.5);
149
150 auto d2 = mallocEmplace!DigitalDelay();
151 d2.initialize(44100, 2000, 100, 0.1, 0.9);
152
153 fxchain.addEffect(d);
154
155 testEffect(fxchain, "FX Chain");*/
156 }