annotate 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
rev   line source
alexbrandmeyer@668 1 //
alexbrandmeyer@668 2 // carfac_test.cc
alexbrandmeyer@668 3 // CARFAC Open Source C++ Library
alexbrandmeyer@668 4 //
alexbrandmeyer@668 5 // Created by Alex Brandmeyer on 5/22/13.
alexbrandmeyer@668 6 //
alexbrandmeyer@668 7 // This C++ file is part of an implementation of Lyon's cochlear model:
alexbrandmeyer@668 8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
alexbrandmeyer@668 9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
alexbrandmeyer@668 10 //
alexbrandmeyer@668 11 // Licensed under the Apache License, Version 2.0 (the "License");
alexbrandmeyer@668 12 // you may not use this file except in compliance with the License.
alexbrandmeyer@668 13 // You may obtain a copy of the License at
alexbrandmeyer@668 14 //
alexbrandmeyer@668 15 // http://www.apache.org/licenses/LICENSE-2.0
alexbrandmeyer@668 16 //
alexbrandmeyer@668 17 // Unless required by applicable law or agreed to in writing, software
alexbrandmeyer@668 18 // distributed under the License is distributed on an "AS IS" BASIS,
alexbrandmeyer@668 19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
alexbrandmeyer@668 20 // See the License for the specific language governing permissions and
alexbrandmeyer@668 21 // limitations under the License.
alexbrandmeyer@668 22
alexbrandmeyer@678 23 #include <string>
alexbrandmeyer@678 24 #include <fstream>
alexbrandmeyer@678 25 // GoogleTest is now included for running unit tests
alexbrandmeyer@678 26 #include <gtest/gtest.h>
alexbrandmeyer@678 27 #include "carfac.h"
alexbrandmeyer@678 28 using std::vector;
alexbrandmeyer@678 29 using std::string;
alexbrandmeyer@678 30 using std::ifstream;
alexbrandmeyer@678 31 using std::ofstream;
alexbrandmeyer@678 32
alexbrandmeyer@678 33 #define TEST_SRC_DIR "./test_data/"
alexbrandmeyer@678 34 // Here we specify the level to which the output should match (7 decimals).
alexbrandmeyer@678 35 #define PRECISION_LEVEL 1.0e-07
ronw@672 36
alexbrandmeyer@668 37 // Three helper functions are defined here for loading the test data generated
alexbrandmeyer@668 38 // by the Matlab version of CARFAC.
alexbrandmeyer@668 39 // This loads one-dimensional FloatArrays from single-column text files.
alexbrandmeyer@678 40 void WriteNAPOutput(CARFACOutput output, const string filename, int ear) {
alexbrandmeyer@678 41 string fullfile = TEST_SRC_DIR + filename;
alexbrandmeyer@678 42 ofstream ofile(fullfile.c_str());
alexbrandmeyer@678 43 int32_t n_timepoints = output.nap_.size();
alexbrandmeyer@678 44 int channels = output.nap_[0][0].size();
alexbrandmeyer@678 45 if (ofile.is_open()) {
alexbrandmeyer@678 46 for (int32_t i = 0; i < n_timepoints; ++i) {
alexbrandmeyer@678 47 for (int j = 0; j < channels; ++j) {
alexbrandmeyer@678 48 ofile << output.nap(ear, i, j);
alexbrandmeyer@678 49 if ( j < channels - 1) {
alexbrandmeyer@678 50 ofile << " ";
alexbrandmeyer@678 51 }
alexbrandmeyer@678 52 }
alexbrandmeyer@678 53 ofile << "\n";
alexbrandmeyer@678 54 }
alexbrandmeyer@678 55 }
alexbrandmeyer@678 56 ofile.close();
alexbrandmeyer@678 57 }
alexbrandmeyer@678 58
alexbrandmeyer@668 59 FloatArray LoadTestData(const std::string filename, const int number_points) {
alexbrandmeyer@678 60 string fullfile = TEST_SRC_DIR + filename;
alexbrandmeyer@678 61 ifstream file(fullfile.c_str());
alexbrandmeyer@668 62 FPType myarray[number_points];
alexbrandmeyer@668 63 FloatArray output(number_points);
alexbrandmeyer@668 64 if (file.is_open()) {
alexbrandmeyer@668 65 for (int i = 0; i < number_points; ++i) {
alexbrandmeyer@668 66 file >> myarray[i];
alexbrandmeyer@668 67 output(i) = myarray[i];
alexbrandmeyer@668 68 }
alexbrandmeyer@668 69 }
alexbrandmeyer@678 70 file.close();
alexbrandmeyer@668 71 return output;
alexbrandmeyer@668 72 }
alexbrandmeyer@668 73
alexbrandmeyer@678 74 // This loads a vector of FloatArrays from multi-column text files.
alexbrandmeyer@678 75 vector<FloatArray> Load2dTestData(const string filename, const int rows,
alexbrandmeyer@668 76 const int columns) {
alexbrandmeyer@678 77 string fullfile = TEST_SRC_DIR + filename;
alexbrandmeyer@678 78 ifstream file(fullfile.c_str());
alexbrandmeyer@668 79 FPType myarray[rows][columns];
alexbrandmeyer@678 80 vector<FloatArray> output;
alexbrandmeyer@668 81 output.resize(rows);
alexbrandmeyer@668 82 for (auto& timepoint : output) {
alexbrandmeyer@668 83 timepoint.resize(columns);
alexbrandmeyer@668 84 }
alexbrandmeyer@668 85 if (file.is_open()) {
alexbrandmeyer@668 86 for (int i = 0; i < rows; ++i) {
alexbrandmeyer@668 87 for (int j = 0; j < columns; ++j) {
alexbrandmeyer@668 88 file >> myarray[i][j];
alexbrandmeyer@668 89 output[i](j) = myarray[i][j];
alexbrandmeyer@668 90 }
alexbrandmeyer@668 91 }
alexbrandmeyer@668 92 }
alexbrandmeyer@678 93 file.close();
alexbrandmeyer@668 94 return output;
alexbrandmeyer@668 95 }
alexbrandmeyer@668 96
alexbrandmeyer@668 97 // This loads two dimensional vectors of audio data using data generated in
alexbrandmeyer@668 98 // Matlab using the wavread() function.
alexbrandmeyer@678 99 vector<vector<float>> Load2dAudioVector(string filename, int timepoints,
alexbrandmeyer@678 100 int channels) {
alexbrandmeyer@678 101 string fullfile = TEST_SRC_DIR + filename;
alexbrandmeyer@678 102 ifstream file(fullfile.c_str());
alexbrandmeyer@678 103 vector<vector<float>> output;
alexbrandmeyer@668 104 output.resize(channels);
alexbrandmeyer@668 105 for (auto& channel : output) {
alexbrandmeyer@668 106 channel.resize(timepoints);
alexbrandmeyer@668 107 }
alexbrandmeyer@668 108 if (file.is_open()) {
alexbrandmeyer@668 109 for (int i = 0; i < timepoints; ++i) {
alexbrandmeyer@668 110 for (int j = 0; j < channels; ++j) {
alexbrandmeyer@668 111 file >> output[j][i];
alexbrandmeyer@668 112 }
alexbrandmeyer@668 113 }
alexbrandmeyer@668 114 }
alexbrandmeyer@678 115 file.close();
alexbrandmeyer@668 116 return output;
alexbrandmeyer@668 117 }
alexbrandmeyer@668 118
alexbrandmeyer@678 119 TEST(CARFACTest, Binaural_Output_test) {
alexbrandmeyer@678 120 int n_timepoints = 882;
alexbrandmeyer@678 121 int n_channels = 71;
alexbrandmeyer@678 122 int n_ears = 2;
alexbrandmeyer@678 123 std::string filename = "binaural_test_nap1.txt";
alexbrandmeyer@678 124 std::vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 125 n_channels);
alexbrandmeyer@678 126 filename = "binaural_test_bm1.txt";
alexbrandmeyer@678 127 std::vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 128 n_channels);
alexbrandmeyer@678 129 filename = "binaural_test_nap2.txt";
alexbrandmeyer@678 130 std::vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 131 n_channels);
alexbrandmeyer@678 132 filename = "binaural_test_bm2.txt";
alexbrandmeyer@678 133 std::vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 134 n_channels);
alexbrandmeyer@678 135 filename = "file_signal_binaural_test.txt";
alexbrandmeyer@678 136 std::vector<std::vector<float>> sound_data = Load2dAudioVector(filename,
alexbrandmeyer@678 137 n_timepoints,
alexbrandmeyer@678 138 n_ears);
alexbrandmeyer@668 139 CARParams car_params;
alexbrandmeyer@678 140 IHCParams ihc_params;
alexbrandmeyer@678 141 AGCParams agc_params;
alexbrandmeyer@678 142 CARFAC mycf;
alexbrandmeyer@678 143 mycf.Design(n_ears, 22050, car_params, ihc_params,
alexbrandmeyer@678 144 agc_params);
alexbrandmeyer@678 145 CARFACOutput my_output;
alexbrandmeyer@678 146 my_output.Init(n_ears, true, false, true, false, false);
alexbrandmeyer@678 147 mycf.Run(sound_data, &my_output);
alexbrandmeyer@678 148 filename = "cpp_nap_output_1_binaural_test.txt";
alexbrandmeyer@678 149 WriteNAPOutput(my_output, filename, 0);
alexbrandmeyer@678 150 filename = "cpp_nap_output_2_binaural_test.txt";
alexbrandmeyer@678 151 WriteNAPOutput(my_output, filename, 1);
alexbrandmeyer@678 152 int ear = 0;
alexbrandmeyer@678 153 int n_ch = 71;
alexbrandmeyer@678 154 for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
alexbrandmeyer@678 155 for (int channel = 0; channel < n_ch; ++channel) {
alexbrandmeyer@678 156 FPType cplusplus = my_output.nap(ear, timepoint, channel);
alexbrandmeyer@678 157 FPType matlab = nap1[timepoint](channel);
alexbrandmeyer@678 158 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 159 cplusplus = my_output.bm(ear, timepoint, channel);
alexbrandmeyer@678 160 matlab = bm1[timepoint](channel);
alexbrandmeyer@678 161 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 162 }
alexbrandmeyer@668 163 }
alexbrandmeyer@678 164 ear = 1;
alexbrandmeyer@678 165 for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
alexbrandmeyer@678 166 for (int channel = 0; channel < n_ch; ++channel) {
alexbrandmeyer@678 167 FPType cplusplus = my_output.nap(ear, timepoint, channel);
alexbrandmeyer@678 168 FPType matlab = nap2[timepoint](channel);
alexbrandmeyer@678 169 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 170 cplusplus = my_output.bm(ear, timepoint, channel);
alexbrandmeyer@678 171 matlab = bm2[timepoint](channel);
alexbrandmeyer@678 172 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@668 173 }
alexbrandmeyer@668 174 }
alexbrandmeyer@668 175 }
alexbrandmeyer@668 176
alexbrandmeyer@678 177 TEST(CARFACTest, Long_Output_test) {
alexbrandmeyer@678 178 int n_timepoints = 2000;
alexbrandmeyer@678 179 int n_channels = 83;
alexbrandmeyer@678 180 int n_ears = 2;
alexbrandmeyer@678 181 string filename = "long_test_nap1.txt";
alexbrandmeyer@678 182 vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 183 n_channels);
alexbrandmeyer@678 184 filename = "long_test_bm1.txt";
alexbrandmeyer@678 185 vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 186 n_channels);
alexbrandmeyer@678 187 filename = "long_test_nap2.txt";
alexbrandmeyer@678 188 vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 189 n_channels);
alexbrandmeyer@678 190 filename = "long_test_bm2.txt";
alexbrandmeyer@678 191 vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints,
alexbrandmeyer@678 192 n_channels);
alexbrandmeyer@678 193 filename = "file_signal_long_test.txt";
alexbrandmeyer@678 194 vector<vector<float>> sound_data = Load2dAudioVector(filename,
alexbrandmeyer@678 195 n_timepoints,
alexbrandmeyer@678 196 n_ears);
alexbrandmeyer@668 197 CARParams car_params;
alexbrandmeyer@668 198 IHCParams ihc_params;
alexbrandmeyer@668 199 AGCParams agc_params;
alexbrandmeyer@678 200 CARFAC mycf;
alexbrandmeyer@678 201 mycf.Design(n_ears, 44100, car_params, ihc_params,
alexbrandmeyer@678 202 agc_params);
alexbrandmeyer@678 203 CARFACOutput my_output;
alexbrandmeyer@678 204 my_output.Init(n_ears, true, false, true, false, false);
alexbrandmeyer@678 205 mycf.Run(sound_data, &my_output);
alexbrandmeyer@678 206 filename = "cpp_nap_output_1_long_test.txt";
alexbrandmeyer@678 207 WriteNAPOutput(my_output, filename, 0);
alexbrandmeyer@678 208 filename = "cpp_nap_output_2_long_test.txt";
alexbrandmeyer@678 209 WriteNAPOutput(my_output, filename, 1);
alexbrandmeyer@678 210 int ear = 0;
alexbrandmeyer@678 211 for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
alexbrandmeyer@678 212 for (int channel = 0; channel < n_channels; ++channel) {
alexbrandmeyer@678 213 FPType cplusplus = my_output.nap(ear, timepoint, channel);
alexbrandmeyer@678 214 FPType matlab = nap1[timepoint](channel);
alexbrandmeyer@678 215 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 216 cplusplus = my_output.bm(ear, timepoint, channel);
alexbrandmeyer@678 217 matlab = bm1[timepoint](channel);
alexbrandmeyer@678 218 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 219 }
alexbrandmeyer@668 220 }
alexbrandmeyer@678 221 ear = 1;
alexbrandmeyer@678 222 for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
alexbrandmeyer@678 223 for (int channel = 0; channel < n_channels; ++channel) {
alexbrandmeyer@678 224 FPType cplusplus = my_output.nap(ear, timepoint, channel);
alexbrandmeyer@678 225 FPType matlab = nap2[timepoint](channel);
alexbrandmeyer@678 226 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@678 227 cplusplus = my_output.bm(ear, timepoint, channel);
alexbrandmeyer@678 228 matlab = bm2[timepoint](channel);
alexbrandmeyer@678 229 ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
alexbrandmeyer@668 230 }
alexbrandmeyer@668 231 }
ronw@672 232 }
alexbrandmeyer@678 233
alexbrandmeyer@678 234 int main(int argc, char **argv) {
alexbrandmeyer@678 235 // This initializes the GoogleTest unit testing framework.
alexbrandmeyer@678 236 ::testing::InitGoogleTest(&argc, argv);
alexbrandmeyer@678 237 // This runs all of the tests that we've defined above.
alexbrandmeyer@678 238 return RUN_ALL_TESTS();
alexbrandmeyer@678 239 }