annotate trunk/src/Modules/BMM/ModuleGammatone.cc @ 288:34993448961f

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