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 }