alexbrandmeyer@668
|
1 //
|
alexbrandmeyer@668
|
2 // carfac_test.cc
|
alexbrandmeyer@668
|
3 // CARFAC Open Source C++ Library
|
alexbrandmeyer@668
|
4 //
|
alexbrandmeyer@668
|
5 // Created by Alex Brandmeyer on 5/22/13.
|
alexbrandmeyer@668
|
6 //
|
alexbrandmeyer@668
|
7 // This C++ file is part of an implementation of Lyon's cochlear model:
|
alexbrandmeyer@668
|
8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
alexbrandmeyer@668
|
9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
|
alexbrandmeyer@668
|
10 //
|
alexbrandmeyer@668
|
11 // Licensed under the Apache License, Version 2.0 (the "License");
|
alexbrandmeyer@668
|
12 // you may not use this file except in compliance with the License.
|
alexbrandmeyer@668
|
13 // You may obtain a copy of the License at
|
alexbrandmeyer@668
|
14 //
|
alexbrandmeyer@668
|
15 // http://www.apache.org/licenses/LICENSE-2.0
|
alexbrandmeyer@668
|
16 //
|
alexbrandmeyer@668
|
17 // Unless required by applicable law or agreed to in writing, software
|
alexbrandmeyer@668
|
18 // distributed under the License is distributed on an "AS IS" BASIS,
|
alexbrandmeyer@668
|
19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
alexbrandmeyer@668
|
20 // See the License for the specific language governing permissions and
|
alexbrandmeyer@668
|
21 // limitations under the License.
|
alexbrandmeyer@668
|
22
|
alexbrandmeyer@668
|
23 #include "carfac_test.h"
|
alexbrandmeyer@668
|
24 // Three helper functions are defined here for loading the test data generated
|
alexbrandmeyer@668
|
25 // by the Matlab version of CARFAC.
|
alexbrandmeyer@668
|
26 // This loads one-dimensional FloatArrays from single-column text files.
|
alexbrandmeyer@668
|
27 FloatArray LoadTestData(const std::string filename, const int number_points) {
|
alexbrandmeyer@668
|
28 std::string fullfile = TEST_SRC_DIR + filename;
|
alexbrandmeyer@668
|
29 std::ifstream file(fullfile.c_str());
|
alexbrandmeyer@668
|
30 FPType myarray[number_points];
|
alexbrandmeyer@668
|
31 FloatArray output(number_points);
|
alexbrandmeyer@668
|
32 if (file.is_open()) {
|
alexbrandmeyer@668
|
33 for (int i = 0; i < number_points; ++i) {
|
alexbrandmeyer@668
|
34 file >> myarray[i];
|
alexbrandmeyer@668
|
35 output(i) = myarray[i];
|
alexbrandmeyer@668
|
36 }
|
alexbrandmeyer@668
|
37 }
|
alexbrandmeyer@668
|
38 return output;
|
alexbrandmeyer@668
|
39 }
|
alexbrandmeyer@668
|
40
|
alexbrandmeyer@668
|
41 // This loads two-dimensional FloatArrays from multi-column text files.
|
alexbrandmeyer@668
|
42 std::vector<FloatArray> Load2dTestData(const std::string filename, const int rows,
|
alexbrandmeyer@668
|
43 const int columns) {
|
alexbrandmeyer@668
|
44 std::string fullfile = TEST_SRC_DIR + filename;
|
alexbrandmeyer@668
|
45 std::ifstream file(fullfile.c_str());
|
alexbrandmeyer@668
|
46 FPType myarray[rows][columns];
|
alexbrandmeyer@668
|
47 std::vector<FloatArray> output;
|
alexbrandmeyer@668
|
48 output.resize(rows);
|
alexbrandmeyer@668
|
49 for (auto& timepoint : output) {
|
alexbrandmeyer@668
|
50 timepoint.resize(columns);
|
alexbrandmeyer@668
|
51 }
|
alexbrandmeyer@668
|
52 if (file.is_open()) {
|
alexbrandmeyer@668
|
53 for (int i = 0; i < rows; ++i) {
|
alexbrandmeyer@668
|
54 for (int j = 0; j < columns; ++j) {
|
alexbrandmeyer@668
|
55 file >> myarray[i][j];
|
alexbrandmeyer@668
|
56 output[i](j) = myarray[i][j];
|
alexbrandmeyer@668
|
57 }
|
alexbrandmeyer@668
|
58 }
|
alexbrandmeyer@668
|
59 }
|
alexbrandmeyer@668
|
60 return output;
|
alexbrandmeyer@668
|
61 }
|
alexbrandmeyer@668
|
62
|
alexbrandmeyer@668
|
63 // This loads two dimensional vectors of audio data using data generated in
|
alexbrandmeyer@668
|
64 // Matlab using the wavread() function.
|
alexbrandmeyer@668
|
65 std::vector<std::vector<float>> Load2dAudioVector(std::string filename,
|
alexbrandmeyer@668
|
66 int timepoints,
|
alexbrandmeyer@668
|
67 int channels) {
|
alexbrandmeyer@668
|
68 std::string fullfile = TEST_SRC_DIR + filename;
|
alexbrandmeyer@668
|
69 std::ifstream file(fullfile.c_str());
|
alexbrandmeyer@668
|
70 std::vector<std::vector<float>> output;
|
alexbrandmeyer@668
|
71 output.resize(channels);
|
alexbrandmeyer@668
|
72 for (auto& channel : output) {
|
alexbrandmeyer@668
|
73 channel.resize(timepoints);
|
alexbrandmeyer@668
|
74 }
|
alexbrandmeyer@668
|
75 if (file.is_open()) {
|
alexbrandmeyer@668
|
76 for (int i = 0; i < timepoints; ++i) {
|
alexbrandmeyer@668
|
77 for (int j = 0; j < channels; ++j) {
|
alexbrandmeyer@668
|
78 file >> output[j][i];
|
alexbrandmeyer@668
|
79 }
|
alexbrandmeyer@668
|
80 }
|
alexbrandmeyer@668
|
81 }
|
alexbrandmeyer@668
|
82 return output;
|
alexbrandmeyer@668
|
83 }
|
alexbrandmeyer@668
|
84
|
alexbrandmeyer@668
|
85 // The first test verifies that the resulting CAR coefficients are the same as
|
alexbrandmeyer@668
|
86 // in Matlab when using the default CAR parameter set.
|
alexbrandmeyer@668
|
87 TEST(CARFACTest, CARCoeffs_Test){
|
alexbrandmeyer@668
|
88 // These initialze the CAR Params and Coeffs objects needed for this test.
|
alexbrandmeyer@668
|
89 CARParams car_params;
|
alexbrandmeyer@668
|
90 CARCoeffs car_coeffs;
|
alexbrandmeyer@668
|
91 FPType fs = 22050.0;
|
alexbrandmeyer@668
|
92 // We calculate the pole frequencies and number of channels in the same way
|
alexbrandmeyer@668
|
93 // as in the CARFAC 'Design' method.
|
alexbrandmeyer@668
|
94 int n_ch = 0;
|
alexbrandmeyer@668
|
95 FPType pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
|
alexbrandmeyer@668
|
96 while (pole_hz > car_params.min_pole_hz_) {
|
alexbrandmeyer@668
|
97 n_ch++;
|
alexbrandmeyer@668
|
98 pole_hz = pole_hz - car_params.erb_per_step_ *
|
alexbrandmeyer@668
|
99 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
|
alexbrandmeyer@668
|
100 }
|
alexbrandmeyer@668
|
101 FloatArray pole_freqs(n_ch);
|
alexbrandmeyer@668
|
102 pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
|
alexbrandmeyer@668
|
103 for (int ch = 0; ch < n_ch; ++ch) {
|
alexbrandmeyer@668
|
104 pole_freqs(ch) = pole_hz;
|
alexbrandmeyer@668
|
105 pole_hz = pole_hz - car_params.erb_per_step_ *
|
alexbrandmeyer@668
|
106 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
|
alexbrandmeyer@668
|
107 }
|
alexbrandmeyer@668
|
108 // This initializes the CAR coeffecients object and runs the design method.
|
alexbrandmeyer@668
|
109 car_coeffs.Design(car_params, 22050, pole_freqs);
|
alexbrandmeyer@668
|
110 // Now we go through each set of coefficients to verify that the values are
|
alexbrandmeyer@668
|
111 // the same as in MATLAB.
|
alexbrandmeyer@668
|
112 std::string filename;
|
alexbrandmeyer@668
|
113 FloatArray output;
|
alexbrandmeyer@668
|
114
|
alexbrandmeyer@668
|
115 ASSERT_EQ(car_coeffs.v_offset_, 0.04);
|
alexbrandmeyer@668
|
116 ASSERT_EQ(car_coeffs.velocity_scale_, 0.1);
|
alexbrandmeyer@668
|
117
|
alexbrandmeyer@668
|
118 filename = "r1_coeffs.txt";
|
alexbrandmeyer@668
|
119 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
120 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
121 ASSERT_NEAR(output(i), car_coeffs.r1_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
122 }
|
alexbrandmeyer@668
|
123
|
alexbrandmeyer@668
|
124 filename = "a0_coeffs.txt";
|
alexbrandmeyer@668
|
125 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
126 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
127 ASSERT_NEAR(output(i), car_coeffs.a0_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
128 }
|
alexbrandmeyer@668
|
129
|
alexbrandmeyer@668
|
130 filename = "c0_coeffs.txt";
|
alexbrandmeyer@668
|
131 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
132 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
133 ASSERT_NEAR(output(i), car_coeffs.c0_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
134 }
|
alexbrandmeyer@668
|
135
|
alexbrandmeyer@668
|
136 filename = "zr_coeffs.txt";
|
alexbrandmeyer@668
|
137 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
138 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
139 ASSERT_NEAR(output(i), car_coeffs.zr_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
140 }
|
alexbrandmeyer@668
|
141
|
alexbrandmeyer@668
|
142 filename = "h_coeffs.txt";
|
alexbrandmeyer@668
|
143 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
144 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
145 ASSERT_NEAR(output(i), car_coeffs.h_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
146 }
|
alexbrandmeyer@668
|
147
|
alexbrandmeyer@668
|
148 filename = "g0_coeffs.txt";
|
alexbrandmeyer@668
|
149 output = LoadTestData(filename, n_ch);
|
alexbrandmeyer@668
|
150 for (int i = 0; i < n_ch; ++i) {
|
alexbrandmeyer@668
|
151 ASSERT_NEAR(output(i), car_coeffs.g0_coeffs_(i), PRECISION_LEVEL);
|
alexbrandmeyer@668
|
152 }
|
alexbrandmeyer@668
|
153 }
|
alexbrandmeyer@668
|
154
|
alexbrandmeyer@668
|
155 // The second test verifies that the IHC coefficient calculations result in the
|
alexbrandmeyer@668
|
156 // same set of values as in the Matlab version of the CARFAC.
|
alexbrandmeyer@668
|
157 TEST(CARFACTest, IHCCoeffs_Test){
|
alexbrandmeyer@668
|
158 IHCParams ihc_params;
|
alexbrandmeyer@668
|
159 IHCCoeffs ihc_coeffs;
|
alexbrandmeyer@668
|
160 FPType fs = 22050.0;
|
alexbrandmeyer@668
|
161 ihc_coeffs.Design(ihc_params, fs);
|
alexbrandmeyer@668
|
162
|
alexbrandmeyer@668
|
163 std::string filename = "ihc_coeffs.txt";
|
alexbrandmeyer@668
|
164 FloatArray output = LoadTestData(filename, 9);
|
alexbrandmeyer@668
|
165
|
alexbrandmeyer@668
|
166 // The sequence of the individual coefficients is determined using the
|
alexbrandmeyer@668
|
167 // CARFAC_GenerateTestData() function in the Matlab version, with all of the
|
alexbrandmeyer@668
|
168 // parameters placed in a single output file for convenience.
|
alexbrandmeyer@668
|
169 bool just_hwr = output(0);
|
alexbrandmeyer@668
|
170 FPType lpf_coeff = output(1);
|
alexbrandmeyer@668
|
171 FPType out_rate = output(2);
|
alexbrandmeyer@668
|
172 FPType in_rate = output(3);
|
alexbrandmeyer@668
|
173 bool one_cap = output(4);
|
alexbrandmeyer@668
|
174 FPType output_gain = output(5);
|
alexbrandmeyer@668
|
175 FPType rest_output = output(6);
|
alexbrandmeyer@668
|
176 FPType rest_cap = output(7);
|
alexbrandmeyer@668
|
177 FPType ac_coeff = output(8);
|
alexbrandmeyer@668
|
178
|
alexbrandmeyer@668
|
179 // Once we have the Matlab values initialized, we can compare them to the
|
alexbrandmeyer@668
|
180 // output of the IHCCoeffs 'Design' method.
|
alexbrandmeyer@668
|
181 ASSERT_EQ(just_hwr, ihc_coeffs.just_hwr_);
|
alexbrandmeyer@668
|
182 ASSERT_NEAR(lpf_coeff, ihc_coeffs.lpf_coeff_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
183 ASSERT_NEAR(out_rate, ihc_coeffs.out1_rate_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
184 ASSERT_NEAR(in_rate, ihc_coeffs.in1_rate_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
185 ASSERT_EQ(one_cap, ihc_coeffs.one_cap_);
|
alexbrandmeyer@668
|
186 ASSERT_NEAR(output_gain, ihc_coeffs.output_gain_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
187 ASSERT_NEAR(rest_output, ihc_coeffs.rest_output_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
188 ASSERT_NEAR(rest_cap, ihc_coeffs.rest_cap1_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
189 ASSERT_NEAR(ac_coeff, ihc_coeffs.ac_coeff_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
190 }
|
alexbrandmeyer@668
|
191
|
alexbrandmeyer@668
|
192
|
alexbrandmeyer@668
|
193 TEST(CARFACTest, AGCCoeffs_Test) {
|
alexbrandmeyer@668
|
194 AGCParams agc_params;
|
alexbrandmeyer@668
|
195 std::vector<AGCCoeffs> agc_coeffs;
|
alexbrandmeyer@668
|
196 std::vector<FloatArray> output;
|
alexbrandmeyer@668
|
197 output.resize(agc_params.n_stages_);
|
alexbrandmeyer@668
|
198 std::string filename = "agc_coeffs_1.txt";
|
alexbrandmeyer@668
|
199 output[0] = LoadTestData(filename, 14);
|
alexbrandmeyer@668
|
200 filename = "agc_coeffs_2.txt";
|
alexbrandmeyer@668
|
201 output[1] = LoadTestData(filename, 14);
|
alexbrandmeyer@668
|
202 filename = "agc_coeffs_3.txt";
|
alexbrandmeyer@668
|
203 output[2] = LoadTestData(filename, 14);
|
alexbrandmeyer@668
|
204 filename = "agc_coeffs_4.txt";
|
alexbrandmeyer@668
|
205 output[3] = LoadTestData(filename, 14);
|
alexbrandmeyer@668
|
206 agc_coeffs.resize(agc_params.n_stages_);
|
alexbrandmeyer@668
|
207 // We initialize the AGC stages in the same was as in Ear::Init.
|
alexbrandmeyer@668
|
208 FPType fs = 22050.0;
|
alexbrandmeyer@668
|
209 FPType previous_stage_gain = 0.0;
|
alexbrandmeyer@668
|
210 FPType decim = 1.0;
|
alexbrandmeyer@668
|
211 for (int stage = 0; stage < agc_params.n_stages_; ++stage) {
|
alexbrandmeyer@668
|
212 agc_coeffs[stage].Design(agc_params, stage, fs, previous_stage_gain, decim);
|
alexbrandmeyer@668
|
213 previous_stage_gain = agc_coeffs[stage].agc_gain_;
|
alexbrandmeyer@668
|
214 decim = agc_coeffs[stage].decim_;
|
alexbrandmeyer@668
|
215 }
|
alexbrandmeyer@668
|
216 // Now we run through the individual coefficients and verify that they're the
|
alexbrandmeyer@668
|
217 // same as in Matlab.
|
alexbrandmeyer@668
|
218 for (int stage = 0; stage < agc_params.n_stages_; ++stage) {
|
alexbrandmeyer@668
|
219 int n_agc_stages = output[stage](1);
|
alexbrandmeyer@668
|
220 FPType agc_stage_gain = output[stage](2);
|
alexbrandmeyer@668
|
221 int decimation = output[stage](3);
|
alexbrandmeyer@668
|
222 FPType agc_epsilon = output[stage](4);
|
alexbrandmeyer@668
|
223 FPType agc_polez1 = output[stage](5);
|
alexbrandmeyer@668
|
224 FPType agc_polez2 = output[stage](6);
|
alexbrandmeyer@668
|
225 int agc_spatial_iterations = output[stage](7);
|
alexbrandmeyer@668
|
226 FPType agc_spatial_fir_1 = output[stage](8);
|
alexbrandmeyer@668
|
227 FPType agc_spatial_fir_2 = output[stage](9);
|
alexbrandmeyer@668
|
228 FPType agc_spatial_fir_3 = output[stage](10);
|
alexbrandmeyer@668
|
229 int agc_spatial_n_taps = output[stage](11);
|
alexbrandmeyer@668
|
230 FPType agc_mix_coeffs = output[stage](12);
|
alexbrandmeyer@668
|
231 FPType detect_scale = output[stage](13);
|
alexbrandmeyer@668
|
232
|
alexbrandmeyer@668
|
233 ASSERT_EQ(n_agc_stages, agc_coeffs[stage].n_agc_stages_);
|
alexbrandmeyer@668
|
234 ASSERT_NEAR(agc_stage_gain, agc_coeffs[stage].agc_stage_gain_,
|
alexbrandmeyer@668
|
235 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
236 ASSERT_EQ(decimation, agc_coeffs[stage].decimation_);
|
alexbrandmeyer@668
|
237 ASSERT_NEAR(agc_epsilon, agc_coeffs[stage].agc_epsilon_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
238 ASSERT_NEAR(agc_polez1, agc_coeffs[stage].agc_pole_z1_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
239 ASSERT_NEAR(agc_polez2, agc_coeffs[stage].agc_pole_z2_, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
240 ASSERT_EQ(agc_spatial_iterations,
|
alexbrandmeyer@668
|
241 agc_coeffs[stage].agc_spatial_iterations_);
|
alexbrandmeyer@668
|
242 ASSERT_NEAR(agc_spatial_fir_1, agc_coeffs[stage].agc_spatial_fir_[0],
|
alexbrandmeyer@668
|
243 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
244 ASSERT_NEAR(agc_spatial_fir_2, agc_coeffs[stage].agc_spatial_fir_[1],
|
alexbrandmeyer@668
|
245 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
246 ASSERT_EQ(agc_spatial_n_taps,
|
alexbrandmeyer@668
|
247 agc_coeffs[stage].agc_spatial_n_taps_);
|
alexbrandmeyer@668
|
248 ASSERT_NEAR(agc_spatial_fir_3, agc_coeffs[stage].agc_spatial_fir_[2],
|
alexbrandmeyer@668
|
249 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
250 ASSERT_NEAR(agc_mix_coeffs, agc_coeffs[stage].agc_mix_coeffs_,
|
alexbrandmeyer@668
|
251 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
252
|
alexbrandmeyer@668
|
253 // The last stage will have the correct detect_scale_ value on the basis of
|
alexbrandmeyer@668
|
254 // the total gain accumlated over the stages.
|
alexbrandmeyer@668
|
255 if (stage == agc_params.n_stages_ - 1) {
|
alexbrandmeyer@668
|
256 ASSERT_NEAR(detect_scale, agc_coeffs[stage].detect_scale_,
|
alexbrandmeyer@668
|
257 PRECISION_LEVEL);
|
alexbrandmeyer@668
|
258 }
|
alexbrandmeyer@668
|
259 }
|
alexbrandmeyer@668
|
260 }
|
alexbrandmeyer@668
|
261
|
alexbrandmeyer@668
|
262 // This test verifies the output of the C++ code relative to that of the Matlab
|
alexbrandmeyer@668
|
263 // version using a single segment (441 samples) of audio from the "plan.wav"
|
alexbrandmeyer@668
|
264 // file. The single-channel audio data and different output matrices from Matlab
|
alexbrandmeyer@668
|
265 // are stored in text files and then read into 2d Eigen arrays (for now, this
|
alexbrandmeyer@668
|
266 // should be changed to a vector of FloatArrays... TODO (alexbrandmeyer)). For
|
alexbrandmeyer@668
|
267 // reference, see the CARFAC_GenerateTestData() function in the Matlab branch
|
alexbrandmeyer@668
|
268 // of the repository.
|
alexbrandmeyer@668
|
269 //
|
alexbrandmeyer@668
|
270 // A single Ear object is used along with the code from CARFAC.RunSegment() to
|
alexbrandmeyer@668
|
271 // evaluate the output of the CAR and IHC steps on a sample by sample basis
|
alexbrandmeyer@668
|
272 // relative to the output read in from Matlab. The test passes with 11 degrees
|
alexbrandmeyer@668
|
273 // of precision, with the Matlab data stored using 12 decimals.
|
alexbrandmeyer@668
|
274 //
|
alexbrandmeyer@668
|
275 // TODO (alexbrandmeyer): A subseqent version of this test will operate directly
|
alexbrandmeyer@668
|
276 // on the CARFACOutput structure and will evaluate binaural data.
|
alexbrandmeyer@668
|
277 TEST(CARFACTest, Monaural_Output_Test) {
|
alexbrandmeyer@668
|
278 std::string filename = "monaural_test_nap.txt";
|
alexbrandmeyer@668
|
279 std::vector<FloatArray> nap = Load2dTestData(filename, 441, 71);
|
alexbrandmeyer@668
|
280 filename = "monaural_test_bm.txt";
|
alexbrandmeyer@668
|
281 std::vector<FloatArray> bm = Load2dTestData(filename, 441, 71);
|
alexbrandmeyer@668
|
282 filename = "monaural_test_ohc.txt";
|
alexbrandmeyer@668
|
283 std::vector<FloatArray> ohc = Load2dTestData(filename, 441, 71);
|
alexbrandmeyer@668
|
284 filename = "monaural_test_agc.txt";
|
alexbrandmeyer@668
|
285 std::vector<FloatArray> agc = Load2dTestData(filename, 441, 71);
|
alexbrandmeyer@668
|
286 filename = "file_signal_monaural_test.txt";
|
alexbrandmeyer@668
|
287 std::vector<std::vector<float>> sound_data = Load2dAudioVector(filename, 441,
|
alexbrandmeyer@668
|
288 1);
|
alexbrandmeyer@668
|
289 // The number of timepoints is determined from the length of the audio
|
alexbrandmeyer@668
|
290 // segment.
|
alexbrandmeyer@668
|
291 int32_t n_timepoints = sound_data[0].size();
|
alexbrandmeyer@668
|
292
|
alexbrandmeyer@668
|
293 CARParams car_params;
|
alexbrandmeyer@668
|
294 IHCParams ihc_params;
|
alexbrandmeyer@668
|
295 AGCParams agc_params;
|
alexbrandmeyer@668
|
296 FPType fs = 22050.0;
|
alexbrandmeyer@668
|
297 int n_ch = 0;
|
alexbrandmeyer@668
|
298 FPType pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
|
alexbrandmeyer@668
|
299 while (pole_hz > car_params.min_pole_hz_) {
|
alexbrandmeyer@668
|
300 n_ch++;
|
alexbrandmeyer@668
|
301 pole_hz = pole_hz - car_params.erb_per_step_ *
|
alexbrandmeyer@668
|
302 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
|
alexbrandmeyer@668
|
303 }
|
alexbrandmeyer@668
|
304 FloatArray pole_freqs(n_ch);
|
alexbrandmeyer@668
|
305 pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
|
alexbrandmeyer@668
|
306 for (int ch = 0; ch < n_ch; ++ch) {
|
alexbrandmeyer@668
|
307 pole_freqs(ch) = pole_hz;
|
alexbrandmeyer@668
|
308 pole_hz = pole_hz - car_params.erb_per_step_ *
|
alexbrandmeyer@668
|
309 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
|
alexbrandmeyer@668
|
310 }
|
alexbrandmeyer@668
|
311
|
alexbrandmeyer@668
|
312 // This initializes the CARFAC object and runs the design method.
|
alexbrandmeyer@668
|
313 Ear ear;
|
alexbrandmeyer@668
|
314 ear.InitEar(n_ch, fs, pole_freqs, car_params, ihc_params, agc_params);
|
alexbrandmeyer@668
|
315
|
alexbrandmeyer@668
|
316 CARFACOutput seg_output;
|
alexbrandmeyer@668
|
317 seg_output.InitOutput(1, n_ch, n_timepoints);
|
alexbrandmeyer@668
|
318
|
alexbrandmeyer@668
|
319 // A nested loop structure is used to iterate through the individual samples
|
alexbrandmeyer@668
|
320 // for each ear (audio channel).
|
alexbrandmeyer@668
|
321 FloatArray car_out(n_ch);
|
alexbrandmeyer@668
|
322 FloatArray ihc_out(n_ch);
|
alexbrandmeyer@668
|
323 FloatArray matlab_car_out(n_ch);
|
alexbrandmeyer@668
|
324 FloatArray matlab_ihc_out(n_ch);
|
alexbrandmeyer@668
|
325 bool updated; // This variable is used by the AGC stage.
|
alexbrandmeyer@668
|
326 for (int32_t i = 0; i < n_timepoints; ++i) {
|
alexbrandmeyer@668
|
327 int j = 0;
|
alexbrandmeyer@668
|
328 // First we create a reference to the current Ear object.
|
alexbrandmeyer@668
|
329 // This stores the audio sample currently being processed.
|
alexbrandmeyer@668
|
330 FPType input = sound_data[j][i];
|
alexbrandmeyer@668
|
331 // Now we apply the three stages of the model in sequence to the current
|
alexbrandmeyer@668
|
332 // audio sample.
|
alexbrandmeyer@668
|
333 ear.CARStep(input, &car_out);
|
alexbrandmeyer@668
|
334 matlab_car_out = bm[i];
|
alexbrandmeyer@668
|
335 // This step verifies that the ouput of the CAR step is the same at each
|
alexbrandmeyer@668
|
336 // timepoint and channel as that of the Matlab version.
|
alexbrandmeyer@668
|
337 for (int channel = 0; channel < n_ch; ++channel) {
|
alexbrandmeyer@668
|
338 FPType a = matlab_car_out(channel);
|
alexbrandmeyer@668
|
339 FPType b = car_out(channel);
|
alexbrandmeyer@668
|
340 ASSERT_NEAR(a, b, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
341 }
|
alexbrandmeyer@668
|
342 ear.IHCStep(car_out, &ihc_out);
|
alexbrandmeyer@668
|
343 matlab_ihc_out = nap[i];
|
alexbrandmeyer@668
|
344 // This step verifies that the ouput of the IHC step is the same at each
|
alexbrandmeyer@668
|
345 // timepoint and channel as that of the Matlab version.
|
alexbrandmeyer@668
|
346 for (int channel = 0; channel < n_ch; ++channel) {
|
alexbrandmeyer@668
|
347 FPType a = matlab_ihc_out(channel);
|
alexbrandmeyer@668
|
348 FPType b = ihc_out(channel);
|
alexbrandmeyer@668
|
349 ASSERT_NEAR(a, b, PRECISION_LEVEL);
|
alexbrandmeyer@668
|
350 }
|
alexbrandmeyer@668
|
351
|
alexbrandmeyer@668
|
352 updated = ear.AGCStep(ihc_out);
|
alexbrandmeyer@668
|
353 // These lines assign the output of the model for the current sample
|
alexbrandmeyer@668
|
354 // to the appropriate data members of the current ear in the output
|
alexbrandmeyer@668
|
355 // object.
|
alexbrandmeyer@668
|
356 seg_output.StoreNAPOutput(i, j, ihc_out);
|
alexbrandmeyer@668
|
357 seg_output.StoreBMOutput(i, j, car_out);
|
alexbrandmeyer@668
|
358 seg_output.StoreOHCOutput(i, j, ear.za_memory());
|
alexbrandmeyer@668
|
359 seg_output.StoreAGCOutput(i, j, ear.zb_memory());
|
alexbrandmeyer@668
|
360 if (updated) {
|
alexbrandmeyer@668
|
361 FloatArray undamping = 1 - ear.agc_memory(0);
|
alexbrandmeyer@668
|
362 // This updates the target stage gain for the new damping.
|
alexbrandmeyer@668
|
363 ear.set_dzb_memory((ear.zr_coeffs() * undamping - ear.zb_memory()) /
|
alexbrandmeyer@668
|
364 ear.agc_decimation(0));
|
alexbrandmeyer@668
|
365 ear.set_dg_memory((ear.StageGValue(undamping) - ear.g_memory()) /
|
alexbrandmeyer@668
|
366 ear.agc_decimation(0));
|
alexbrandmeyer@668
|
367 }
|
alexbrandmeyer@668
|
368 }
|
alexbrandmeyer@668
|
369 } |