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