1 /** 2 Filters. 3 4 Based on code from STK library 5 https://github.com/thestk/stk 6 https://ccrma.stanford.edu/software/stk/ 7 */ 8 module soundtab.audio.filters; 9 10 import std.math : log2, exp2, sin, cos, pow, PI; 11 12 immutable float TWO_PI = PI * 2; 13 14 15 //float linearToDb(float f) { 16 // 17 //} 18 /// decibels to linear 19 float dbToLinear(float f) { 20 return pow(10.0f, f / 20); 21 } 22 23 24 /// digital filter - base interface 25 interface Filter { 26 // set sample rate (frames per second) 27 void setSampleRate(int samplesPerSecond); 28 // feed single input sample and get filtered result 29 float tick(float input); 30 // clear state 31 void clear(); 32 } 33 34 abstract class FilterBase : Filter { 35 int _sampleRate = 44100; 36 void setSampleRate(int samplesPerSecond) { 37 _sampleRate = samplesPerSecond; 38 } 39 } 40 41 class OnePole : FilterBase { 42 this(float thePole = 0.9f) { 43 setPole(thePole); 44 } 45 void setPole(float thePole) { 46 // Normalize coefficients for peak unity gain. 47 if (thePole > 0.0f) 48 _b[0] = 1.0f - thePole; 49 else 50 _b[0] = 1.0f + thePole; 51 52 _a[1] = -thePole; 53 } 54 float tick(float input) { 55 _inputs[0] = _gain * input; 56 _lastFrame = _b[0] * _inputs[0] - _a[1] * _outputs[1]; 57 _outputs[1] = _lastFrame; 58 return _lastFrame; 59 } 60 // clear state 61 void clear() { 62 _inputs = [0,0]; 63 _outputs = [0,0]; 64 _lastFrame = 0; 65 } 66 float _gain = 1.0f; 67 float[2] _a = [0,0]; 68 float[2] _b = [0,0]; 69 float[2] _inputs = [0,0]; 70 float[2] _outputs = [0,0]; 71 float _lastFrame = 0; 72 } 73 74 class OneZero : FilterBase { 75 this(float theZero = -1.0f) { 76 setZero(theZero); 77 } 78 void setZero(float theZero) { 79 // Normalize coefficients for unity gain. 80 if ( theZero > 0.0 ) 81 _b[0] = 1 / (1 + theZero); 82 else 83 _b[0] = 1 / (1 - theZero); 84 85 _b[1] = -theZero * _b[0]; 86 } 87 float tick(float input) { 88 _inputs[0] = _gain * input; 89 _lastFrame = _b[1] * _inputs[1] + _b[0] * _inputs[0]; 90 _inputs[1] = _inputs[0]; 91 return _lastFrame; 92 } 93 // clear state 94 void clear() { 95 _inputs = [0,0]; 96 _outputs = [0,0]; 97 _lastFrame = 0; 98 } 99 float _gain = 1.0f; 100 float[2] _a = [0,0]; 101 float[2] _b = [0,0]; 102 float[2] _inputs = [0,0]; 103 float[2] _outputs = [0,0]; 104 float _lastFrame = 0; 105 } 106 107 class Formant : FilterBase { 108 this() { 109 } 110 void setParams(float frequency, float radius, float gain) { 111 _gain = gain; 112 setResonance(frequency, radius); 113 } 114 override void setSampleRate(int samplesPerSecond) { 115 super.setSampleRate(samplesPerSecond); 116 setResonance(_frequency, _radius); 117 } 118 void setResonance(float frequency, float radius) { 119 _radius = radius; 120 _frequency = frequency; 121 _a[2] = radius * radius; 122 _a[1] = -2.0f * radius * cos( TWO_PI * frequency / _sampleRate ); 123 // Use zeros at +- 1 and normalize the filter peak gain. 124 _b[0] = 0.5 - 0.5 * _a[2]; 125 _b[1] = 0.0; 126 _b[2] = -_b[0]; 127 } 128 float tick(float input) { 129 _inputs[0] = _gain * input; 130 _lastFrame = _b[0] * _inputs[0] + _b[1] * _inputs[1] + _b[2] * _inputs[2]; 131 _lastFrame -= _a[2] * _outputs[2] + _a[1] * _outputs[1]; 132 _inputs[2] = _inputs[1]; 133 _inputs[1] = _inputs[0]; 134 _outputs[2] = _outputs[1]; 135 _outputs[1] = _lastFrame; 136 return _lastFrame; 137 } 138 // clear state 139 void clear() { 140 _inputs = [0,0,0,0]; 141 _outputs = [0,0,0,0]; 142 _lastFrame = 0; 143 } 144 float _frequency = 400; 145 float _radius = 0; 146 float _gain = 1.0f; 147 float[4] _a = [0,0,0,0]; 148 float[4] _b = [0,0,0,0]; 149 float[4] _inputs = [0,0,0,0]; 150 float[4] _outputs = [0,0,0,0]; 151 float _lastFrame = 0; 152 } 153 154 class PhonemeFormantsFilter : Filter { 155 Formant[4] _formants; 156 float _formantFreqMult; 157 this(PhonemeType phoneme = PhonemeType.ahh, float formantFreqMult = 1) { 158 _formantFreqMult = formantFreqMult; 159 for(int i = 0; i < 4; i++) 160 _formants[i] = new Formant(); 161 setPhoneme(phoneme, formantFreqMult); 162 } 163 164 void setPhoneme(PhonemeType type, float formantFreqMult = 1) { 165 _formantFreqMult = formantFreqMult; 166 for (int i = 0; i < 4; i ++) { 167 _formants[i].setParams(_formantFreqMult * phonemes[type][i].frequency, phonemes[type][i].radius, dbToLinear(phonemes[type][i].gainDb)); 168 } 169 } 170 171 void setSampleRate(int samplesPerSecond) { 172 for(int i = 0; i < 4; i++) 173 _formants[i].setSampleRate(samplesPerSecond); 174 } 175 176 float tick(float input) { 177 float temp = input; 178 179 _lastFrame = _formants[0].tick(temp); 180 _lastFrame += _formants[1].tick(temp); 181 _lastFrame += _formants[2].tick(temp); 182 _lastFrame += _formants[3].tick(temp); 183 return _lastFrame; 184 } 185 // clear state 186 void clear() { 187 for(int i = 0; i < 4; i++) 188 _formants[i].clear(); 189 } 190 float _lastFrame = 0; 191 } 192 193 struct FormantParams { 194 float frequency; 195 float radius; 196 float gainDb; 197 } 198 199 alias PhonemeParams = FormantParams[4]; 200 201 enum PhonemeType { 202 eee, 203 ihh, 204 ehh, 205 aaa, 206 207 ahh, 208 aww, 209 ohh, 210 uhh, 211 212 uuu, 213 ooo, 214 rrr, 215 lll, 216 } 217 218 PhonemeParams[12] phonemes = [ 219 [ 220 //// PhonemeType.eee (beet) 221 FormantParams(273, 0.996, 10), 222 FormantParams(2086, 0.945, -16), 223 FormantParams(2754, 0.979, -12), 224 FormantParams(3270, 0.440, -17) 225 ], 226 [ 227 //// PhonemeType.ihh (bit) 228 FormantParams(385, 0.987, 10), 229 FormantParams(2056, 0.930, -20), 230 FormantParams(2587, 0.890, -20), 231 FormantParams(3150, 0.400, -20) 232 ], 233 [ 234 //// PhonemeType.ehh (bet) 235 FormantParams(515, 0.977, 10), 236 FormantParams(1805, 0.810, -10), 237 FormantParams(2526, 0.875, -10), 238 FormantParams(3103, 0.400, -13) 239 ], 240 [ 241 //// PhonemeType.aaa (bat) 242 FormantParams(773, 0.950, 10), 243 FormantParams(1676, 0.830, -6), 244 FormantParams(2380, 0.880, -20), 245 FormantParams(3027, 0.600, -20) 246 ], 247 [ 248 //// PhonemeType.ahh (father) 249 FormantParams(770, 0.950, 0), 250 FormantParams(1153, 0.970, -9), 251 FormantParams(2450, 0.780, -29), 252 FormantParams(3140, 0.800, -39) 253 ], 254 [ 255 //// PhonemeType.aww (bought) 256 FormantParams(637, 0.910, 0), 257 FormantParams(895, 0.900, -3), 258 FormantParams(2556, 0.950, -17), 259 FormantParams(3070, 0.910, -20) 260 ], 261 [ 262 //// PhonemeType.ohh (bone) NOTE:: same as aww (bought) 263 FormantParams(637, 0.910, 0), 264 FormantParams(895, 0.900, -3), 265 FormantParams(2556, 0.950, -17), 266 FormantParams(3070, 0.910, -20) 267 ], 268 [ 269 //// PhonemeType.uhh (but) 270 FormantParams(561, 0.965, 0), 271 FormantParams(1084, 0.930, -10), 272 FormantParams(2541, 0.930, -15), 273 FormantParams(3345, 0.900, -20) 274 ], 275 [ 276 //// PhonemeType.uuu (foot) 277 FormantParams(515, 0.976, 0), 278 FormantParams(1031, 0.950, -3), 279 FormantParams(2572, 0.960, -11), 280 FormantParams(3345, 0.960, -20) 281 ], 282 [ 283 //// PhonemeType.ooo (boot) 284 FormantParams(349, 0.986, -10), 285 FormantParams(918, 0.940, -20), 286 FormantParams(2350, 0.960, -27), 287 FormantParams(2731, 0.950, -33) 288 ], 289 [ 290 //// PhonemeType.rrr (bird) 291 FormantParams(394, 0.959, -10), 292 FormantParams(1297, 0.780, -16), 293 FormantParams(1441, 0.980, -16), 294 FormantParams(2754, 0.950, -40) 295 ], 296 [ 297 //// PhonemeType.lll (lull) 298 FormantParams(462, 0.990, +5), 299 FormantParams(1200, 0.640, -10), 300 FormantParams(2500, 0.200, -20), 301 FormantParams(3000, 0.100, -30) 302 ], 303 ];