1 /** 2 * Copyright 2017 Cut Through Recordings 3 * License: MIT License 4 * Author(s): Ethan Reker 5 * 6 * Based on equations from https://github.com/vinniefalco/DSPFilters 7 * -------------------------------------------------------------------------------- 8 * License: MIT License (http://www.opensource.org/licenses/mit-license.php) 9 * Copyright (c) 2009 by Vinnie Falco 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 * of this software and associated documentation files (the "Software"), to deal 12 * in the Software without restriction, including without limitation the rights 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 * copies of the Software, and to permit persons to whom the Software is 15 * furnished to do so, subject to the following conditions: 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 */ 26 module ddsp.filter.allpass; 27 28 import std.math; 29 30 import ddsp.effect.effect; 31 32 const float pi = 3.14159265; 33 34 35 /// Allpass filter for introducting a 180 degrees phase shift at the center 36 /// frequency. This is necessary for summing more than 2 bands created from 37 /// Linkwitz-Riley filters. 38 /// TODO: Inherit BiQuad directly to remove redundent code. 39 class Allpass : AudioEffect 40 { 41 public: 42 43 this() nothrow @nogc 44 { 45 46 } 47 48 void initialize(float frequency, float q = 0.707) nothrow @nogc 49 { 50 _frequency = frequency; 51 float _w0 = 2 * pi * _frequency / _sampleRate; 52 float cs = cos(_w0); 53 float sn = sin(_w0); 54 float AL = sn / (2 * q); 55 56 _a0 = 1 + AL; 57 _a1 = (-2 * cs) / _a0; 58 _a2 = (1 - AL) / _a0; 59 _b0 = (1 - AL) / _a0; 60 _b1 = (-2 * cs) / _a0; 61 _b2 = (1 + AL) / _a0; 62 } 63 64 override float getNextSample(const float input) nothrow @nogc 65 { 66 _w = input - _a1 * _w1 - _a2 * _w2; 67 _yn = _b0 * _w + _b1 *_w1 + _b2 * _w2; 68 69 _w2 = _w1; 70 _w1 = _w; 71 72 return _yn; 73 } 74 75 override void reset() nothrow @nogc 76 { 77 _a0 = 0; 78 _a1 = 0; 79 _a2 = 0; 80 _b0 = 0; 81 _b1 = 0; 82 _b2 = 0; 83 84 _w = 0; 85 _w1 = 0; 86 _w2 = 0; 87 88 _yn = 0; 89 } 90 91 void setFrequency(float frequency) nothrow @nogc 92 { 93 if(_frequency != frequency) 94 initialize(frequency, _sampleRate); 95 } 96 97 private: 98 float _a0 = 0; 99 float _a1 = 0; 100 float _a2 = 0; 101 float _b0 = 0; 102 float _b1 = 0; 103 float _b2 = 0; 104 105 float _w = 0; 106 float _w1 = 0; 107 float _w2 = 0; 108 109 float _yn; 110 111 float _frequency; 112 } 113 114 unittest 115 { 116 import dplug.core.nogc; 117 118 Allpass f = mallocNew!Allpass(); 119 f.setSampleRate(44100); 120 f.setFrequency(10000); 121 testEffect(f, "Allpass", 44100 * 2, false); 122 }