alexbrandmeyer@668: // alexbrandmeyer@668: // carfac_test.cc alexbrandmeyer@668: // CARFAC Open Source C++ Library alexbrandmeyer@668: // alexbrandmeyer@668: // Created by Alex Brandmeyer on 5/22/13. alexbrandmeyer@668: // alexbrandmeyer@668: // This C++ file is part of an implementation of Lyon's cochlear model: alexbrandmeyer@668: // "Cascade of Asymmetric Resonators with Fast-Acting Compression" alexbrandmeyer@668: // to supplement Lyon's upcoming book "Human and Machine Hearing" alexbrandmeyer@668: // alexbrandmeyer@668: // Licensed under the Apache License, Version 2.0 (the "License"); alexbrandmeyer@668: // you may not use this file except in compliance with the License. alexbrandmeyer@668: // You may obtain a copy of the License at alexbrandmeyer@668: // alexbrandmeyer@668: // http://www.apache.org/licenses/LICENSE-2.0 alexbrandmeyer@668: // alexbrandmeyer@668: // Unless required by applicable law or agreed to in writing, software alexbrandmeyer@668: // distributed under the License is distributed on an "AS IS" BASIS, alexbrandmeyer@668: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. alexbrandmeyer@668: // See the License for the specific language governing permissions and alexbrandmeyer@668: // limitations under the License. alexbrandmeyer@668: alexbrandmeyer@678: #include alexbrandmeyer@678: #include alexbrandmeyer@682: #include ronw@684: ronw@684: #include "gtest/gtest.h" alexbrandmeyer@685: alexbrandmeyer@685: #include "car.h" alexbrandmeyer@685: #include "ihc.h" alexbrandmeyer@685: #include "agc.h" alexbrandmeyer@678: #include "carfac.h" alexbrandmeyer@685: #include "common.h" alexbrandmeyer@679: alexbrandmeyer@678: using std::vector; alexbrandmeyer@678: using std::string; alexbrandmeyer@678: using std::ifstream; alexbrandmeyer@678: using std::ofstream; alexbrandmeyer@678: alexbrandmeyer@679: // This is the 'test_data' subdirectory of aimc/carfac that specifies where to alexbrandmeyer@679: // locate the text files produced by 'CARFAC_GenerateTestData.m' for comparing alexbrandmeyer@679: // the ouput of the Matlab version of CARFAC with this C++ version. alexbrandmeyer@679: static const char* kTestSourceDir= "./test_data/"; alexbrandmeyer@685: // Here we specify the level to which the output should match (2 decimals). alexbrandmeyer@685: static const float kPrecisionLevel = 1.0e-2; ronw@672: alexbrandmeyer@668: // Three helper functions are defined here for loading the test data generated alexbrandmeyer@668: // by the Matlab version of CARFAC. alexbrandmeyer@685: // This loads one-dimensional ArrayXs from single-column text files. alexbrandmeyer@685: void WriteNAPOutput(CARFACOutput& output, const string filename, int ear) { alexbrandmeyer@679: string fullfile = kTestSourceDir + filename; alexbrandmeyer@678: ofstream ofile(fullfile.c_str()); alexbrandmeyer@685: int32_t num_timepoints = output.nap().size(); alexbrandmeyer@679: int channels = output.nap()[0][0].size(); alexbrandmeyer@678: if (ofile.is_open()) { alexbrandmeyer@685: for (int32_t i = 0; i < num_timepoints; ++i) { alexbrandmeyer@678: for (int j = 0; j < channels; ++j) { alexbrandmeyer@679: ofile << output.nap()[i][ear](j); alexbrandmeyer@678: if ( j < channels - 1) { alexbrandmeyer@678: ofile << " "; alexbrandmeyer@678: } alexbrandmeyer@678: } alexbrandmeyer@678: ofile << "\n"; alexbrandmeyer@678: } alexbrandmeyer@678: } alexbrandmeyer@678: ofile.close(); alexbrandmeyer@678: } alexbrandmeyer@678: alexbrandmeyer@685: ArrayX LoadTestData(const string filename, const int number_points) { alexbrandmeyer@679: string fullfile = kTestSourceDir + filename; alexbrandmeyer@678: ifstream file(fullfile.c_str()); alexbrandmeyer@668: FPType myarray[number_points]; alexbrandmeyer@685: ArrayX output(number_points); alexbrandmeyer@668: if (file.is_open()) { alexbrandmeyer@668: for (int i = 0; i < number_points; ++i) { alexbrandmeyer@668: file >> myarray[i]; alexbrandmeyer@668: output(i) = myarray[i]; alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@678: file.close(); alexbrandmeyer@668: return output; alexbrandmeyer@668: } alexbrandmeyer@668: alexbrandmeyer@685: // This loads a vector of ArrayXs from multi-column text files. alexbrandmeyer@685: vector Load2dTestData(const string filename, const int rows, alexbrandmeyer@668: const int columns) { alexbrandmeyer@679: string fullfile = kTestSourceDir + filename; alexbrandmeyer@678: ifstream file(fullfile.c_str()); alexbrandmeyer@668: FPType myarray[rows][columns]; alexbrandmeyer@685: vector output; alexbrandmeyer@668: output.resize(rows); alexbrandmeyer@668: for (auto& timepoint : output) { alexbrandmeyer@668: timepoint.resize(columns); alexbrandmeyer@668: } alexbrandmeyer@668: if (file.is_open()) { alexbrandmeyer@668: for (int i = 0; i < rows; ++i) { alexbrandmeyer@668: for (int j = 0; j < columns; ++j) { alexbrandmeyer@668: file >> myarray[i][j]; alexbrandmeyer@668: output[i](j) = myarray[i][j]; alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@678: file.close(); alexbrandmeyer@668: return output; alexbrandmeyer@668: } alexbrandmeyer@668: alexbrandmeyer@668: // This loads two dimensional vectors of audio data using data generated in alexbrandmeyer@668: // Matlab using the wavread() function. alexbrandmeyer@678: vector> Load2dAudioVector(string filename, int timepoints, alexbrandmeyer@678: int channels) { alexbrandmeyer@679: string fullfile = kTestSourceDir + filename; alexbrandmeyer@678: ifstream file(fullfile.c_str()); alexbrandmeyer@678: vector> output; alexbrandmeyer@668: output.resize(channels); alexbrandmeyer@668: for (auto& channel : output) { alexbrandmeyer@668: channel.resize(timepoints); alexbrandmeyer@668: } alexbrandmeyer@668: if (file.is_open()) { alexbrandmeyer@668: for (int i = 0; i < timepoints; ++i) { alexbrandmeyer@668: for (int j = 0; j < channels; ++j) { alexbrandmeyer@668: file >> output[j][i]; alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@678: file.close(); alexbrandmeyer@668: return output; alexbrandmeyer@668: } alexbrandmeyer@668: alexbrandmeyer@678: TEST(CARFACTest, Binaural_Output_test) { alexbrandmeyer@685: int num_timepoints = 882; alexbrandmeyer@685: int num_channels = 71; alexbrandmeyer@685: int num_ears = 2; alexbrandmeyer@679: string filename = "binaural_test_nap1.txt"; alexbrandmeyer@685: vector nap1 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "binaural_test_bm1.txt"; alexbrandmeyer@685: vector bm1 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "binaural_test_nap2.txt"; alexbrandmeyer@685: vector nap2 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "binaural_test_bm2.txt"; alexbrandmeyer@685: vector bm2 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "file_signal_binaural_test.txt"; alexbrandmeyer@685: vector> sound_data = Load2dAudioVector(filename, num_timepoints, alexbrandmeyer@685: num_ears); alexbrandmeyer@668: CARParams car_params; alexbrandmeyer@678: IHCParams ihc_params; alexbrandmeyer@678: AGCParams agc_params; alexbrandmeyer@685: CARFAC mycf(num_ears, 22050, car_params, ihc_params, agc_params); alexbrandmeyer@685: CARFACOutput my_output(true, false, true, false, false); ronw@683: const bool kOpenLoop = false; ronw@683: const int length = sound_data[0].size(); ronw@683: mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output); alexbrandmeyer@678: filename = "cpp_nap_output_1_binaural_test.txt"; alexbrandmeyer@678: WriteNAPOutput(my_output, filename, 0); alexbrandmeyer@678: filename = "cpp_nap_output_2_binaural_test.txt"; alexbrandmeyer@678: WriteNAPOutput(my_output, filename, 1); alexbrandmeyer@678: int ear = 0; alexbrandmeyer@678: int n_ch = 71; alexbrandmeyer@685: for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) { alexbrandmeyer@678: for (int channel = 0; channel < n_ch; ++channel) { alexbrandmeyer@679: FPType cplusplus = my_output.nap()[timepoint][ear](channel); alexbrandmeyer@678: FPType matlab = nap1[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@679: cplusplus = my_output.bm()[timepoint][ear](channel); alexbrandmeyer@678: matlab = bm1[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@678: } alexbrandmeyer@668: } alexbrandmeyer@678: ear = 1; alexbrandmeyer@685: for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) { alexbrandmeyer@678: for (int channel = 0; channel < n_ch; ++channel) { alexbrandmeyer@679: FPType cplusplus = my_output.nap()[timepoint][ear](channel); alexbrandmeyer@678: FPType matlab = nap2[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@679: cplusplus = my_output.bm()[timepoint][ear](channel); alexbrandmeyer@678: matlab = bm2[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@668: alexbrandmeyer@678: TEST(CARFACTest, Long_Output_test) { alexbrandmeyer@685: int num_timepoints = 2000; alexbrandmeyer@685: int num_channels = 83; alexbrandmeyer@685: int num_ears = 2; alexbrandmeyer@678: string filename = "long_test_nap1.txt"; alexbrandmeyer@685: vector nap1 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "long_test_bm1.txt"; alexbrandmeyer@685: vector bm1 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "long_test_nap2.txt"; alexbrandmeyer@685: vector nap2 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "long_test_bm2.txt"; alexbrandmeyer@685: vector bm2 = Load2dTestData(filename, num_timepoints, num_channels); alexbrandmeyer@678: filename = "file_signal_long_test.txt"; alexbrandmeyer@685: vector> sound_data = Load2dAudioVector(filename, num_timepoints, alexbrandmeyer@685: num_ears); alexbrandmeyer@668: CARParams car_params; alexbrandmeyer@668: IHCParams ihc_params; alexbrandmeyer@668: AGCParams agc_params; alexbrandmeyer@685: CARFAC mycf(num_ears, 44100, car_params, ihc_params, agc_params); alexbrandmeyer@685: CARFACOutput my_output(true, false, true, false, false); ronw@683: const bool kOpenLoop = false; ronw@683: const int length = sound_data[0].size(); ronw@683: mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output); alexbrandmeyer@678: filename = "cpp_nap_output_1_long_test.txt"; alexbrandmeyer@678: WriteNAPOutput(my_output, filename, 0); alexbrandmeyer@678: filename = "cpp_nap_output_2_long_test.txt"; alexbrandmeyer@678: WriteNAPOutput(my_output, filename, 1); alexbrandmeyer@678: int ear = 0; alexbrandmeyer@685: for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) { alexbrandmeyer@685: for (int channel = 0; channel < num_channels; ++channel) { alexbrandmeyer@679: FPType cplusplus = my_output.nap()[timepoint][ear](channel); alexbrandmeyer@678: FPType matlab = nap1[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@679: cplusplus = my_output.bm()[timepoint][ear](channel); alexbrandmeyer@678: matlab = bm1[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@678: } alexbrandmeyer@668: } alexbrandmeyer@678: ear = 1; alexbrandmeyer@685: for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) { alexbrandmeyer@685: for (int channel = 0; channel < num_channels; ++channel) { alexbrandmeyer@679: FPType cplusplus = my_output.nap()[timepoint][ear](channel); alexbrandmeyer@678: FPType matlab = nap2[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@679: cplusplus = my_output.bm()[timepoint][ear](channel); alexbrandmeyer@678: matlab = bm2[timepoint](channel); alexbrandmeyer@679: ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); alexbrandmeyer@668: } alexbrandmeyer@668: } alexbrandmeyer@685: }