annotate src/Modules/BMM/ModuleGammatone.cc @ 16:2a5354042241

-Updated the Slaney IIR gammatone to use a cascase of four second-order filters as per the implementtion in Slaney's auditory toolbox. This is more numerically stable at high sample rates and low centre frequencies.
author tomwalters
date Sat, 20 Feb 2010 17:56:40 +0000
parents b4cafba48e9d
children f4e712d41321
rev   line source
tomwalters@4 1 // Copyright 2009-2010, Thomas Walters
tomwalters@4 2 //
tomwalters@4 3 // AIM-C: A C++ implementation of the Auditory Image Model
tomwalters@4 4 // http://www.acousticscale.org/AIMC
tomwalters@4 5 //
tomwalters@4 6 // This program is free software: you can redistribute it and/or modify
tomwalters@4 7 // it under the terms of the GNU General Public License as published by
tomwalters@4 8 // the Free Software Foundation, either version 3 of the License, or
tomwalters@4 9 // (at your option) any later version.
tomwalters@4 10 //
tomwalters@4 11 // This program is distributed in the hope that it will be useful,
tomwalters@4 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
tomwalters@4 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
tomwalters@4 14 // GNU General Public License for more details.
tomwalters@4 15 //
tomwalters@4 16 // You should have received a copy of the GNU General Public License
tomwalters@4 17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
tomwalters@4 18
tomwalters@4 19 /*! \file
tomwalters@4 20 * \brief Slaney's gammatone filterbank
tomwalters@4 21 *
tomwalters@4 22 * \author Thomas Walters <tom@acousticscale.org>
tomwalters@4 23 * \date created 2009/11/13
tomwalters@4 24 * \version \$Id$
tomwalters@4 25 */
tomwalters@4 26
tomwalters@15 27 #include <cmath>
tomwalters@4 28 #include <complex>
tomwalters@5 29 #include "Support/ERBTools.h"
tomwalters@4 30
tomwalters@4 31 #include "Modules/BMM/ModuleGammatone.h"
tomwalters@4 32
tomwalters@4 33 namespace aimc {
tomwalters@5 34 using std::vector;
tomwalters@4 35 using std::complex;
tomwalters@4 36 ModuleGammatone::ModuleGammatone(Parameters *params) : Module(params) {
tomwalters@5 37 module_identifier_ = "gt";
tomwalters@4 38 module_type_ = "bmm";
tomwalters@4 39 module_description_ = "Gammatone filterbank (Slaney's IIR gammatone)";
tomwalters@4 40 module_version_ = "$Id$";
tomwalters@4 41
tomwalters@4 42 num_channels_ = parameters_->DefaultInt("gtfb.channel_count", 200);
tomwalters@4 43 min_frequency_ = parameters_->DefaultFloat("gtfb.min_frequency", 86.0f);
tomwalters@4 44 max_frequency_ = parameters_->DefaultFloat("gtfb.max_frequency", 16000.0f);
tomwalters@4 45 }
tomwalters@4 46
tomwalters@5 47 ModuleGammatone::~ModuleGammatone() {
tomwalters@5 48 }
tomwalters@5 49
tomwalters@5 50 void ModuleGammatone::ResetInternal() {
tomwalters@16 51 state_1_.resize(num_channels_);
tomwalters@16 52 state_2_.resize(num_channels_);
tomwalters@16 53 state_3_.resize(num_channels_);
tomwalters@16 54 state_4_.resize(num_channels_);
tomwalters@5 55 for (int i = 0; i < num_channels_; ++i) {
tomwalters@16 56 state_1_[i].resize(3, 0.0f);
tomwalters@16 57 state_2_[i].resize(3, 0.0f);
tomwalters@16 58 state_3_[i].resize(3, 0.0f);
tomwalters@16 59 state_4_[i].resize(3, 0.0f);
tomwalters@5 60 }
tomwalters@5 61 }
tomwalters@5 62
tomwalters@4 63 bool ModuleGammatone::InitializeInternal(const SignalBank& input) {
tomwalters@4 64 // Calculate number of channels, and centre frequencies
tomwalters@5 65 float erb_max = ERBTools::Freq2ERB(max_frequency_);
tomwalters@5 66 float erb_min = ERBTools::Freq2ERB(min_frequency_);
tomwalters@5 67 float delta_erb = (erb_max - erb_min) / (num_channels_ - 1);
tomwalters@5 68
tomwalters@5 69 centre_frequencies_.resize(num_channels_);
tomwalters@5 70 float erb_current = erb_min;
tomwalters@5 71
tomwalters@16 72 output_.Initialize(num_channels_,
tomwalters@16 73 input.buffer_length(),
tomwalters@16 74 input.sample_rate());
tomwalters@16 75
tomwalters@5 76 for (int i = 0; i < num_channels_; ++i) {
tomwalters@8 77 centre_frequencies_[i] = ERBTools::ERB2Freq(erb_current);
tomwalters@8 78 erb_current += delta_erb;
tomwalters@16 79 output_.set_centre_frequency(i, centre_frequencies_[i]);
tomwalters@5 80 }
tomwalters@4 81
tomwalters@16 82 a_.resize(num_channels_);
tomwalters@16 83 b1_.resize(num_channels_);
tomwalters@16 84 b2_.resize(num_channels_);
tomwalters@16 85 b3_.resize(num_channels_);
tomwalters@16 86 b4_.resize(num_channels_);
tomwalters@16 87 state_1_.resize(num_channels_);
tomwalters@16 88 state_2_.resize(num_channels_);
tomwalters@16 89 state_3_.resize(num_channels_);
tomwalters@16 90 state_4_.resize(num_channels_);
tomwalters@4 91
tomwalters@4 92 for (int ch = 0; ch < num_channels_; ++ch) {
tomwalters@16 93 double cf = centre_frequencies_[ch];
tomwalters@16 94 double erb = ERBTools::Freq2ERBw(cf);
tomwalters@4 95
tomwalters@4 96 // Sample interval
tomwalters@16 97 double dt = 1.0f / input.sample_rate();
tomwalters@4 98
tomwalters@4 99 // Bandwidth parameter
tomwalters@16 100 double b = 1.019f * 2.0f * M_PI * erb;
tomwalters@4 101
tomwalters@16 102 // The following expressions are derived in Apple TR #35, "An
tomwalters@8 103 // Efficient Implementation of the Patterson-Holdsworth Cochlear
tomwalters@16 104 // Filter Bank" and used in Malcolm Slaney's auditory toolbox, where he
tomwalters@16 105 // defines this alternaltive four stage cascade of second-order filters.
tomwalters@4 106
tomwalters@4 107 // Calculate the gain:
tomwalters@16 108 double cpt = cf * M_PI * dt;
tomwalters@16 109 complex<double> exponent(0.0, 2.0 * cpt);
tomwalters@16 110 complex<double> ec = exp(2.0 * exponent);
tomwalters@16 111 complex<double> two_cf_pi_t(2.0 * cpt, 0.0);
tomwalters@16 112 complex<double> two_pow(pow(2.0, (3.0 / 2.0)), 0.0);
tomwalters@16 113 complex<double> p = -2.0 * ec * dt
tomwalters@16 114 + 2.0 * exp(-(b * dt) + exponent) * dt;
tomwalters@16 115 complex<double> b_dt(b * dt, 0.0);
tomwalters@4 116
tomwalters@16 117 double gain = abs(
tomwalters@16 118 (p * (cos(two_cf_pi_t) - sqrt(3.0 - two_pow) * sin(two_cf_pi_t)))
tomwalters@16 119 * (p * (cos(two_cf_pi_t) + sqrt(3.0 - two_pow) * sin(two_cf_pi_t)))
tomwalters@16 120 * (p * (cos(two_cf_pi_t) - sqrt(3.0 + two_pow) * sin(two_cf_pi_t)))
tomwalters@16 121 * (p * (cos(two_cf_pi_t) + sqrt(3.0 + two_pow) * sin(two_cf_pi_t)))
tomwalters@16 122 / pow(-2.0 / exp(2.0 * b_dt) - 2.0 * ec + 2.0 * (1.0 + ec)
tomwalters@16 123 / exp(b_dt), 4.0));
tomwalters@4 124
tomwalters@4 125 // The filter coefficients themselves:
tomwalters@16 126 const int coeff_count = 3;
tomwalters@16 127 a_[ch].resize(coeff_count, 0.0f);
tomwalters@16 128 b1_[ch].resize(coeff_count, 0.0f);
tomwalters@16 129 b2_[ch].resize(coeff_count, 0.0f);
tomwalters@16 130 b3_[ch].resize(coeff_count, 0.0f);
tomwalters@16 131 b4_[ch].resize(coeff_count, 0.0f);
tomwalters@16 132 state_1_[ch].resize(coeff_count, 0.0f);
tomwalters@16 133 state_2_[ch].resize(coeff_count, 0.0f);
tomwalters@16 134 state_3_[ch].resize(coeff_count, 0.0f);
tomwalters@16 135 state_4_[ch].resize(coeff_count, 0.0f);
tomwalters@4 136
tomwalters@16 137 double B0 = dt;
tomwalters@16 138 double B2 = 0.0f;
tomwalters@4 139
tomwalters@16 140 double B11 = -(2.0f * dt * cos(2.0f * cf * M_PI * dt) / exp(b * dt)
tomwalters@16 141 + 2.0f * sqrt(3 + pow(2.0f, 1.5f)) * dt
tomwalters@16 142 * sin(2.0f * cf * M_PI * dt) / exp(b * dt)) / 2.0f;
tomwalters@16 143 double B12 = -(2.0f * dt * cos(2.0f * cf * M_PI * dt) / exp(b * dt)
tomwalters@16 144 - 2.0f * sqrt(3 + pow(2.0f, 1.5f)) * dt
tomwalters@16 145 * sin(2.0f * cf * M_PI * dt) / exp(b * dt)) / 2.0f;
tomwalters@16 146 double B13 = -(2.0f * dt * cos(2.0f * cf * M_PI * dt) / exp(b * dt)
tomwalters@16 147 + 2.0f * sqrt(3 - pow(2.0f, 1.5f)) * dt
tomwalters@16 148 * sin(2.0f * cf * M_PI * dt) / exp(b * dt)) / 2.0f;
tomwalters@16 149 double B14 = -(2.0f * dt * cos(2.0f * cf * M_PI * dt) / exp(b * dt)
tomwalters@16 150 - 2.0f * sqrt(3 - pow(2.0f, 1.5f)) * dt
tomwalters@16 151 * sin(2.0f * cf * M_PI * dt) / exp(b * dt)) / 2.0f;;
tomwalters@16 152
tomwalters@16 153 a_[ch][0] = 1.0f;
tomwalters@16 154 a_[ch][1] = -2.0f * cos(2.0f * cf * M_PI * dt) / exp(b * dt);
tomwalters@16 155 a_[ch][2] = exp(-2.0f * b * dt);
tomwalters@16 156 b1_[ch][0] = B0 / gain;
tomwalters@16 157 b1_[ch][1] = B11 / gain;
tomwalters@16 158 b1_[ch][2] = B2 / gain;
tomwalters@16 159 b2_[ch][0] = B0;
tomwalters@16 160 b2_[ch][1] = B12;
tomwalters@16 161 b2_[ch][2] = B2;
tomwalters@16 162 b3_[ch][0] = B0;
tomwalters@16 163 b3_[ch][1] = B13;
tomwalters@16 164 b3_[ch][2] = B2;
tomwalters@16 165 b4_[ch][0] = B0;
tomwalters@16 166 b4_[ch][1] = B14;
tomwalters@16 167 b4_[ch][2] = B2;
tomwalters@16 168
tomwalters@4 169 }
tomwalters@5 170 return true;
tomwalters@4 171 }
tomwalters@5 172
tomwalters@5 173 void ModuleGammatone::Process(const SignalBank &input) {
tomwalters@5 174 output_.set_start_time(input.start_time());
tomwalters@5 175 int audio_channel = 0;
tomwalters@5 176
tomwalters@16 177 vector<vector<double> >::iterator b1 = b1_.begin();
tomwalters@16 178 vector<vector<double> >::iterator b2 = b2_.begin();
tomwalters@16 179 vector<vector<double> >::iterator b3 = b3_.begin();
tomwalters@16 180 vector<vector<double> >::iterator b4 = b4_.begin();
tomwalters@16 181 vector<vector<double> >::iterator a = a_.begin();
tomwalters@16 182 vector<vector<double> >::iterator s1 = state_1_.begin();
tomwalters@16 183 vector<vector<double> >::iterator s2 = state_2_.begin();
tomwalters@16 184 vector<vector<double> >::iterator s3 = state_3_.begin();
tomwalters@16 185 vector<vector<double> >::iterator s4 = state_4_.begin();
tomwalters@5 186
tomwalters@16 187 // Temporary storage between filter stages
tomwalters@16 188 vector<double> out(input.buffer_length());
tomwalters@16 189 for (int ch = 0; ch < num_channels_;
tomwalters@16 190 ++ch, ++b1, ++b2, ++b3, ++b4, ++a, ++s1, ++s2, ++s3, ++s4) {
tomwalters@5 191 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@8 192 // Direct-form-II IIR filter
tomwalters@16 193 double in = input.sample(audio_channel, i);
tomwalters@16 194 out[i] = (*b1)[0] * in + (*s1)[0];
tomwalters@16 195 for (unsigned int stage = 1; stage < s1->size(); ++stage)
tomwalters@16 196 (*s1)[stage - 1] = (*b1)[stage] * in
tomwalters@16 197 - (*a)[stage] * out[i] + (*s1)[stage];
tomwalters@16 198 }
tomwalters@16 199 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@16 200 // Direct-form-II IIR filter
tomwalters@16 201 double in = out[i];
tomwalters@16 202 out[i] = (*b2)[0] * in + (*s2)[0];
tomwalters@16 203 for (unsigned int stage = 1; stage < s2->size(); ++stage)
tomwalters@16 204 (*s2)[stage - 1] = (*b2)[stage] * in
tomwalters@16 205 - (*a)[stage] * out[i] + (*s2)[stage];
tomwalters@16 206 }
tomwalters@16 207 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@16 208 // Direct-form-II IIR filter
tomwalters@16 209 double in = out[i];
tomwalters@16 210 out[i] = (*b3)[0] * in + (*s3)[0];
tomwalters@16 211 for (unsigned int stage = 1; stage < s3->size(); ++stage)
tomwalters@16 212 (*s3)[stage - 1] = (*b3)[stage] * in
tomwalters@16 213 - (*a)[stage] * out[i] + (*s3)[stage];
tomwalters@16 214 }
tomwalters@16 215 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@16 216 // Direct-form-II IIR filter
tomwalters@16 217 double in = out[i];
tomwalters@16 218 out[i] = (*b4)[0] * in + (*s4)[0];
tomwalters@16 219 for (unsigned int stage = 1; stage < s4->size(); ++stage)
tomwalters@16 220 (*s4)[stage - 1] = (*b4)[stage] * in
tomwalters@16 221 - (*a)[stage] * out[i] + (*s4)[stage];
tomwalters@16 222 output_.set_sample(ch, i, out[i]);
tomwalters@5 223 }
tomwalters@5 224 }
tomwalters@5 225 PushOutput();
tomwalters@5 226 }
tomwalters@5 227
tomwalters@8 228 } // namespace aimc