alexbrandmeyer@609
|
1 //
|
alexbrandmeyer@609
|
2 // carfac.cc
|
alexbrandmeyer@609
|
3 // CARFAC Open Source C++ Library
|
alexbrandmeyer@609
|
4 //
|
alexbrandmeyer@609
|
5 // Created by Alex Brandmeyer on 5/10/13.
|
alexbrandmeyer@609
|
6 //
|
alexbrandmeyer@609
|
7 // This C++ file is part of an implementation of Lyon's cochlear model:
|
alexbrandmeyer@609
|
8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
alexbrandmeyer@609
|
9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
|
alexbrandmeyer@609
|
10 //
|
alexbrandmeyer@609
|
11 // Licensed under the Apache License, Version 2.0 (the "License");
|
alexbrandmeyer@609
|
12 // you may not use this file except in compliance with the License.
|
alexbrandmeyer@609
|
13 // You may obtain a copy of the License at
|
alexbrandmeyer@609
|
14 //
|
alexbrandmeyer@609
|
15 // http://www.apache.org/licenses/LICENSE-2.0
|
alexbrandmeyer@609
|
16 //
|
alexbrandmeyer@609
|
17 // Unless required by applicable law or agreed to in writing, software
|
alexbrandmeyer@609
|
18 // distributed under the License is distributed on an "AS IS" BASIS,
|
alexbrandmeyer@609
|
19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
alexbrandmeyer@609
|
20 // See the License for the specific language governing permissions and
|
alexbrandmeyer@609
|
21 // limitations under the License.
|
alexbrandmeyer@640
|
22
|
alexbrandmeyer@636
|
23 #include <assert.h>
|
alexbrandmeyer@643
|
24
|
alexbrandmeyer@609
|
25 #include "carfac.h"
|
alexbrandmeyer@643
|
26
|
alexbrandmeyer@636
|
27 using std::vector;
|
ronw@625
|
28
|
alexbrandmeyer@643
|
29 CARFAC::CARFAC(const int num_ears, const FPType sample_rate,
|
ronw@645
|
30 const CARParams& car_params, const IHCParams& ihc_params,
|
ronw@645
|
31 const AGCParams& agc_params) {
|
ronw@645
|
32 Reset(num_ears, sample_rate, car_params, ihc_params, agc_params);
|
ronw@645
|
33 }
|
ronw@645
|
34
|
ronw@645
|
35 void CARFAC::Reset(const int num_ears, const FPType sample_rate,
|
ronw@645
|
36 const CARParams& car_params, const IHCParams& ihc_params,
|
ronw@645
|
37 const AGCParams& agc_params) {
|
alexbrandmeyer@643
|
38 num_ears_ = num_ears;
|
alexbrandmeyer@643
|
39 sample_rate_ = sample_rate;
|
alexbrandmeyer@643
|
40 car_params_ = car_params;
|
alexbrandmeyer@643
|
41 ihc_params_ = ihc_params;
|
alexbrandmeyer@643
|
42 agc_params_ = agc_params;
|
alexbrandmeyer@643
|
43 num_channels_ = 0;
|
alexbrandmeyer@643
|
44 FPType pole_hz = car_params_.first_pole_theta * sample_rate_ / (2 * kPi);
|
alexbrandmeyer@643
|
45 while (pole_hz > car_params_.min_pole_hz) {
|
alexbrandmeyer@643
|
46 ++num_channels_;
|
alexbrandmeyer@643
|
47 pole_hz = pole_hz - car_params_.erb_per_step *
|
alexbrandmeyer@643
|
48 ERBHz(pole_hz, car_params_.erb_break_freq, car_params_.erb_q);
|
alexbrandmeyer@609
|
49 }
|
alexbrandmeyer@643
|
50 pole_freqs_.resize(num_channels_);
|
alexbrandmeyer@643
|
51 pole_hz = car_params_.first_pole_theta * sample_rate_ / (2 * kPi);
|
alexbrandmeyer@643
|
52 for (int channel = 0; channel < num_channels_; ++channel) {
|
alexbrandmeyer@643
|
53 pole_freqs_(channel) = pole_hz;
|
alexbrandmeyer@643
|
54 pole_hz = pole_hz - car_params_.erb_per_step *
|
alexbrandmeyer@643
|
55 ERBHz(pole_hz, car_params_.erb_break_freq, car_params_.erb_q);
|
alexbrandmeyer@610
|
56 }
|
alexbrandmeyer@626
|
57 max_channels_per_octave_ = log(2) / log(pole_freqs_(0) / pole_freqs_(1));
|
alexbrandmeyer@636
|
58 CARCoeffs car_coeffs;
|
alexbrandmeyer@636
|
59 IHCCoeffs ihc_coeffs;
|
alexbrandmeyer@636
|
60 std::vector<AGCCoeffs> agc_coeffs;
|
alexbrandmeyer@643
|
61 DesignCARCoeffs(car_params_, sample_rate_, pole_freqs_, &car_coeffs);
|
alexbrandmeyer@643
|
62 DesignIHCCoeffs(ihc_params_, sample_rate_, &ihc_coeffs);
|
alexbrandmeyer@643
|
63 DesignAGCCoeffs(agc_params_, sample_rate_, &agc_coeffs);
|
alexbrandmeyer@636
|
64 // Once we have the coefficient structure we can design the ears.
|
ronw@645
|
65 ears_.reserve(num_ears_);
|
ronw@645
|
66 for (int i = 0; i < num_ears_; ++i) {
|
ronw@646
|
67 if (ears_.size() > i && ears_[i] != NULL) {
|
ronw@646
|
68 // Reset any existing ears.
|
ronw@646
|
69 ears_[i]->Reset(num_channels_, car_coeffs, ihc_coeffs, agc_coeffs);
|
ronw@646
|
70 } else {
|
ronw@646
|
71 ears_.push_back(
|
ronw@646
|
72 new Ear(num_channels_, car_coeffs, ihc_coeffs, agc_coeffs));
|
ronw@646
|
73 }
|
alexbrandmeyer@610
|
74 }
|
alexbrandmeyer@609
|
75 }
|
alexbrandmeyer@609
|
76
|
alexbrandmeyer@636
|
77 void CARFAC::RunSegment(const vector<vector<float>>& sound_data,
|
alexbrandmeyer@626
|
78 const int32_t start, const int32_t length,
|
alexbrandmeyer@636
|
79 const bool open_loop, CARFACOutput* seg_output) {
|
ronw@646
|
80 assert(sound_data.size() == num_ears_);
|
alexbrandmeyer@610
|
81 // A nested loop structure is used to iterate through the individual samples
|
alexbrandmeyer@610
|
82 // for each ear (audio channel).
|
alexbrandmeyer@626
|
83 bool updated; // This variable is used by the AGC stage.
|
alexbrandmeyer@643
|
84 for (int32_t timepoint = 0; timepoint < length; ++timepoint) {
|
alexbrandmeyer@643
|
85 for (int audio_channel = 0; audio_channel < num_ears_; ++audio_channel) {
|
ronw@645
|
86 Ear* ear = ears_[audio_channel];
|
alexbrandmeyer@610
|
87 // This stores the audio sample currently being processed.
|
alexbrandmeyer@643
|
88 FPType input = sound_data[audio_channel][start + timepoint];
|
ronw@644
|
89
|
alexbrandmeyer@610
|
90 // Now we apply the three stages of the model in sequence to the current
|
alexbrandmeyer@610
|
91 // audio sample.
|
ronw@645
|
92 ear->CARStep(input);
|
ronw@645
|
93 ear->IHCStep(ear->car_out());
|
ronw@645
|
94 updated = ear->AGCStep(ear->ihc_out());
|
alexbrandmeyer@610
|
95 }
|
alexbrandmeyer@643
|
96 seg_output->AppendOutput(ears_);
|
alexbrandmeyer@636
|
97 if (updated) {
|
alexbrandmeyer@643
|
98 if (num_ears_ > 1) {
|
alexbrandmeyer@636
|
99 CrossCouple();
|
alexbrandmeyer@636
|
100 }
|
ronw@646
|
101 if (!open_loop) {
|
alexbrandmeyer@636
|
102 CloseAGCLoop();
|
alexbrandmeyer@636
|
103 }
|
alexbrandmeyer@626
|
104 }
|
alexbrandmeyer@610
|
105 }
|
alexbrandmeyer@610
|
106 }
|
alexbrandmeyer@610
|
107
|
alexbrandmeyer@610
|
108 void CARFAC::CrossCouple() {
|
ronw@645
|
109 for (int stage = 0; stage < ears_[0]->agc_num_stages(); ++stage) {
|
ronw@645
|
110 if (ears_[0]->agc_decim_phase(stage) > 0) {
|
alexbrandmeyer@610
|
111 break;
|
alexbrandmeyer@610
|
112 } else {
|
ronw@645
|
113 FPType mix_coeff = ears_[0]->agc_mix_coeff(stage);
|
alexbrandmeyer@610
|
114 if (mix_coeff > 0) {
|
alexbrandmeyer@643
|
115 ArrayX stage_state;
|
alexbrandmeyer@643
|
116 ArrayX this_stage_values = ArrayX::Zero(num_channels_);
|
ronw@645
|
117 for (const auto& ear : ears_) {
|
ronw@645
|
118 stage_state = ear->agc_memory(stage);
|
alexbrandmeyer@610
|
119 this_stage_values += stage_state;
|
alexbrandmeyer@610
|
120 }
|
alexbrandmeyer@643
|
121 this_stage_values /= num_ears_;
|
ronw@645
|
122 for (const auto& ear : ears_) {
|
ronw@645
|
123 stage_state = ear->agc_memory(stage);
|
ronw@645
|
124 ear->set_agc_memory(stage, stage_state + mix_coeff *
|
ronw@645
|
125 (this_stage_values - stage_state));
|
alexbrandmeyer@610
|
126 }
|
alexbrandmeyer@610
|
127 }
|
alexbrandmeyer@609
|
128 }
|
alexbrandmeyer@609
|
129 }
|
alexbrandmeyer@609
|
130 }
|
alexbrandmeyer@610
|
131
|
alexbrandmeyer@610
|
132 void CARFAC::CloseAGCLoop() {
|
ronw@645
|
133 for (auto& ear : ears_) {
|
ronw@645
|
134 ArrayX undamping = 1 - ear->agc_memory(0);
|
alexbrandmeyer@610
|
135 // This updates the target stage gain for the new damping.
|
ronw@645
|
136 ear->set_dzb_memory((ear->zr_coeffs() * undamping - ear->zb_memory()) /
|
ronw@645
|
137 ear->agc_decimation(0));
|
ronw@645
|
138 ear->set_dg_memory((ear->StageGValue(undamping) - ear->g_memory()) /
|
ronw@645
|
139 ear->agc_decimation(0));
|
alexbrandmeyer@610
|
140 }
|
alexbrandmeyer@636
|
141 }
|
alexbrandmeyer@636
|
142
|
alexbrandmeyer@643
|
143 void CARFAC::DesignCARCoeffs(const CARParams& car_params,
|
alexbrandmeyer@643
|
144 const FPType sample_rate,
|
alexbrandmeyer@643
|
145 const ArrayX& pole_freqs,
|
alexbrandmeyer@636
|
146 CARCoeffs* car_coeffs) {
|
alexbrandmeyer@643
|
147 num_channels_ = pole_freqs.size();
|
alexbrandmeyer@640
|
148 car_coeffs->velocity_scale = car_params.velocity_scale;
|
alexbrandmeyer@640
|
149 car_coeffs->v_offset = car_params.v_offset;
|
alexbrandmeyer@643
|
150 car_coeffs->r1_coeffs.resize(num_channels_);
|
alexbrandmeyer@643
|
151 car_coeffs->a0_coeffs.resize(num_channels_);
|
alexbrandmeyer@643
|
152 car_coeffs->c0_coeffs.resize(num_channels_);
|
alexbrandmeyer@643
|
153 car_coeffs->h_coeffs.resize(num_channels_);
|
alexbrandmeyer@643
|
154 car_coeffs->g0_coeffs.resize(num_channels_);
|
alexbrandmeyer@640
|
155 FPType f = car_params.zero_ratio * car_params.zero_ratio - 1.0;
|
alexbrandmeyer@643
|
156 ArrayX theta = pole_freqs * ((2.0 * kPi) / sample_rate);
|
alexbrandmeyer@640
|
157 car_coeffs->c0_coeffs = theta.sin();
|
alexbrandmeyer@640
|
158 car_coeffs->a0_coeffs = theta.cos();
|
alexbrandmeyer@640
|
159 FPType ff = car_params.high_f_damping_compression;
|
alexbrandmeyer@643
|
160 ArrayX x = theta / kPi;
|
alexbrandmeyer@640
|
161 car_coeffs->zr_coeffs = kPi * (x - (ff * (x*x*x)));
|
alexbrandmeyer@640
|
162 FPType max_zeta = car_params.max_zeta;
|
alexbrandmeyer@640
|
163 FPType min_zeta = car_params.min_zeta;
|
alexbrandmeyer@640
|
164 car_coeffs->r1_coeffs = (1.0 - (car_coeffs->zr_coeffs * max_zeta));
|
alexbrandmeyer@643
|
165 ArrayX erb_freqs(num_channels_);
|
alexbrandmeyer@643
|
166 for (int channel = 0; channel < num_channels_; ++channel) {
|
alexbrandmeyer@643
|
167 erb_freqs(channel) = ERBHz(pole_freqs(channel), car_params.erb_break_freq,
|
alexbrandmeyer@640
|
168 car_params.erb_q);
|
alexbrandmeyer@636
|
169 }
|
alexbrandmeyer@643
|
170 ArrayX min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) -
|
alexbrandmeyer@636
|
171 min_zeta));
|
alexbrandmeyer@640
|
172 car_coeffs->zr_coeffs *= max_zeta - min_zetas;
|
alexbrandmeyer@640
|
173 car_coeffs->h_coeffs = car_coeffs->c0_coeffs * f;
|
alexbrandmeyer@643
|
174 ArrayX relative_undamping = ArrayX::Ones(num_channels_);
|
alexbrandmeyer@643
|
175 ArrayX r = car_coeffs->r1_coeffs + (car_coeffs->zr_coeffs *
|
alexbrandmeyer@636
|
176 relative_undamping);
|
alexbrandmeyer@640
|
177 car_coeffs->g0_coeffs = (1.0 - (2.0 * r * car_coeffs->a0_coeffs) + (r*r)) /
|
alexbrandmeyer@640
|
178 (1 - (2 * r * car_coeffs->a0_coeffs) +
|
alexbrandmeyer@640
|
179 (car_coeffs->h_coeffs * r * car_coeffs->c0_coeffs) + (r*r));
|
alexbrandmeyer@636
|
180 }
|
alexbrandmeyer@636
|
181
|
alexbrandmeyer@643
|
182 void CARFAC::DesignIHCCoeffs(const IHCParams& ihc_params,
|
alexbrandmeyer@643
|
183 const FPType sample_rate, IHCCoeffs* ihc_coeffs) {
|
alexbrandmeyer@640
|
184 if (ihc_params.just_half_wave_rectify) {
|
alexbrandmeyer@640
|
185 ihc_coeffs->just_half_wave_rectify = ihc_params.just_half_wave_rectify;
|
alexbrandmeyer@636
|
186 } else {
|
alexbrandmeyer@636
|
187 // This section calculates conductance values using two pre-defined scalars.
|
alexbrandmeyer@643
|
188 ArrayX x(1);
|
alexbrandmeyer@636
|
189 FPType conduct_at_10, conduct_at_0;
|
alexbrandmeyer@636
|
190 x(0) = 10.0;
|
alexbrandmeyer@636
|
191 x = CARFACDetect(x);
|
alexbrandmeyer@636
|
192 conduct_at_10 = x(0);
|
alexbrandmeyer@636
|
193 x(0) = 0.0;
|
alexbrandmeyer@636
|
194 x = CARFACDetect(x);
|
alexbrandmeyer@636
|
195 conduct_at_0 = x(0);
|
alexbrandmeyer@640
|
196 if (ihc_params.one_capacitor) {
|
ronw@646
|
197 FPType ro = 1 / conduct_at_10;
|
alexbrandmeyer@640
|
198 FPType c = ihc_params.tau1_out / ro;
|
alexbrandmeyer@640
|
199 FPType ri = ihc_params.tau1_in / c;
|
alexbrandmeyer@636
|
200 FPType saturation_output = 1 / ((2 * ro) + ri);
|
alexbrandmeyer@636
|
201 FPType r0 = 1 / conduct_at_0;
|
alexbrandmeyer@636
|
202 FPType current = 1 / (ri + r0);
|
alexbrandmeyer@640
|
203 ihc_coeffs->cap1_voltage = 1 - (current * ri);
|
alexbrandmeyer@640
|
204 ihc_coeffs->just_half_wave_rectify = false;
|
ronw@646
|
205 ihc_coeffs->lpf_coeff = 1 - exp(-1 / (ihc_params.tau_lpf * sample_rate));
|
alexbrandmeyer@643
|
206 ihc_coeffs->out1_rate = ro / (ihc_params.tau1_out * sample_rate);
|
alexbrandmeyer@643
|
207 ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * sample_rate);
|
alexbrandmeyer@640
|
208 ihc_coeffs->one_capacitor = ihc_params.one_capacitor;
|
alexbrandmeyer@640
|
209 ihc_coeffs->output_gain = 1 / (saturation_output - current);
|
alexbrandmeyer@640
|
210 ihc_coeffs->rest_output = current / (saturation_output - current);
|
alexbrandmeyer@640
|
211 ihc_coeffs->rest_cap1 = ihc_coeffs->cap1_voltage;
|
alexbrandmeyer@636
|
212 } else {
|
alexbrandmeyer@636
|
213 FPType ro = 1 / conduct_at_10;
|
alexbrandmeyer@640
|
214 FPType c2 = ihc_params.tau2_out / ro;
|
alexbrandmeyer@640
|
215 FPType r2 = ihc_params.tau2_in / c2;
|
alexbrandmeyer@640
|
216 FPType c1 = ihc_params.tau1_out / r2;
|
alexbrandmeyer@640
|
217 FPType r1 = ihc_params.tau1_in / c1;
|
alexbrandmeyer@636
|
218 FPType saturation_output = 1 / (2 * ro + r2 + r1);
|
alexbrandmeyer@636
|
219 FPType r0 = 1 / conduct_at_0;
|
alexbrandmeyer@636
|
220 FPType current = 1 / (r1 + r2 + r0);
|
alexbrandmeyer@640
|
221 ihc_coeffs->cap1_voltage = 1 - (current * r1);
|
alexbrandmeyer@640
|
222 ihc_coeffs->cap2_voltage = ihc_coeffs->cap1_voltage - (current * r2);
|
alexbrandmeyer@640
|
223 ihc_coeffs->just_half_wave_rectify = false;
|
alexbrandmeyer@643
|
224 ihc_coeffs->lpf_coeff = 1 - exp(-1 / (ihc_params.tau_lpf * sample_rate));
|
alexbrandmeyer@643
|
225 ihc_coeffs->out1_rate = 1 / (ihc_params.tau1_out * sample_rate);
|
alexbrandmeyer@643
|
226 ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * sample_rate);
|
alexbrandmeyer@643
|
227 ihc_coeffs->out2_rate = ro / (ihc_params.tau2_out * sample_rate);
|
alexbrandmeyer@643
|
228 ihc_coeffs->in2_rate = 1 / (ihc_params.tau2_in * sample_rate);
|
alexbrandmeyer@640
|
229 ihc_coeffs->one_capacitor = ihc_params.one_capacitor;
|
alexbrandmeyer@640
|
230 ihc_coeffs->output_gain = 1 / (saturation_output - current);
|
alexbrandmeyer@640
|
231 ihc_coeffs->rest_output = current / (saturation_output - current);
|
alexbrandmeyer@640
|
232 ihc_coeffs->rest_cap1 = ihc_coeffs->cap1_voltage;
|
alexbrandmeyer@640
|
233 ihc_coeffs->rest_cap2 = ihc_coeffs->cap2_voltage;
|
alexbrandmeyer@636
|
234 }
|
alexbrandmeyer@636
|
235 }
|
alexbrandmeyer@643
|
236 ihc_coeffs->ac_coeff = 2 * kPi * ihc_params.ac_corner_hz / sample_rate;
|
alexbrandmeyer@636
|
237 }
|
alexbrandmeyer@636
|
238
|
alexbrandmeyer@643
|
239 void CARFAC::DesignAGCCoeffs(const AGCParams& agc_params,
|
alexbrandmeyer@643
|
240 const FPType sample_rate,
|
alexbrandmeyer@636
|
241 vector<AGCCoeffs>* agc_coeffs) {
|
alexbrandmeyer@643
|
242 agc_coeffs->resize(agc_params.num_stages);
|
alexbrandmeyer@636
|
243 FPType previous_stage_gain = 0.0;
|
alexbrandmeyer@636
|
244 FPType decim = 1.0;
|
alexbrandmeyer@643
|
245 for (int stage = 0; stage < agc_params.num_stages; ++stage) {
|
alexbrandmeyer@636
|
246 AGCCoeffs& agc_coeff = agc_coeffs->at(stage);
|
alexbrandmeyer@643
|
247 agc_coeff.num_agc_stages = agc_params.num_stages;
|
alexbrandmeyer@640
|
248 agc_coeff.agc_stage_gain = agc_params.agc_stage_gain;
|
alexbrandmeyer@640
|
249 vector<FPType> agc1_scales = agc_params.agc1_scales;
|
alexbrandmeyer@640
|
250 vector<FPType> agc2_scales = agc_params.agc2_scales;
|
alexbrandmeyer@640
|
251 vector<FPType> time_constants = agc_params.time_constants;
|
alexbrandmeyer@640
|
252 FPType mix_coeff = agc_params.agc_mix_coeff;
|
alexbrandmeyer@640
|
253 agc_coeff.decimation = agc_params.decimation[stage];
|
alexbrandmeyer@636
|
254 FPType total_dc_gain = previous_stage_gain;
|
ronw@646
|
255 // Calculate the parameters for the current stage.
|
alexbrandmeyer@636
|
256 FPType tau = time_constants[stage];
|
alexbrandmeyer@640
|
257 agc_coeff.decim = decim;
|
alexbrandmeyer@640
|
258 agc_coeff.decim *= agc_coeff.decimation;
|
alexbrandmeyer@643
|
259 agc_coeff.agc_epsilon = 1 - exp((-1 * agc_coeff.decim) /
|
alexbrandmeyer@643
|
260 (tau * sample_rate));
|
alexbrandmeyer@643
|
261 FPType n_times = tau * (sample_rate / agc_coeff.decim);
|
alexbrandmeyer@636
|
262 FPType delay = (agc2_scales[stage] - agc1_scales[stage]) / n_times;
|
alexbrandmeyer@636
|
263 FPType spread_sq = (pow(agc1_scales[stage], 2) +
|
alexbrandmeyer@636
|
264 pow(agc2_scales[stage], 2)) / n_times;
|
alexbrandmeyer@636
|
265 FPType u = 1 + (1 / spread_sq);
|
alexbrandmeyer@636
|
266 FPType p = u - sqrt(pow(u, 2) - 1);
|
alexbrandmeyer@636
|
267 FPType dp = delay * (1 - (2 * p) + (p*p)) / 2;
|
alexbrandmeyer@640
|
268 agc_coeff.agc_pole_z1 = p - dp;
|
alexbrandmeyer@640
|
269 agc_coeff.agc_pole_z2 = p + dp;
|
alexbrandmeyer@636
|
270 int n_taps = 0;
|
alexbrandmeyer@636
|
271 bool fir_ok = false;
|
alexbrandmeyer@636
|
272 int n_iterations = 1;
|
ronw@646
|
273 // Initialize the FIR coefficient settings at each stage.
|
alexbrandmeyer@636
|
274 FPType fir_left, fir_mid, fir_right;
|
ronw@646
|
275 while (!fir_ok) {
|
alexbrandmeyer@636
|
276 switch (n_taps) {
|
alexbrandmeyer@636
|
277 case 0:
|
alexbrandmeyer@636
|
278 n_taps = 3;
|
alexbrandmeyer@636
|
279 break;
|
alexbrandmeyer@636
|
280 case 3:
|
alexbrandmeyer@636
|
281 n_taps = 5;
|
alexbrandmeyer@636
|
282 break;
|
alexbrandmeyer@636
|
283 case 5:
|
alexbrandmeyer@636
|
284 n_iterations++;
|
alexbrandmeyer@636
|
285 assert(n_iterations < 16 &&
|
alexbrandmeyer@636
|
286 "Too many iterations needed in AGC spatial smoothing.");
|
alexbrandmeyer@636
|
287 break;
|
alexbrandmeyer@636
|
288 default:
|
alexbrandmeyer@636
|
289 assert(true && "Bad n_taps; should be 3 or 5.");
|
alexbrandmeyer@636
|
290 break;
|
alexbrandmeyer@636
|
291 }
|
alexbrandmeyer@636
|
292 // The smoothing function is a space-domain smoothing, but it considered
|
alexbrandmeyer@636
|
293 // here by analogy to time-domain smoothing, which is why its potential
|
alexbrandmeyer@636
|
294 // off-centeredness is called a delay. Since it's a smoothing filter, it
|
alexbrandmeyer@636
|
295 // is also analogous to a discrete probability distribution (a p.m.f.),
|
alexbrandmeyer@636
|
296 // with mean corresponding to delay and variance corresponding to squared
|
alexbrandmeyer@636
|
297 // spatial spread (in samples, or channels, and the square thereof,
|
alexbrandmeyer@636
|
298 // respecitively). Here we design a filter implementation's coefficient
|
alexbrandmeyer@636
|
299 // via the method of moment matching, so we get the intended delay and
|
alexbrandmeyer@636
|
300 // spread, and don't worry too much about the shape of the distribution,
|
alexbrandmeyer@636
|
301 // which will be some kind of blob not too far from Gaussian if we run
|
alexbrandmeyer@636
|
302 // several FIR iterations.
|
alexbrandmeyer@636
|
303 FPType delay_variance = spread_sq / n_iterations;
|
alexbrandmeyer@636
|
304 FPType mean_delay = delay / n_iterations;
|
alexbrandmeyer@636
|
305 FPType a, b;
|
alexbrandmeyer@636
|
306 switch (n_taps) {
|
alexbrandmeyer@636
|
307 case 3:
|
alexbrandmeyer@636
|
308 a = (delay_variance + (mean_delay*mean_delay) - mean_delay) / 2.0;
|
alexbrandmeyer@636
|
309 b = (delay_variance + (mean_delay*mean_delay) + mean_delay) / 2.0;
|
alexbrandmeyer@636
|
310 fir_left = a;
|
alexbrandmeyer@636
|
311 fir_mid = 1 - a - b;
|
alexbrandmeyer@636
|
312 fir_right = b;
|
alexbrandmeyer@636
|
313 fir_ok = fir_mid >= 0.2 ? true : false;
|
alexbrandmeyer@636
|
314 break;
|
alexbrandmeyer@636
|
315 case 5:
|
alexbrandmeyer@636
|
316 a = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) -
|
alexbrandmeyer@636
|
317 (mean_delay * 2.0/3.0)) / 2.0;
|
alexbrandmeyer@636
|
318 b = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) +
|
alexbrandmeyer@636
|
319 (mean_delay * 2.0/3.0)) / 2.0;
|
alexbrandmeyer@636
|
320 fir_left = a / 2.0;
|
alexbrandmeyer@636
|
321 fir_mid = 1 - a - b;
|
alexbrandmeyer@636
|
322 fir_right = b / 2.0;
|
alexbrandmeyer@636
|
323 fir_ok = fir_mid >= 0.1 ? true : false;
|
alexbrandmeyer@636
|
324 break;
|
alexbrandmeyer@636
|
325 default:
|
alexbrandmeyer@636
|
326 assert(true && "Bad n_taps; should be 3 or 5.");
|
alexbrandmeyer@636
|
327 break;
|
alexbrandmeyer@636
|
328 }
|
alexbrandmeyer@636
|
329 }
|
alexbrandmeyer@636
|
330 // Once we have the FIR design for this stage we can assign it to the
|
alexbrandmeyer@636
|
331 // appropriate data members.
|
alexbrandmeyer@640
|
332 agc_coeff.agc_spatial_iterations = n_iterations;
|
alexbrandmeyer@640
|
333 agc_coeff.agc_spatial_n_taps = n_taps;
|
alexbrandmeyer@640
|
334 agc_coeff.agc_spatial_fir_left = fir_left;
|
alexbrandmeyer@640
|
335 agc_coeff.agc_spatial_fir_mid = fir_mid;
|
alexbrandmeyer@640
|
336 agc_coeff.agc_spatial_fir_right = fir_right;
|
alexbrandmeyer@640
|
337 total_dc_gain += pow(agc_coeff.agc_stage_gain, stage);
|
alexbrandmeyer@640
|
338 agc_coeff.agc_mix_coeffs = stage == 0 ? 0 : mix_coeff /
|
alexbrandmeyer@643
|
339 (tau * (sample_rate / agc_coeff.decim));
|
alexbrandmeyer@640
|
340 agc_coeff.agc_gain = total_dc_gain;
|
alexbrandmeyer@640
|
341 agc_coeff.detect_scale = 1 / total_dc_gain;
|
alexbrandmeyer@640
|
342 previous_stage_gain = agc_coeff.agc_gain;
|
alexbrandmeyer@640
|
343 decim = agc_coeff.decim;
|
alexbrandmeyer@636
|
344 }
|
ronw@641
|
345 }
|
alexbrandmeyer@643
|
346
|
ronw@644
|
347 FPType CARFAC::ERBHz(const FPType center_frequency_hz,
|
ronw@646
|
348 const FPType erb_break_freq, const FPType erb_q) const {
|
alexbrandmeyer@643
|
349 return (erb_break_freq + center_frequency_hz) / erb_q;
|
ronw@644
|
350 }
|