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 }