annotate trunk/src/Modules/BMM/ModuleGammatone.cc @ 289:6cf55200a199

-Added basic support for unit tests using gtest -Updated lint scripts to exclude header guard problems -Made everything lint-friendly -Added a trivial script to build the Doxygen documentation
author tomwalters
date Sat, 20 Feb 2010 21:03:57 +0000
parents 34993448961f
children e344ef4898b2
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@289 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@276 168 }
tomwalters@277 169 return true;
tomwalters@276 170 }
tomwalters@277 171
tomwalters@277 172 void ModuleGammatone::Process(const SignalBank &input) {
tomwalters@277 173 output_.set_start_time(input.start_time());
tomwalters@277 174 int audio_channel = 0;
tomwalters@277 175
tomwalters@288 176 vector<vector<double> >::iterator b1 = b1_.begin();
tomwalters@288 177 vector<vector<double> >::iterator b2 = b2_.begin();
tomwalters@288 178 vector<vector<double> >::iterator b3 = b3_.begin();
tomwalters@288 179 vector<vector<double> >::iterator b4 = b4_.begin();
tomwalters@288 180 vector<vector<double> >::iterator a = a_.begin();
tomwalters@288 181 vector<vector<double> >::iterator s1 = state_1_.begin();
tomwalters@288 182 vector<vector<double> >::iterator s2 = state_2_.begin();
tomwalters@288 183 vector<vector<double> >::iterator s3 = state_3_.begin();
tomwalters@288 184 vector<vector<double> >::iterator s4 = state_4_.begin();
tomwalters@277 185
tomwalters@288 186 // Temporary storage between filter stages
tomwalters@288 187 vector<double> out(input.buffer_length());
tomwalters@288 188 for (int ch = 0; ch < num_channels_;
tomwalters@288 189 ++ch, ++b1, ++b2, ++b3, ++b4, ++a, ++s1, ++s2, ++s3, ++s4) {
tomwalters@277 190 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@280 191 // Direct-form-II IIR filter
tomwalters@288 192 double in = input.sample(audio_channel, i);
tomwalters@288 193 out[i] = (*b1)[0] * in + (*s1)[0];
tomwalters@288 194 for (unsigned int stage = 1; stage < s1->size(); ++stage)
tomwalters@288 195 (*s1)[stage - 1] = (*b1)[stage] * in
tomwalters@288 196 - (*a)[stage] * out[i] + (*s1)[stage];
tomwalters@288 197 }
tomwalters@288 198 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@288 199 // Direct-form-II IIR filter
tomwalters@288 200 double in = out[i];
tomwalters@288 201 out[i] = (*b2)[0] * in + (*s2)[0];
tomwalters@288 202 for (unsigned int stage = 1; stage < s2->size(); ++stage)
tomwalters@288 203 (*s2)[stage - 1] = (*b2)[stage] * in
tomwalters@288 204 - (*a)[stage] * out[i] + (*s2)[stage];
tomwalters@288 205 }
tomwalters@288 206 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@288 207 // Direct-form-II IIR filter
tomwalters@288 208 double in = out[i];
tomwalters@288 209 out[i] = (*b3)[0] * in + (*s3)[0];
tomwalters@288 210 for (unsigned int stage = 1; stage < s3->size(); ++stage)
tomwalters@288 211 (*s3)[stage - 1] = (*b3)[stage] * in
tomwalters@288 212 - (*a)[stage] * out[i] + (*s3)[stage];
tomwalters@288 213 }
tomwalters@288 214 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@288 215 // Direct-form-II IIR filter
tomwalters@288 216 double in = out[i];
tomwalters@288 217 out[i] = (*b4)[0] * in + (*s4)[0];
tomwalters@288 218 for (unsigned int stage = 1; stage < s4->size(); ++stage)
tomwalters@288 219 (*s4)[stage - 1] = (*b4)[stage] * in
tomwalters@288 220 - (*a)[stage] * out[i] + (*s4)[stage];
tomwalters@288 221 output_.set_sample(ch, i, out[i]);
tomwalters@277 222 }
tomwalters@277 223 }
tomwalters@277 224 PushOutput();
tomwalters@277 225 }
tomwalters@277 226
tomwalters@280 227 } // namespace aimc