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