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@660
|
29 #include <Eigen/Core>
|
ronw@660
|
30
|
ronw@642
|
31 #include "gtest/gtest.h"
|
alexbrandmeyer@643
|
32
|
ronw@646
|
33 #include "agc.h"
|
alexbrandmeyer@643
|
34 #include "car.h"
|
ronw@647
|
35 #include "carfac_output.h"
|
alexbrandmeyer@643
|
36 #include "common.h"
|
ronw@646
|
37 #include "ihc.h"
|
ronw@660
|
38 #include "test_util.h"
|
alexbrandmeyer@637
|
39
|
ronw@652
|
40 using std::deque;
|
alexbrandmeyer@636
|
41 using std::ofstream;
|
ronw@646
|
42 using std::string;
|
ronw@646
|
43 using std::vector;
|
alexbrandmeyer@636
|
44
|
ronw@660
|
45 // Reads a two dimensional vector of audio data from a text file
|
ronw@660
|
46 // containing the output of the Matlab wavread() function.
|
ronw@660
|
47 vector<vector<float>> LoadAudio(const string& filename, int timepoints,
|
ronw@660
|
48 int num_channels) {
|
ronw@660
|
49 return LoadMatrix<vector<float>, false>(filename, timepoints, num_channels);
|
ronw@660
|
50 }
|
ronw@630
|
51
|
ronw@652
|
52 // Writes the CARFAC NAP output to a text file.
|
ronw@652
|
53 void WriteNAPOutput(const CARFACOutput& output, const string& filename,
|
ronw@646
|
54 int ear) {
|
ronw@656
|
55 string fullfile = kTestDataDir + filename;
|
alexbrandmeyer@636
|
56 ofstream ofile(fullfile.c_str());
|
ronw@660
|
57 const int kPrecision = 9;
|
ronw@660
|
58 ofile.precision(kPrecision);
|
alexbrandmeyer@643
|
59 int32_t num_timepoints = output.nap().size();
|
alexbrandmeyer@637
|
60 int channels = output.nap()[0][0].size();
|
ronw@660
|
61 Eigen::IOFormat ioformat(kPrecision, Eigen::DontAlignCols);
|
alexbrandmeyer@636
|
62 if (ofile.is_open()) {
|
alexbrandmeyer@643
|
63 for (int32_t i = 0; i < num_timepoints; ++i) {
|
ronw@660
|
64 ofile << output.nap()[i][ear].transpose().format(ioformat) << std::endl;
|
alexbrandmeyer@636
|
65 }
|
alexbrandmeyer@636
|
66 }
|
alexbrandmeyer@636
|
67 ofile.close();
|
alexbrandmeyer@636
|
68 }
|
alexbrandmeyer@636
|
69
|
ronw@652
|
70 class CARFACTest : public testing::Test {
|
ronw@652
|
71 protected:
|
ronw@654
|
72 deque<vector<ArrayX>> LoadTestData(const string& basename,
|
ronw@654
|
73 int num_samples,
|
ronw@654
|
74 int num_ears,
|
ronw@654
|
75 int num_channels) const {
|
ronw@654
|
76 deque<vector<ArrayX>> test_data(num_samples, vector<ArrayX>(num_ears));
|
ronw@654
|
77 for (int ear = 0; ear < num_ears; ++ear) {
|
ronw@652
|
78 string filename = basename + std::to_string(ear + 1) + ".txt";
|
ronw@660
|
79 vector<ArrayX> data = LoadMatrix(filename, num_samples, num_channels);
|
ronw@654
|
80 for (int i = 0; i < num_samples; ++i) {
|
ronw@652
|
81 test_data[i][ear] = data[i];
|
ronw@652
|
82 }
|
ronw@652
|
83 }
|
ronw@652
|
84 return test_data;
|
alexbrandmeyer@626
|
85 }
|
ronw@652
|
86
|
ronw@652
|
87 void AssertCARFACOutputNear(const deque<vector<ArrayX>>& expected,
|
ronw@652
|
88 const deque<vector<ArrayX>>& actual,
|
ronw@654
|
89 int num_samples,
|
ronw@660
|
90 int num_ears) const {
|
ronw@654
|
91 for (int timepoint = 0; timepoint < num_samples; ++timepoint) {
|
ronw@654
|
92 for (int ear = 0; ear < num_ears; ++ear) {
|
ronw@660
|
93 const float kPrecisionLevel = 1.0e-7;
|
ronw@660
|
94 ASSERT_TRUE(ArraysNear(expected[timepoint][ear],
|
ronw@660
|
95 actual[timepoint][ear],
|
ronw@660
|
96 kPrecisionLevel));
|
ronw@652
|
97 }
|
alexbrandmeyer@626
|
98 }
|
alexbrandmeyer@626
|
99 }
|
ronw@652
|
100
|
ronw@660
|
101 void RunCARFACAndCompareWithMatlab(const string& test_name,
|
ronw@660
|
102 int num_samples,
|
ronw@660
|
103 int num_ears,
|
ronw@660
|
104 int num_channels,
|
ronw@660
|
105 FPType sample_rate) const {
|
ronw@660
|
106 vector<vector<float>> sound_data =
|
ronw@660
|
107 LoadAudio(test_name + "-audio.txt", num_samples, num_ears);
|
ronw@660
|
108
|
ronw@660
|
109 CARParams car_params;
|
ronw@660
|
110 IHCParams ihc_params;
|
ronw@660
|
111 AGCParams agc_params;
|
ronw@660
|
112 CARFAC carfac(num_ears, sample_rate, car_params, ihc_params, agc_params);
|
ronw@660
|
113 CARFACOutput output(true, true, false, false);
|
ronw@660
|
114 const bool kOpenLoop = false;
|
ronw@660
|
115 const int length = sound_data[0].size();
|
ronw@660
|
116 carfac.RunSegment(sound_data, 0, length, kOpenLoop, &output);
|
ronw@660
|
117
|
ronw@660
|
118 // TODO(ronw): Don't unconditionally overwrite files that are
|
ronw@660
|
119 // checked in to the repository on every test run.
|
ronw@660
|
120 WriteNAPOutput(output, test_name + "-cpp-nap1.txt", 0);
|
ronw@660
|
121 WriteNAPOutput(output, test_name + "-cpp-nap2.txt", 1);
|
ronw@660
|
122
|
ronw@660
|
123 deque<vector<ArrayX>> expected_nap = LoadTestData(
|
ronw@660
|
124 test_name + "-matlab-nap", num_samples, num_ears, num_channels);
|
ronw@660
|
125 AssertCARFACOutputNear(expected_nap, output.nap(), num_samples, num_ears);
|
ronw@660
|
126 deque<vector<ArrayX>> expected_bm = LoadTestData(
|
ronw@660
|
127 test_name + "-matlab-bm", num_samples, num_ears, num_channels);
|
ronw@660
|
128 AssertCARFACOutputNear(expected_bm, output.bm(), num_samples, num_ears);
|
ronw@660
|
129 }
|
ronw@652
|
130 };
|
ronw@652
|
131
|
ronw@660
|
132 TEST_F(CARFACTest, MatchesMatlabOnBinauralData) {
|
ronw@654
|
133 const int kNumSamples = 882;
|
ronw@654
|
134 const int kNumEars = 2;
|
ronw@654
|
135 const int kNumChannels = 71;
|
ronw@660
|
136 const FPType kSampleRate = 22050.0;
|
ronw@660
|
137 RunCARFACAndCompareWithMatlab(
|
ronw@660
|
138 "binaural_test", kNumSamples, kNumEars, kNumChannels, kSampleRate);
|
alexbrandmeyer@626
|
139 }
|
alexbrandmeyer@626
|
140
|
ronw@660
|
141 TEST_F(CARFACTest, MatchesMatlabOnLongBinauralData) {
|
ronw@654
|
142 const int kNumSamples = 2000;
|
ronw@654
|
143 const int kNumEars = 2;
|
ronw@654
|
144 const int kNumChannels = 83;
|
ronw@660
|
145 const FPType kSampleRate = 44100.0;
|
ronw@660
|
146 RunCARFACAndCompareWithMatlab(
|
ronw@660
|
147 "long_test", kNumSamples, kNumEars, kNumChannels, kSampleRate);
|
alexbrandmeyer@626
|
148 }
|