alexbrandmeyer@626: // alexbrandmeyer@626: // carfac_test.cc alexbrandmeyer@626: // CARFAC Open Source C++ Library alexbrandmeyer@626: // alexbrandmeyer@626: // Created by Alex Brandmeyer on 5/22/13. alexbrandmeyer@626: // alexbrandmeyer@626: // This C++ file is part of an implementation of Lyon's cochlear model: alexbrandmeyer@626: // "Cascade of Asymmetric Resonators with Fast-Acting Compression" alexbrandmeyer@626: // to supplement Lyon's upcoming book "Human and Machine Hearing" alexbrandmeyer@626: // alexbrandmeyer@626: // Licensed under the Apache License, Version 2.0 (the "License"); alexbrandmeyer@626: // you may not use this file except in compliance with the License. alexbrandmeyer@626: // You may obtain a copy of the License at alexbrandmeyer@626: // alexbrandmeyer@626: // http://www.apache.org/licenses/LICENSE-2.0 alexbrandmeyer@626: // alexbrandmeyer@626: // Unless required by applicable law or agreed to in writing, software alexbrandmeyer@626: // distributed under the License is distributed on an "AS IS" BASIS, alexbrandmeyer@626: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. alexbrandmeyer@626: // See the License for the specific language governing permissions and alexbrandmeyer@626: // limitations under the License. alexbrandmeyer@626: ronw@647: #include "carfac.h" ronw@647: ronw@646: #include alexbrandmeyer@636: #include alexbrandmeyer@640: #include ronw@642: ronw@642: #include "gtest/gtest.h" alexbrandmeyer@643: ronw@646: #include "agc.h" alexbrandmeyer@643: #include "car.h" ronw@647: #include "carfac_output.h" alexbrandmeyer@643: #include "common.h" ronw@646: #include "ihc.h" alexbrandmeyer@637: ronw@652: using std::deque; alexbrandmeyer@636: using std::ifstream; alexbrandmeyer@636: using std::ofstream; ronw@646: using std::string; ronw@646: using std::vector; alexbrandmeyer@636: ronw@652: // Location of the text files produced by 'CARFAC_GenerateTestData.m' for ronw@652: // comparing the ouput of the Matlab implementation with the one used here. ronw@652: static const char* kTestSourceDir = "./test_data/"; ronw@630: ronw@652: // Writes the CARFAC NAP output to a text file. ronw@652: void WriteNAPOutput(const CARFACOutput& output, const string& filename, ronw@646: int ear) { alexbrandmeyer@637: string fullfile = kTestSourceDir + filename; alexbrandmeyer@636: ofstream ofile(fullfile.c_str()); alexbrandmeyer@643: int32_t num_timepoints = output.nap().size(); alexbrandmeyer@637: int channels = output.nap()[0][0].size(); alexbrandmeyer@636: if (ofile.is_open()) { alexbrandmeyer@643: for (int32_t i = 0; i < num_timepoints; ++i) { alexbrandmeyer@636: for (int j = 0; j < channels; ++j) { alexbrandmeyer@637: ofile << output.nap()[i][ear](j); ronw@646: if (j < channels - 1) { alexbrandmeyer@636: ofile << " "; alexbrandmeyer@636: } alexbrandmeyer@636: } alexbrandmeyer@636: ofile << "\n"; alexbrandmeyer@636: } alexbrandmeyer@636: } alexbrandmeyer@636: ofile.close(); alexbrandmeyer@636: } alexbrandmeyer@636: ronw@652: // Reads a size rows vector of size columns Container objects from a ronw@652: // multi-column text file generated by the Matlab version of CARFAC. ronw@652: template ronw@654: vector Load2dTestData(const string& filename, int rows, ronw@654: int columns) { alexbrandmeyer@637: string fullfile = kTestSourceDir + filename; alexbrandmeyer@636: ifstream file(fullfile.c_str()); ronw@652: vector output; ronw@652: if (ColMajor) { ronw@652: output.assign(rows, Container(columns)); ronw@652: } else { ronw@652: output.assign(columns, Container(rows)); alexbrandmeyer@626: } alexbrandmeyer@626: if (file.is_open()) { alexbrandmeyer@626: for (int i = 0; i < rows; ++i) { alexbrandmeyer@626: for (int j = 0; j < columns; ++j) { ronw@652: if (ColMajor) { ronw@652: file >> output[i][j]; ronw@652: } else { ronw@652: file >> output[j][i]; ronw@652: } alexbrandmeyer@626: } alexbrandmeyer@626: } alexbrandmeyer@626: } alexbrandmeyer@636: file.close(); alexbrandmeyer@626: return output; alexbrandmeyer@626: } alexbrandmeyer@626: ronw@652: // Reads a two dimensional vector of audio data from a text file ronw@652: // containing the output of the Matlab wavread() function. alexbrandmeyer@636: vector> Load2dAudioVector(string filename, int timepoints, ronw@654: int num_channels) { ronw@654: return Load2dTestData, false>(filename, timepoints, ronw@654: num_channels); ronw@652: } ronw@652: ronw@652: class CARFACTest : public testing::Test { ronw@652: protected: ronw@654: deque> LoadTestData(const string& basename, ronw@654: int num_samples, ronw@654: int num_ears, ronw@654: int num_channels) const { ronw@654: deque> test_data(num_samples, vector(num_ears)); ronw@654: for (int ear = 0; ear < num_ears; ++ear) { ronw@652: string filename = basename + std::to_string(ear + 1) + ".txt"; ronw@654: vector data = Load2dTestData(filename, num_samples, num_channels); ronw@654: for (int i = 0; i < num_samples; ++i) { ronw@652: test_data[i][ear] = data[i]; ronw@652: } ronw@652: } ronw@652: return test_data; alexbrandmeyer@626: } ronw@652: ronw@652: void AssertCARFACOutputNear(const deque>& expected, ronw@652: const deque>& actual, ronw@654: int num_samples, ronw@654: int num_ears, ronw@654: int num_channels) const { ronw@654: for (int timepoint = 0; timepoint < num_samples; ++timepoint) { ronw@654: for (int ear = 0; ear < num_ears; ++ear) { ronw@654: for (int channel = 0; channel < num_channels; ++channel) { ronw@652: const float kPrecisionLevel = 1.0e-7; ronw@652: ASSERT_NEAR(expected[timepoint][ear](channel), ronw@652: actual[timepoint][ear](channel), ronw@652: kPrecisionLevel); ronw@652: } alexbrandmeyer@626: } alexbrandmeyer@626: } alexbrandmeyer@626: } ronw@652: ronw@652: CARParams car_params_; ronw@652: IHCParams ihc_params_; ronw@652: AGCParams agc_params_; ronw@652: }; ronw@652: ronw@652: TEST_F(CARFACTest, BinauralData) { ronw@654: const int kNumSamples = 882; ronw@654: const int kNumEars = 2; ronw@654: const int kNumChannels = 71; ronw@652: vector> sound_data = ronw@654: Load2dAudioVector("file_signal_binaural_test.txt", kNumSamples, kNumEars); ronw@654: CARFAC carfac(kNumEars, 22050, car_params_, ihc_params_, agc_params_); ronw@652: CARFACOutput output(true, true, false, false); ronw@652: const bool kOpenLoop = false; ronw@652: const int length = sound_data[0].size(); ronw@652: carfac.RunSegment(sound_data, 0, length, kOpenLoop, &output); ronw@652: ronw@652: // TODO(ronw): Don't unconditionally overwrite files that are ronw@652: // checked in to the repository on every test run. ronw@652: WriteNAPOutput(output, "cpp_nap_output_1_binaural_test.txt", 0); ronw@652: WriteNAPOutput(output, "cpp_nap_output_2_binaural_test.txt", 1); ronw@652: ronw@652: deque> expected_nap = ronw@654: LoadTestData("binaural_test_nap", kNumSamples, kNumEars, kNumChannels); ronw@652: AssertCARFACOutputNear(expected_nap, output.nap(), ronw@654: kNumSamples, kNumEars, kNumChannels); ronw@652: deque> expected_bm = ronw@654: LoadTestData("binaural_test_bm", kNumSamples, kNumEars, kNumChannels); ronw@652: AssertCARFACOutputNear(expected_bm, output.bm(), ronw@654: kNumSamples, kNumEars, kNumChannels); alexbrandmeyer@626: } alexbrandmeyer@626: ronw@652: TEST_F(CARFACTest, LongBinauralData) { ronw@654: const int kNumSamples = 2000; ronw@654: const int kNumEars = 2; ronw@654: const int kNumChannels = 83; ronw@652: vector> sound_data = ronw@654: Load2dAudioVector("file_signal_long_test.txt", kNumSamples, kNumEars); ronw@654: CARFAC carfac(kNumEars, 44100, car_params_, ihc_params_, agc_params_); ronw@652: CARFACOutput output(true, true, false, false); ronw@641: const bool kOpenLoop = false; ronw@641: const int length = sound_data[0].size(); ronw@652: carfac.RunSegment(sound_data, 0, length, kOpenLoop, &output); ronw@652: ronw@652: // TODO(ronw): Don't unconditionally overwrite files that are ronw@652: // checked in to the repository on every test run. ronw@652: WriteNAPOutput(output, "cpp_nap_output_1_long_test.txt", 0); ronw@652: WriteNAPOutput(output, "cpp_nap_output_2_long_test.txt", 1); ronw@652: ronw@652: deque> expected_nap = ronw@654: LoadTestData("long_test_nap", kNumSamples, kNumEars, kNumChannels); ronw@652: AssertCARFACOutputNear(expected_nap, output.nap(), ronw@654: kNumSamples, kNumEars, kNumChannels); ronw@652: deque> expected_bm = ronw@654: LoadTestData("long_test_bm", kNumSamples, kNumEars, kNumChannels); ronw@652: AssertCARFACOutputNear(expected_bm, output.bm(), ronw@654: kNumSamples, kNumEars, kNumChannels); alexbrandmeyer@626: }