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@682: #include "car_params.h" alexbrandmeyer@682: #include "ihc_params.h" alexbrandmeyer@682: #include "agc_params.h" alexbrandmeyer@678: #include "carfac.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@678: // Here we specify the level to which the output should match (7 decimals). alexbrandmeyer@679: static const float kPrecisionLevel = 1.0e-7; 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@668: // This loads one-dimensional FloatArrays from single-column text files. alexbrandmeyer@678: void WriteNAPOutput(CARFACOutput output, const string filename, int ear) { alexbrandmeyer@679: string fullfile = kTestSourceDir + filename; alexbrandmeyer@678: ofstream ofile(fullfile.c_str()); alexbrandmeyer@679: int32_t n_timepoints = output.nap().size(); alexbrandmeyer@679: int channels = output.nap()[0][0].size(); alexbrandmeyer@678: if (ofile.is_open()) { alexbrandmeyer@678: for (int32_t i = 0; i < n_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@679: FloatArray 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@668: FloatArray 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@678: // This loads a vector of FloatArrays from multi-column text files. alexbrandmeyer@678: 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@678: 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@678: int n_timepoints = 882; alexbrandmeyer@678: int n_channels = 71; alexbrandmeyer@678: int n_ears = 2; alexbrandmeyer@679: string filename = "binaural_test_nap1.txt"; alexbrandmeyer@679: vector nap1 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "binaural_test_bm1.txt"; alexbrandmeyer@679: vector bm1 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "binaural_test_nap2.txt"; alexbrandmeyer@679: vector nap2 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "binaural_test_bm2.txt"; alexbrandmeyer@679: vector bm2 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "file_signal_binaural_test.txt"; alexbrandmeyer@679: vector> sound_data = Load2dAudioVector(filename, n_timepoints, alexbrandmeyer@679: n_ears); alexbrandmeyer@668: CARParams car_params; alexbrandmeyer@678: IHCParams ihc_params; alexbrandmeyer@678: AGCParams agc_params; alexbrandmeyer@678: CARFAC mycf; alexbrandmeyer@678: mycf.Design(n_ears, 22050, car_params, ihc_params, alexbrandmeyer@678: agc_params); alexbrandmeyer@678: CARFACOutput my_output; alexbrandmeyer@678: my_output.Init(n_ears, 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@678: for (int timepoint = 0; timepoint < n_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@678: for (int timepoint = 0; timepoint < n_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@678: int n_timepoints = 2000; alexbrandmeyer@678: int n_channels = 83; alexbrandmeyer@678: int n_ears = 2; alexbrandmeyer@678: string filename = "long_test_nap1.txt"; alexbrandmeyer@679: vector nap1 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "long_test_bm1.txt"; alexbrandmeyer@679: vector bm1 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "long_test_nap2.txt"; alexbrandmeyer@679: vector nap2 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "long_test_bm2.txt"; alexbrandmeyer@679: vector bm2 = Load2dTestData(filename, n_timepoints, n_channels); alexbrandmeyer@678: filename = "file_signal_long_test.txt"; alexbrandmeyer@679: vector> sound_data = Load2dAudioVector(filename, n_timepoints, alexbrandmeyer@679: n_ears); alexbrandmeyer@668: CARParams car_params; alexbrandmeyer@668: IHCParams ihc_params; alexbrandmeyer@668: AGCParams agc_params; alexbrandmeyer@678: CARFAC mycf; alexbrandmeyer@678: mycf.Design(n_ears, 44100, car_params, ihc_params, alexbrandmeyer@678: agc_params); alexbrandmeyer@678: CARFACOutput my_output; alexbrandmeyer@678: my_output.Init(n_ears, 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@678: for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { alexbrandmeyer@678: for (int channel = 0; channel < n_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@678: for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { alexbrandmeyer@678: for (int channel = 0; channel < n_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: } ronw@683: }