alexbrandmeyer@626
|
1 //
|
alexbrandmeyer@626
|
2 // agc_coeffs.cc
|
alexbrandmeyer@626
|
3 // CARFAC Open Source C++ Library
|
alexbrandmeyer@626
|
4 //
|
alexbrandmeyer@626
|
5 // Created by Alex Brandmeyer on 5/18/13.
|
alexbrandmeyer@626
|
6 //
|
alexbrandmeyer@626
|
7 // This C++ file is part of an implementation of Lyon's cochlear model:
|
alexbrandmeyer@626
|
8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
alexbrandmeyer@626
|
9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
|
alexbrandmeyer@626
|
10 //
|
alexbrandmeyer@626
|
11 // Licensed under the Apache License, Version 2.0 (the "License");
|
alexbrandmeyer@626
|
12 // you may not use this file except in compliance with the License.
|
alexbrandmeyer@626
|
13 // You may obtain a copy of the License at
|
alexbrandmeyer@626
|
14 //
|
alexbrandmeyer@626
|
15 // http://www.apache.org/licenses/LICENSE-2.0
|
alexbrandmeyer@626
|
16 //
|
alexbrandmeyer@626
|
17 // Unless required by applicable law or agreed to in writing, software
|
alexbrandmeyer@626
|
18 // distributed under the License is distributed on an "AS IS" BASIS,
|
alexbrandmeyer@626
|
19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
alexbrandmeyer@626
|
20 // See the License for the specific language governing permissions and
|
alexbrandmeyer@626
|
21 // limitations under the License.
|
alexbrandmeyer@626
|
22
|
ronw@628
|
23 #include <assert.h>
|
ronw@628
|
24
|
alexbrandmeyer@626
|
25 #include "agc_coeffs.h"
|
alexbrandmeyer@626
|
26
|
alexbrandmeyer@626
|
27 void AGCCoeffs::Design(const AGCParams& agc_params, const int stage,
|
alexbrandmeyer@626
|
28 const FPType fs, const FPType previous_stage_gain,
|
alexbrandmeyer@626
|
29 FPType decim) {
|
alexbrandmeyer@626
|
30 n_agc_stages_ = agc_params.n_stages_;
|
alexbrandmeyer@626
|
31 agc_stage_gain_ = agc_params.agc_stage_gain_;
|
alexbrandmeyer@626
|
32 std::vector<FPType> agc1_scales = agc_params.agc1_scales_;
|
alexbrandmeyer@626
|
33 std::vector<FPType> agc2_scales = agc_params.agc2_scales_;
|
alexbrandmeyer@626
|
34 std::vector<FPType> time_constants = agc_params.time_constants_;
|
alexbrandmeyer@626
|
35 FPType mix_coeff = agc_params.agc_mix_coeff_;
|
alexbrandmeyer@626
|
36 decimation_ = agc_params.decimation_[stage];
|
alexbrandmeyer@626
|
37 FPType total_dc_gain = previous_stage_gain;
|
alexbrandmeyer@626
|
38 // Here we calculate the parameters for the current stage.
|
alexbrandmeyer@626
|
39 FPType tau = time_constants[stage];
|
alexbrandmeyer@626
|
40 decim_ = decim;
|
alexbrandmeyer@626
|
41 decim_ *= decimation_;
|
alexbrandmeyer@626
|
42 agc_epsilon_ = 1 - exp((-1 * decim_) / (tau * fs));
|
alexbrandmeyer@626
|
43 FPType n_times = tau * (fs / decim_);
|
alexbrandmeyer@626
|
44 FPType delay = (agc2_scales[stage] - agc1_scales[stage]) / n_times;
|
alexbrandmeyer@626
|
45 FPType spread_sq = (pow(agc1_scales[stage], 2) +
|
alexbrandmeyer@626
|
46 pow(agc2_scales[stage], 2)) / n_times;
|
alexbrandmeyer@626
|
47 FPType u = 1 + (1 / spread_sq);
|
alexbrandmeyer@626
|
48 FPType p = u - sqrt(pow(u, 2) - 1);
|
alexbrandmeyer@626
|
49 FPType dp = delay * (1 - (2 * p) + (p * p)) / 2;
|
alexbrandmeyer@626
|
50 agc_pole_z1_ = p - dp;
|
alexbrandmeyer@626
|
51 agc_pole_z2_ = p + dp;
|
alexbrandmeyer@626
|
52 int n_taps = 0;
|
alexbrandmeyer@626
|
53 bool fir_ok = false;
|
alexbrandmeyer@626
|
54 int n_iterations = 1;
|
alexbrandmeyer@626
|
55 // This section initializes the FIR coeffs settings at each stage.
|
alexbrandmeyer@626
|
56 agc_spatial_fir_.resize(3);
|
alexbrandmeyer@626
|
57 std::vector<FPType> fir(3);
|
alexbrandmeyer@626
|
58 while (! fir_ok) {
|
alexbrandmeyer@626
|
59 switch (n_taps) {
|
alexbrandmeyer@626
|
60 case 0:
|
alexbrandmeyer@626
|
61 n_taps = 3;
|
alexbrandmeyer@626
|
62 break;
|
alexbrandmeyer@626
|
63 case 3:
|
alexbrandmeyer@626
|
64 n_taps = 5;
|
alexbrandmeyer@626
|
65 break;
|
alexbrandmeyer@626
|
66 case 5:
|
ronw@628
|
67 n_iterations++;
|
ronw@628
|
68 assert(n_iterations < 16 &&
|
ronw@628
|
69 "Too many iterations needed in AGC spatial smoothing.");
|
alexbrandmeyer@626
|
70 break;
|
alexbrandmeyer@626
|
71 default:
|
ronw@628
|
72 assert(true && "Bad n_taps; should be 3 or 5.");
|
alexbrandmeyer@626
|
73 break;
|
alexbrandmeyer@626
|
74 }
|
alexbrandmeyer@626
|
75 // The smoothing function is a space-domain smoothing, but it considered
|
alexbrandmeyer@626
|
76 // here by analogy to time-domain smoothing, which is why its potential
|
alexbrandmeyer@626
|
77 // off-centeredness is called a delay. Since it's a smoothing filter, it
|
alexbrandmeyer@626
|
78 // is also analogous to a discrete probability distribution (a p.m.f.),
|
alexbrandmeyer@626
|
79 // with mean corresponding to delay and variance corresponding to squared
|
alexbrandmeyer@626
|
80 // spatial spread (in samples, or channels, and the square thereof,
|
alexbrandmeyer@626
|
81 // respecitively). Here we design a filter implementation's coefficient
|
alexbrandmeyer@626
|
82 // via the method of moment matching, so we get the intended delay and
|
alexbrandmeyer@626
|
83 // spread, and don't worry too much about the shape of the distribution,
|
alexbrandmeyer@626
|
84 // which will be some kind of blob not too far from Gaussian if we run
|
alexbrandmeyer@626
|
85 // several FIR iterations.
|
alexbrandmeyer@626
|
86 FPType delay_variance = spread_sq / n_iterations;
|
alexbrandmeyer@626
|
87 FPType mean_delay = delay / n_iterations;
|
alexbrandmeyer@626
|
88 FPType a, b;
|
alexbrandmeyer@626
|
89 switch (n_taps) {
|
alexbrandmeyer@626
|
90 case 3:
|
alexbrandmeyer@626
|
91 a = (delay_variance + (mean_delay*mean_delay) - mean_delay) / 2.0;
|
alexbrandmeyer@626
|
92 b = (delay_variance + (mean_delay*mean_delay) + mean_delay) / 2.0;
|
alexbrandmeyer@626
|
93 fir[0] = a;
|
alexbrandmeyer@626
|
94 fir[1] = 1 - a - b;
|
alexbrandmeyer@626
|
95 fir[2] = b;
|
alexbrandmeyer@626
|
96 fir_ok = fir[1] >= 0.2 ? true : false;
|
alexbrandmeyer@626
|
97 break;
|
alexbrandmeyer@626
|
98 case 5:
|
alexbrandmeyer@626
|
99 a = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) -
|
alexbrandmeyer@626
|
100 (mean_delay * 2.0/3.0)) / 2.0;
|
alexbrandmeyer@626
|
101 b = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) +
|
alexbrandmeyer@626
|
102 (mean_delay * 2.0/3.0)) / 2.0;
|
alexbrandmeyer@626
|
103 fir[0] = a / 2.0;
|
alexbrandmeyer@626
|
104 fir[1] = 1 - a - b;
|
alexbrandmeyer@626
|
105 fir[2] = b / 2.0;
|
alexbrandmeyer@626
|
106 fir_ok = fir[1] >= 0.1 ? true : false;
|
alexbrandmeyer@626
|
107 break;
|
alexbrandmeyer@626
|
108 default:
|
ronw@630
|
109 assert(true && "Bad n_taps; should be 3 or 5.");
|
ronw@630
|
110 break;
|
alexbrandmeyer@626
|
111 }
|
alexbrandmeyer@626
|
112 }
|
alexbrandmeyer@626
|
113 // Once we have the FIR design for this stage we can assign it to the
|
alexbrandmeyer@626
|
114 // appropriate data members.
|
alexbrandmeyer@626
|
115 agc_spatial_iterations_ = n_iterations;
|
alexbrandmeyer@626
|
116 agc_spatial_n_taps_ = n_taps;
|
alexbrandmeyer@626
|
117 agc_spatial_fir_[0] = fir[0];
|
alexbrandmeyer@626
|
118 agc_spatial_fir_[1] = fir[1];
|
alexbrandmeyer@626
|
119 agc_spatial_fir_[2] = fir[2];
|
alexbrandmeyer@626
|
120 total_dc_gain += pow(agc_stage_gain_, stage);
|
alexbrandmeyer@626
|
121 agc_mix_coeffs_ = stage == 0 ? 0 : mix_coeff / (tau * (fs /decim_));
|
alexbrandmeyer@626
|
122 agc_gain_ = total_dc_gain;
|
alexbrandmeyer@626
|
123 detect_scale_ = 1 / total_dc_gain;
|
ronw@628
|
124 }
|