Mercurial > hg > aimc
view trunk/carfac/carfac_test.cc @ 678:7f424c1a8b78
Fifth revision of Alex Brandmeyer's C++ implementation of CARFAC. Moved output structure to deque<vector<FloatArray>, moved coefficient Design methods to CARFAC object, moved tests into carfac_test.cc. Verified binaural output against Matlab using two tests. Added CARFAC_Compare_CPP_Test_Data to plot NAP output of C++ version against Matlab version. Verified build and test success on OS X using SCons with g++ 4.7 (std=c++11).
author | alexbrandmeyer |
---|---|
date | Mon, 27 May 2013 16:36:54 +0000 |
parents | a9694d0bb55a |
children | 594b410c2aed |
line wrap: on
line source
// // carfac_test.cc // CARFAC Open Source C++ Library // // Created by Alex Brandmeyer on 5/22/13. // // This C++ file is part of an implementation of Lyon's cochlear model: // "Cascade of Asymmetric Resonators with Fast-Acting Compression" // to supplement Lyon's upcoming book "Human and Machine Hearing" // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <string> #include <fstream> // GoogleTest is now included for running unit tests #include <gtest/gtest.h> #include "carfac.h" using std::vector; using std::string; using std::ifstream; using std::ofstream; #define TEST_SRC_DIR "./test_data/" // Here we specify the level to which the output should match (7 decimals). #define PRECISION_LEVEL 1.0e-07 // Three helper functions are defined here for loading the test data generated // by the Matlab version of CARFAC. // This loads one-dimensional FloatArrays from single-column text files. void WriteNAPOutput(CARFACOutput output, const string filename, int ear) { string fullfile = TEST_SRC_DIR + filename; ofstream ofile(fullfile.c_str()); int32_t n_timepoints = output.nap_.size(); int channels = output.nap_[0][0].size(); if (ofile.is_open()) { for (int32_t i = 0; i < n_timepoints; ++i) { for (int j = 0; j < channels; ++j) { ofile << output.nap(ear, i, j); if ( j < channels - 1) { ofile << " "; } } ofile << "\n"; } } ofile.close(); } FloatArray LoadTestData(const std::string filename, const int number_points) { string fullfile = TEST_SRC_DIR + filename; ifstream file(fullfile.c_str()); FPType myarray[number_points]; FloatArray output(number_points); if (file.is_open()) { for (int i = 0; i < number_points; ++i) { file >> myarray[i]; output(i) = myarray[i]; } } file.close(); return output; } // This loads a vector of FloatArrays from multi-column text files. vector<FloatArray> Load2dTestData(const string filename, const int rows, const int columns) { string fullfile = TEST_SRC_DIR + filename; ifstream file(fullfile.c_str()); FPType myarray[rows][columns]; vector<FloatArray> output; output.resize(rows); for (auto& timepoint : output) { timepoint.resize(columns); } if (file.is_open()) { for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { file >> myarray[i][j]; output[i](j) = myarray[i][j]; } } } file.close(); return output; } // This loads two dimensional vectors of audio data using data generated in // Matlab using the wavread() function. vector<vector<float>> Load2dAudioVector(string filename, int timepoints, int channels) { string fullfile = TEST_SRC_DIR + filename; ifstream file(fullfile.c_str()); vector<vector<float>> output; output.resize(channels); for (auto& channel : output) { channel.resize(timepoints); } if (file.is_open()) { for (int i = 0; i < timepoints; ++i) { for (int j = 0; j < channels; ++j) { file >> output[j][i]; } } } file.close(); return output; } TEST(CARFACTest, Binaural_Output_test) { int n_timepoints = 882; int n_channels = 71; int n_ears = 2; std::string filename = "binaural_test_nap1.txt"; std::vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels); filename = "binaural_test_bm1.txt"; std::vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels); filename = "binaural_test_nap2.txt"; std::vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels); filename = "binaural_test_bm2.txt"; std::vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels); filename = "file_signal_binaural_test.txt"; std::vector<std::vector<float>> sound_data = Load2dAudioVector(filename, n_timepoints, n_ears); CARParams car_params; IHCParams ihc_params; AGCParams agc_params; CARFAC mycf; mycf.Design(n_ears, 22050, car_params, ihc_params, agc_params); CARFACOutput my_output; my_output.Init(n_ears, true, false, true, false, false); mycf.Run(sound_data, &my_output); filename = "cpp_nap_output_1_binaural_test.txt"; WriteNAPOutput(my_output, filename, 0); filename = "cpp_nap_output_2_binaural_test.txt"; WriteNAPOutput(my_output, filename, 1); int ear = 0; int n_ch = 71; for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { for (int channel = 0; channel < n_ch; ++channel) { FPType cplusplus = my_output.nap(ear, timepoint, channel); FPType matlab = nap1[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); cplusplus = my_output.bm(ear, timepoint, channel); matlab = bm1[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); } } ear = 1; for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { for (int channel = 0; channel < n_ch; ++channel) { FPType cplusplus = my_output.nap(ear, timepoint, channel); FPType matlab = nap2[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); cplusplus = my_output.bm(ear, timepoint, channel); matlab = bm2[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); } } } TEST(CARFACTest, Long_Output_test) { int n_timepoints = 2000; int n_channels = 83; int n_ears = 2; string filename = "long_test_nap1.txt"; vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels); filename = "long_test_bm1.txt"; vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels); filename = "long_test_nap2.txt"; vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels); filename = "long_test_bm2.txt"; vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels); filename = "file_signal_long_test.txt"; vector<vector<float>> sound_data = Load2dAudioVector(filename, n_timepoints, n_ears); CARParams car_params; IHCParams ihc_params; AGCParams agc_params; CARFAC mycf; mycf.Design(n_ears, 44100, car_params, ihc_params, agc_params); CARFACOutput my_output; my_output.Init(n_ears, true, false, true, false, false); mycf.Run(sound_data, &my_output); filename = "cpp_nap_output_1_long_test.txt"; WriteNAPOutput(my_output, filename, 0); filename = "cpp_nap_output_2_long_test.txt"; WriteNAPOutput(my_output, filename, 1); int ear = 0; for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { for (int channel = 0; channel < n_channels; ++channel) { FPType cplusplus = my_output.nap(ear, timepoint, channel); FPType matlab = nap1[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); cplusplus = my_output.bm(ear, timepoint, channel); matlab = bm1[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); } } ear = 1; for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { for (int channel = 0; channel < n_channels; ++channel) { FPType cplusplus = my_output.nap(ear, timepoint, channel); FPType matlab = nap2[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); cplusplus = my_output.bm(ear, timepoint, channel); matlab = bm2[timepoint](channel); ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL); } } } int main(int argc, char **argv) { // This initializes the GoogleTest unit testing framework. ::testing::InitGoogleTest(&argc, argv); // This runs all of the tests that we've defined above. return RUN_ALL_TESTS(); }