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@4
|
27 #include <complex>
|
tomwalters@5
|
28 #include <math.h>
|
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@5
|
51 state_.resize(num_channels_);
|
tomwalters@5
|
52 for (int i = 0; i < num_channels_; ++i) {
|
tomwalters@5
|
53 state_[i].resize(9, 0.0f);
|
tomwalters@5
|
54 }
|
tomwalters@5
|
55 }
|
tomwalters@5
|
56
|
tomwalters@4
|
57 bool ModuleGammatone::InitializeInternal(const SignalBank& input) {
|
tomwalters@4
|
58 // Calculate number of channels, and centre frequencies
|
tomwalters@5
|
59 float erb_max = ERBTools::Freq2ERB(max_frequency_);
|
tomwalters@5
|
60 float erb_min = ERBTools::Freq2ERB(min_frequency_);
|
tomwalters@5
|
61 float delta_erb = (erb_max - erb_min) / (num_channels_ - 1);
|
tomwalters@5
|
62
|
tomwalters@5
|
63 centre_frequencies_.resize(num_channels_);
|
tomwalters@5
|
64 float erb_current = erb_min;
|
tomwalters@5
|
65
|
tomwalters@5
|
66 for (int i = 0; i < num_channels_; ++i) {
|
tomwalters@5
|
67 centre_frequencies_[i] = ERBTools::ERB2Freq(erb_current);
|
tomwalters@5
|
68 erb_current += delta_erb;
|
tomwalters@5
|
69 }
|
tomwalters@4
|
70
|
tomwalters@4
|
71 forward_.resize(num_channels_);
|
tomwalters@5
|
72 back_.resize(num_channels_);
|
tomwalters@5
|
73 state_.resize(num_channels_);
|
tomwalters@4
|
74
|
tomwalters@4
|
75 for (int ch = 0; ch < num_channels_; ++ch) {
|
tomwalters@5
|
76 float cf = centre_frequencies_[ch];
|
tomwalters@5
|
77 float erb = ERBTools::Freq2ERBw(cf);
|
tomwalters@4
|
78
|
tomwalters@4
|
79 // Sample interval
|
tomwalters@5
|
80 float dt = 1.0f / input.sample_rate();
|
tomwalters@4
|
81
|
tomwalters@4
|
82 // Bandwidth parameter
|
tomwalters@4
|
83 float b = 1.019f * 2.0f * M_PI * erb;
|
tomwalters@4
|
84
|
tomwalters@4
|
85 // All of the following expressions are derived in Apple TR #35, "An
|
tomwalters@4
|
86 // Efficient Implementation of the Patterson-Holdsworth Cochlear
|
tomwalters@4
|
87 // Filter Bank".
|
tomwalters@4
|
88
|
tomwalters@4
|
89 // Calculate the gain:
|
tomwalters@5
|
90 float cpt = cf * M_PI * dt;
|
tomwalters@5
|
91 complex<float> exponent(0.0f, 2.0f * cpt);
|
tomwalters@5
|
92 complex<float> ec = exp(2.0f * exponent);
|
tomwalters@5
|
93 complex<float> two_cf_pi_t(2.0f * cpt, 0.0f);
|
tomwalters@5
|
94 complex<float> two_pow(pow(2.0f, (3.0f / 2.0f)), 0.0f);
|
tomwalters@5
|
95 complex<float> p = -2.0f * ec * dt
|
tomwalters@5
|
96 + 2.0f * exp(-(b * dt) + exponent) * dt;
|
tomwalters@5
|
97 complex<float> b_dt(b * dt, 0.0f);
|
tomwalters@4
|
98
|
tomwalters@4
|
99 float gain = abs(
|
tomwalters@5
|
100 (p * (cos(two_cf_pi_t) - sqrt(3.0f - two_pow) * sin(two_cf_pi_t)))
|
tomwalters@5
|
101 * (p * (cos(two_cf_pi_t) + sqrt(3.0f - two_pow) * sin(two_cf_pi_t)))
|
tomwalters@5
|
102 * (p * (cos(two_cf_pi_t) - sqrt(3.0f + two_pow) * sin(two_cf_pi_t)))
|
tomwalters@5
|
103 * (p * (cos(two_cf_pi_t) + sqrt(3.0f + two_pow) * sin(two_cf_pi_t)))
|
tomwalters@5
|
104 / pow(-2.0f / exp(2.0f * b_dt) - 2.0f * ec + 2.0f * (1.0f + ec)
|
tomwalters@5
|
105 / exp(b_dt), 4.0f));
|
tomwalters@4
|
106
|
tomwalters@4
|
107 // The filter coefficients themselves:
|
tomwalters@5
|
108 const int coeff_count = 9;
|
tomwalters@5
|
109 forward_[ch].resize(coeff_count, 0.0f);
|
tomwalters@5
|
110 back_[ch].resize(coeff_count, 0.0f);
|
tomwalters@5
|
111 state_[ch].resize(coeff_count, 0.0f);
|
tomwalters@4
|
112
|
tomwalters@5
|
113 forward_[ch][0] = pow(dt, 4.0f) / gain;
|
tomwalters@5
|
114 forward_[ch][1] = (-4.0f * pow(dt, 4.0f) * cos(2.0f * cpt)
|
tomwalters@4
|
115 / exp(b * dt) / gain);
|
tomwalters@5
|
116 forward_[ch][2] = (6.0f * pow(dt, 4.0f) * cos(4.0f * cpt)
|
tomwalters@4
|
117 / exp(2.0f * b * dt) / gain);
|
tomwalters@5
|
118 forward_[ch][3] = (-4.0f * pow(dt, 4.0f) * cos(6.0f * cpt)
|
tomwalters@4
|
119 / exp(3.0f * b * dt) / gain);
|
tomwalters@5
|
120 forward_[ch][4] = (pow(dt, 4.0f) * cos(8.0f * cpt)
|
tomwalters@4
|
121 / exp(4.0f * b * dt) / gain);
|
tomwalters@5
|
122 // Note: the remainder of the forward vector is zero-padded
|
tomwalters@4
|
123
|
tomwalters@5
|
124 back_[ch][0] = 1.0f;
|
tomwalters@5
|
125 back_[ch][1] = -8.0f * cos(2.0f * cpt) / exp(b * dt);
|
tomwalters@5
|
126 back_[ch][2] = (4.0f * (4.0f + 3.0f * cos(4.0f * cpt))
|
tomwalters@5
|
127 / exp(2.0f * b * dt));
|
tomwalters@5
|
128 back_[ch][3] = (-8.0f * (6.0f * cos(2.0f * cpt) + cos(6.0f * cpt))
|
tomwalters@5
|
129 / exp(3.0f * b * dt));
|
tomwalters@5
|
130 back_[ch][4] = (2.0f * (18.0f + 16.0f * cos(4.0f * cpt) + cos(8.0f * cpt))
|
tomwalters@5
|
131 / exp(4.0f * b * dt));
|
tomwalters@5
|
132 back_[ch][5] = (-8.0f * (6.0f * cos(2.0f * cpt) + cos(6.0f * cpt))
|
tomwalters@5
|
133 / exp(5.0f * b * dt));
|
tomwalters@5
|
134 back_[ch][6] = (4.0f * (4.0f + 3.0f * cos(4.0f * cpt))
|
tomwalters@5
|
135 / exp(6.0f * b * dt));
|
tomwalters@5
|
136 back_[ch][7] = -8.0f * cos(2.0f * cpt) / exp(7.0f * b * dt);
|
tomwalters@5
|
137 back_[ch][8] = exp(-8.0f * b * dt);
|
tomwalters@4
|
138 }
|
tomwalters@5
|
139 output_.Initialize(num_channels_,
|
tomwalters@5
|
140 input.buffer_length(),
|
tomwalters@5
|
141 input.sample_rate());
|
tomwalters@5
|
142 return true;
|
tomwalters@4
|
143 }
|
tomwalters@5
|
144
|
tomwalters@5
|
145 void ModuleGammatone::Process(const SignalBank &input) {
|
tomwalters@5
|
146 output_.set_start_time(input.start_time());
|
tomwalters@5
|
147 int audio_channel = 0;
|
tomwalters@5
|
148
|
tomwalters@5
|
149 vector<vector<float> >::iterator b = forward_.begin();
|
tomwalters@5
|
150 vector<vector<float> >::iterator a = back_.begin();
|
tomwalters@5
|
151 vector<vector<float> >::iterator s = state_.begin();
|
tomwalters@5
|
152
|
tomwalters@5
|
153 for (int ch = 0; ch < num_channels_; ++ch, ++a, ++b, ++s) {
|
tomwalters@5
|
154 for (int i = 0; i < input.buffer_length(); ++i) {
|
tomwalters@5
|
155 // Direct-form-II IIR filter
|
tomwalters@5
|
156 float in = input.sample(audio_channel, i);
|
tomwalters@5
|
157 float out = (*b)[0] * in + (*s)[0];
|
tomwalters@5
|
158 for (unsigned int stage = 1; stage < s->size(); ++stage)
|
tomwalters@5
|
159 (*s)[stage - 1] = (*b)[stage] * in - (*a)[stage] * out + (*s)[stage];
|
tomwalters@5
|
160 output_.set_sample(ch, i, out);
|
tomwalters@5
|
161 }
|
tomwalters@5
|
162 }
|
tomwalters@5
|
163 PushOutput();
|
tomwalters@5
|
164 }
|
tomwalters@5
|
165
|
tomwalters@4
|
166 } // namespace aimc |