annotate carfac/carfac_test.cc @ 626:586b0677aae8

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