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@682
|
25 #include <vector>
|
ronw@684
|
26
|
ronw@684
|
27 #include "gtest/gtest.h"
|
alexbrandmeyer@685
|
28
|
alexbrandmeyer@685
|
29 #include "car.h"
|
alexbrandmeyer@685
|
30 #include "ihc.h"
|
alexbrandmeyer@685
|
31 #include "agc.h"
|
alexbrandmeyer@678
|
32 #include "carfac.h"
|
alexbrandmeyer@685
|
33 #include "common.h"
|
alexbrandmeyer@679
|
34
|
alexbrandmeyer@678
|
35 using std::vector;
|
alexbrandmeyer@678
|
36 using std::string;
|
alexbrandmeyer@678
|
37 using std::ifstream;
|
alexbrandmeyer@678
|
38 using std::ofstream;
|
alexbrandmeyer@678
|
39
|
alexbrandmeyer@679
|
40 // This is the 'test_data' subdirectory of aimc/carfac that specifies where to
|
alexbrandmeyer@679
|
41 // locate the text files produced by 'CARFAC_GenerateTestData.m' for comparing
|
alexbrandmeyer@679
|
42 // the ouput of the Matlab version of CARFAC with this C++ version.
|
alexbrandmeyer@679
|
43 static const char* kTestSourceDir= "./test_data/";
|
alexbrandmeyer@685
|
44 // Here we specify the level to which the output should match (2 decimals).
|
alexbrandmeyer@685
|
45 static const float kPrecisionLevel = 1.0e-2;
|
ronw@672
|
46
|
alexbrandmeyer@668
|
47 // Three helper functions are defined here for loading the test data generated
|
alexbrandmeyer@668
|
48 // by the Matlab version of CARFAC.
|
alexbrandmeyer@685
|
49 // This loads one-dimensional ArrayXs from single-column text files.
|
alexbrandmeyer@685
|
50 void WriteNAPOutput(CARFACOutput& output, const string filename, int ear) {
|
alexbrandmeyer@679
|
51 string fullfile = kTestSourceDir + filename;
|
alexbrandmeyer@678
|
52 ofstream ofile(fullfile.c_str());
|
alexbrandmeyer@685
|
53 int32_t num_timepoints = output.nap().size();
|
alexbrandmeyer@679
|
54 int channels = output.nap()[0][0].size();
|
alexbrandmeyer@678
|
55 if (ofile.is_open()) {
|
alexbrandmeyer@685
|
56 for (int32_t i = 0; i < num_timepoints; ++i) {
|
alexbrandmeyer@678
|
57 for (int j = 0; j < channels; ++j) {
|
alexbrandmeyer@679
|
58 ofile << output.nap()[i][ear](j);
|
alexbrandmeyer@678
|
59 if ( j < channels - 1) {
|
alexbrandmeyer@678
|
60 ofile << " ";
|
alexbrandmeyer@678
|
61 }
|
alexbrandmeyer@678
|
62 }
|
alexbrandmeyer@678
|
63 ofile << "\n";
|
alexbrandmeyer@678
|
64 }
|
alexbrandmeyer@678
|
65 }
|
alexbrandmeyer@678
|
66 ofile.close();
|
alexbrandmeyer@678
|
67 }
|
alexbrandmeyer@678
|
68
|
alexbrandmeyer@685
|
69 ArrayX LoadTestData(const string filename, const int number_points) {
|
alexbrandmeyer@679
|
70 string fullfile = kTestSourceDir + filename;
|
alexbrandmeyer@678
|
71 ifstream file(fullfile.c_str());
|
alexbrandmeyer@668
|
72 FPType myarray[number_points];
|
alexbrandmeyer@685
|
73 ArrayX output(number_points);
|
alexbrandmeyer@668
|
74 if (file.is_open()) {
|
alexbrandmeyer@668
|
75 for (int i = 0; i < number_points; ++i) {
|
alexbrandmeyer@668
|
76 file >> myarray[i];
|
alexbrandmeyer@668
|
77 output(i) = myarray[i];
|
alexbrandmeyer@668
|
78 }
|
alexbrandmeyer@668
|
79 }
|
alexbrandmeyer@678
|
80 file.close();
|
alexbrandmeyer@668
|
81 return output;
|
alexbrandmeyer@668
|
82 }
|
alexbrandmeyer@668
|
83
|
alexbrandmeyer@685
|
84 // This loads a vector of ArrayXs from multi-column text files.
|
alexbrandmeyer@685
|
85 vector<ArrayX> Load2dTestData(const string filename, const int rows,
|
alexbrandmeyer@668
|
86 const int columns) {
|
alexbrandmeyer@679
|
87 string fullfile = kTestSourceDir + filename;
|
alexbrandmeyer@678
|
88 ifstream file(fullfile.c_str());
|
alexbrandmeyer@668
|
89 FPType myarray[rows][columns];
|
alexbrandmeyer@685
|
90 vector<ArrayX> output;
|
alexbrandmeyer@668
|
91 output.resize(rows);
|
alexbrandmeyer@668
|
92 for (auto& timepoint : output) {
|
alexbrandmeyer@668
|
93 timepoint.resize(columns);
|
alexbrandmeyer@668
|
94 }
|
alexbrandmeyer@668
|
95 if (file.is_open()) {
|
alexbrandmeyer@668
|
96 for (int i = 0; i < rows; ++i) {
|
alexbrandmeyer@668
|
97 for (int j = 0; j < columns; ++j) {
|
alexbrandmeyer@668
|
98 file >> myarray[i][j];
|
alexbrandmeyer@668
|
99 output[i](j) = myarray[i][j];
|
alexbrandmeyer@668
|
100 }
|
alexbrandmeyer@668
|
101 }
|
alexbrandmeyer@668
|
102 }
|
alexbrandmeyer@678
|
103 file.close();
|
alexbrandmeyer@668
|
104 return output;
|
alexbrandmeyer@668
|
105 }
|
alexbrandmeyer@668
|
106
|
alexbrandmeyer@668
|
107 // This loads two dimensional vectors of audio data using data generated in
|
alexbrandmeyer@668
|
108 // Matlab using the wavread() function.
|
alexbrandmeyer@678
|
109 vector<vector<float>> Load2dAudioVector(string filename, int timepoints,
|
alexbrandmeyer@678
|
110 int channels) {
|
alexbrandmeyer@679
|
111 string fullfile = kTestSourceDir + filename;
|
alexbrandmeyer@678
|
112 ifstream file(fullfile.c_str());
|
alexbrandmeyer@678
|
113 vector<vector<float>> output;
|
alexbrandmeyer@668
|
114 output.resize(channels);
|
alexbrandmeyer@668
|
115 for (auto& channel : output) {
|
alexbrandmeyer@668
|
116 channel.resize(timepoints);
|
alexbrandmeyer@668
|
117 }
|
alexbrandmeyer@668
|
118 if (file.is_open()) {
|
alexbrandmeyer@668
|
119 for (int i = 0; i < timepoints; ++i) {
|
alexbrandmeyer@668
|
120 for (int j = 0; j < channels; ++j) {
|
alexbrandmeyer@668
|
121 file >> output[j][i];
|
alexbrandmeyer@668
|
122 }
|
alexbrandmeyer@668
|
123 }
|
alexbrandmeyer@668
|
124 }
|
alexbrandmeyer@678
|
125 file.close();
|
alexbrandmeyer@668
|
126 return output;
|
alexbrandmeyer@668
|
127 }
|
alexbrandmeyer@668
|
128
|
alexbrandmeyer@678
|
129 TEST(CARFACTest, Binaural_Output_test) {
|
alexbrandmeyer@685
|
130 int num_timepoints = 882;
|
alexbrandmeyer@685
|
131 int num_channels = 71;
|
alexbrandmeyer@685
|
132 int num_ears = 2;
|
alexbrandmeyer@679
|
133 string filename = "binaural_test_nap1.txt";
|
alexbrandmeyer@685
|
134 vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
135 filename = "binaural_test_bm1.txt";
|
alexbrandmeyer@685
|
136 vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
137 filename = "binaural_test_nap2.txt";
|
alexbrandmeyer@685
|
138 vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
139 filename = "binaural_test_bm2.txt";
|
alexbrandmeyer@685
|
140 vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
141 filename = "file_signal_binaural_test.txt";
|
alexbrandmeyer@685
|
142 vector<vector<float>> sound_data = Load2dAudioVector(filename, num_timepoints,
|
alexbrandmeyer@685
|
143 num_ears);
|
alexbrandmeyer@668
|
144 CARParams car_params;
|
alexbrandmeyer@678
|
145 IHCParams ihc_params;
|
alexbrandmeyer@678
|
146 AGCParams agc_params;
|
alexbrandmeyer@685
|
147 CARFAC mycf(num_ears, 22050, car_params, ihc_params, agc_params);
|
alexbrandmeyer@685
|
148 CARFACOutput my_output(true, false, true, false, false);
|
ronw@683
|
149 const bool kOpenLoop = false;
|
ronw@683
|
150 const int length = sound_data[0].size();
|
ronw@683
|
151 mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output);
|
alexbrandmeyer@678
|
152 filename = "cpp_nap_output_1_binaural_test.txt";
|
alexbrandmeyer@678
|
153 WriteNAPOutput(my_output, filename, 0);
|
alexbrandmeyer@678
|
154 filename = "cpp_nap_output_2_binaural_test.txt";
|
alexbrandmeyer@678
|
155 WriteNAPOutput(my_output, filename, 1);
|
alexbrandmeyer@678
|
156 int ear = 0;
|
alexbrandmeyer@678
|
157 int n_ch = 71;
|
alexbrandmeyer@685
|
158 for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
|
alexbrandmeyer@678
|
159 for (int channel = 0; channel < n_ch; ++channel) {
|
alexbrandmeyer@679
|
160 FPType cplusplus = my_output.nap()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
161 FPType matlab = nap1[timepoint](channel);
|
alexbrandmeyer@679
|
162 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@679
|
163 cplusplus = my_output.bm()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
164 matlab = bm1[timepoint](channel);
|
alexbrandmeyer@679
|
165 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@678
|
166 }
|
alexbrandmeyer@668
|
167 }
|
alexbrandmeyer@678
|
168 ear = 1;
|
alexbrandmeyer@685
|
169 for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
|
alexbrandmeyer@678
|
170 for (int channel = 0; channel < n_ch; ++channel) {
|
alexbrandmeyer@679
|
171 FPType cplusplus = my_output.nap()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
172 FPType matlab = nap2[timepoint](channel);
|
alexbrandmeyer@679
|
173 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@679
|
174 cplusplus = my_output.bm()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
175 matlab = bm2[timepoint](channel);
|
alexbrandmeyer@679
|
176 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@668
|
177 }
|
alexbrandmeyer@668
|
178 }
|
alexbrandmeyer@668
|
179 }
|
alexbrandmeyer@668
|
180
|
alexbrandmeyer@678
|
181 TEST(CARFACTest, Long_Output_test) {
|
alexbrandmeyer@685
|
182 int num_timepoints = 2000;
|
alexbrandmeyer@685
|
183 int num_channels = 83;
|
alexbrandmeyer@685
|
184 int num_ears = 2;
|
alexbrandmeyer@678
|
185 string filename = "long_test_nap1.txt";
|
alexbrandmeyer@685
|
186 vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
187 filename = "long_test_bm1.txt";
|
alexbrandmeyer@685
|
188 vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
189 filename = "long_test_nap2.txt";
|
alexbrandmeyer@685
|
190 vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
191 filename = "long_test_bm2.txt";
|
alexbrandmeyer@685
|
192 vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_channels);
|
alexbrandmeyer@678
|
193 filename = "file_signal_long_test.txt";
|
alexbrandmeyer@685
|
194 vector<vector<float>> sound_data = Load2dAudioVector(filename, num_timepoints,
|
alexbrandmeyer@685
|
195 num_ears);
|
alexbrandmeyer@668
|
196 CARParams car_params;
|
alexbrandmeyer@668
|
197 IHCParams ihc_params;
|
alexbrandmeyer@668
|
198 AGCParams agc_params;
|
alexbrandmeyer@685
|
199 CARFAC mycf(num_ears, 44100, car_params, ihc_params, agc_params);
|
alexbrandmeyer@685
|
200 CARFACOutput my_output(true, false, true, false, false);
|
ronw@683
|
201 const bool kOpenLoop = false;
|
ronw@683
|
202 const int length = sound_data[0].size();
|
ronw@683
|
203 mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output);
|
alexbrandmeyer@678
|
204 filename = "cpp_nap_output_1_long_test.txt";
|
alexbrandmeyer@678
|
205 WriteNAPOutput(my_output, filename, 0);
|
alexbrandmeyer@678
|
206 filename = "cpp_nap_output_2_long_test.txt";
|
alexbrandmeyer@678
|
207 WriteNAPOutput(my_output, filename, 1);
|
alexbrandmeyer@678
|
208 int ear = 0;
|
alexbrandmeyer@685
|
209 for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
|
alexbrandmeyer@685
|
210 for (int channel = 0; channel < num_channels; ++channel) {
|
alexbrandmeyer@679
|
211 FPType cplusplus = my_output.nap()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
212 FPType matlab = nap1[timepoint](channel);
|
alexbrandmeyer@679
|
213 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@679
|
214 cplusplus = my_output.bm()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
215 matlab = bm1[timepoint](channel);
|
alexbrandmeyer@679
|
216 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@678
|
217 }
|
alexbrandmeyer@668
|
218 }
|
alexbrandmeyer@678
|
219 ear = 1;
|
alexbrandmeyer@685
|
220 for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
|
alexbrandmeyer@685
|
221 for (int channel = 0; channel < num_channels; ++channel) {
|
alexbrandmeyer@679
|
222 FPType cplusplus = my_output.nap()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
223 FPType matlab = nap2[timepoint](channel);
|
alexbrandmeyer@679
|
224 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@679
|
225 cplusplus = my_output.bm()[timepoint][ear](channel);
|
alexbrandmeyer@678
|
226 matlab = bm2[timepoint](channel);
|
alexbrandmeyer@679
|
227 ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
|
alexbrandmeyer@668
|
228 }
|
alexbrandmeyer@668
|
229 }
|
alexbrandmeyer@685
|
230 } |