changeset 694:a43892e7e5ad

Clean up carfac_test.
author ronw@google.com
date Mon, 24 Jun 2013 16:35:21 +0000
parents 3d749a008b87
children 2e3672df5698
files trunk/carfac/carfac_test.cc trunk/carfac/common.h
diffstat 2 files changed, 106 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/carfac/carfac_test.cc	Tue Jun 18 18:07:02 2013 +0000
+++ b/trunk/carfac/carfac_test.cc	Mon Jun 24 16:35:21 2013 +0000
@@ -34,22 +34,18 @@
 #include "common.h"
 #include "ihc.h"
 
+using std::deque;
 using std::ifstream;
 using std::ofstream;
 using std::string;
 using std::vector;
 
-// This is the 'test_data' subdirectory of aimc/carfac that specifies where to
-// locate the text files produced by 'CARFAC_GenerateTestData.m' for comparing
-// the ouput of the Matlab version of CARFAC with this C++ version.
-static const char* kTestSourceDir= "./test_data/";
-// Here we specify the level to which the output should match (2 decimals).
-static const float kPrecisionLevel = 1.0e-2;
+// Location of the text files produced by 'CARFAC_GenerateTestData.m' for
+// comparing the ouput of the Matlab implementation with the one used here.
+static const char* kTestSourceDir = "./test_data/";
 
-// Three helper functions are defined here for loading the test data generated
-// by the Matlab version of CARFAC.
-// This loads one-dimensional ArrayXs from single-column text files.
-void WriteNAPOutput(const CARFACOutput& output, const string filename,
+// Writes the CARFAC NAP output to a text file.
+void WriteNAPOutput(const CARFACOutput& output, const string& filename,
                     int ear) {
   string fullfile = kTestSourceDir + filename;
   ofstream ofile(fullfile.c_str());
@@ -69,37 +65,27 @@
   ofile.close();
 }
 
-ArrayX LoadTestData(const string filename, const int number_points) {
+// Reads a size rows vector of size columns Container objects from a
+// multi-column text file generated by the Matlab version of CARFAC.
+template <typename Container = ArrayX, bool ColMajor = true>
+vector<Container> Load2dTestData(const string& filename, const int rows,
+                                 const int columns) {
   string fullfile = kTestSourceDir + filename;
   ifstream file(fullfile.c_str());
-  FPType myarray[number_points];
-  ArrayX 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 ArrayXs from multi-column text files.
-vector<ArrayX> Load2dTestData(const string filename, const int rows,
-                              const int columns) {
-  string fullfile = kTestSourceDir + filename;
-  ifstream file(fullfile.c_str());
-  FPType myarray[rows][columns];
-  vector<ArrayX> output;
-  output.resize(rows);
-  for (ArrayX& timepoint : output) {
-    timepoint.resize(columns);
+  vector<Container> output;
+  if (ColMajor) {
+    output.assign(rows, Container(columns));
+  } else {
+    output.assign(columns, Container(rows));
   }
   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];
+        if (ColMajor) {
+          file >> output[i][j];
+        } else {
+          file >> output[j][i];
+        }
       }
     }
   }
@@ -107,127 +93,98 @@
   return output;
 }
 
-// This loads two dimensional vectors of audio data using data generated in
-// Matlab using the wavread() function.
+// Reads a two dimensional vector of audio data from a text file
+// containing the output of the Matlab wavread() function.
 vector<vector<float>> Load2dAudioVector(string filename, int timepoints,
                                         int channels) {
-  string fullfile = kTestSourceDir + filename;
-  ifstream file(fullfile.c_str());
-  vector<vector<float>> output;
-  output.resize(channels);
-  for (auto& channel : output) {
-    channel.resize(timepoints);
+  return Load2dTestData<vector<float>, false>(filename, timepoints, channels);
+}
+
+class CARFACTest : public testing::Test {
+ protected:
+  deque<vector<ArrayX>> LoadTestData(
+      const string& basename, int n_timepoints, int n_ears, int n_ch) const {
+    deque<vector<ArrayX>> test_data(n_timepoints, vector<ArrayX>(n_ears));
+    for (int ear = 0; ear < n_ears; ++ear) {
+      string filename = basename + std::to_string(ear + 1) + ".txt";
+      vector<ArrayX> data = Load2dTestData(filename, n_timepoints, n_ch);
+      for (int i = 0; i < n_timepoints; ++i) {
+        test_data[i][ear] = data[i];
+      }
+    }
+    return test_data;
   }
-  if (file.is_open()) {
-    for (int i = 0; i < timepoints; ++i) {
-      for (int j = 0; j < channels; ++j) {
-        file >> output[j][i];
+
+  void AssertCARFACOutputNear(const deque<vector<ArrayX>>& expected,
+                              const deque<vector<ArrayX>>& actual,
+                              int n_timepoints, int n_ears, int n_ch) const {
+    for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
+      for (int ear = 0; ear < n_ears; ++ear) {
+        for (int channel = 0; channel < n_ch; ++channel) {
+          const float kPrecisionLevel = 1.0e-7;
+          ASSERT_NEAR(expected[timepoint][ear](channel),
+                      actual[timepoint][ear](channel),
+                      kPrecisionLevel);
+        }
       }
     }
   }
-  file.close();
-  return output;
+
+  CARParams car_params_;
+  IHCParams ihc_params_;
+  AGCParams agc_params_;
+};
+
+TEST_F(CARFACTest, BinauralData) {
+  const int n_timepoints = 882;
+  const int n_ears = 2;
+  const int n_ch = 71;
+  vector<vector<float>> sound_data =
+      Load2dAudioVector("file_signal_binaural_test.txt", n_timepoints, n_ears);
+  CARFAC carfac(n_ears, 22050, car_params_, ihc_params_, agc_params_);
+  CARFACOutput output(true, true, false, false);
+  const bool kOpenLoop = false;
+  const int length = sound_data[0].size();
+  carfac.RunSegment(sound_data, 0, length, kOpenLoop, &output);
+
+  // TODO(ronw): Don't unconditionally overwrite files that are
+  // checked in to the repository on every test run.
+  WriteNAPOutput(output, "cpp_nap_output_1_binaural_test.txt", 0);
+  WriteNAPOutput(output, "cpp_nap_output_2_binaural_test.txt", 1);
+
+  deque<vector<ArrayX>> expected_nap =
+      LoadTestData("binaural_test_nap", n_timepoints, n_ears, n_ch);
+  AssertCARFACOutputNear(expected_nap, output.nap(),
+                         n_timepoints, n_ears, n_ch);
+  deque<vector<ArrayX>> expected_bm =
+      LoadTestData("binaural_test_bm", n_timepoints, n_ears, n_ch);
+  AssertCARFACOutputNear(expected_bm, output.bm(),
+                         n_timepoints, n_ears, n_ch);
 }
 
-TEST(CARFACTest, Binaural_Output_test) {
-  int num_timepoints = 882;
-  int num_channels = 71;
-  int num_ears = 2;
-  string filename = "binaural_test_nap1.txt";
-  vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "binaural_test_bm1.txt";
-  vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "binaural_test_nap2.txt";
-  vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "binaural_test_bm2.txt";
-  vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "file_signal_binaural_test.txt";
-  vector<vector<float>> sound_data = Load2dAudioVector(filename, num_timepoints,
-                                                       num_ears);
-  CARParams car_params;
-  IHCParams ihc_params;
-  AGCParams agc_params;
-  CARFAC mycf(num_ears, 22050, car_params, ihc_params, agc_params);
-  CARFACOutput my_output(true, true, false, false);
+TEST_F(CARFACTest, LongBinauralData) {
+  const int n_timepoints = 2000;
+  const int n_ears = 2;
+  const int n_ch = 83;
+  vector<vector<float>> sound_data =
+      Load2dAudioVector("file_signal_long_test.txt", n_timepoints, n_ears);
+  CARFAC carfac(n_ears, 44100, car_params_, ihc_params_, agc_params_);
+  CARFACOutput output(true, true, false, false);
   const bool kOpenLoop = false;
   const int length = sound_data[0].size();
-  mycf.RunSegment(sound_data, 0, length, kOpenLoop, &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 < num_timepoints; ++timepoint) {
-    for (int channel = 0; channel < n_ch; ++channel) {
-      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
-      FPType matlab = nap1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-      cplusplus = my_output.bm()[timepoint][ear](channel);
-      matlab = bm1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-    }
-  }
-  ear = 1;
-  for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
-    for (int channel = 0; channel < n_ch; ++channel) {
-      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
-      FPType matlab = nap2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-      cplusplus = my_output.bm()[timepoint][ear](channel);
-      matlab = bm2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-    }
-  }
+  carfac.RunSegment(sound_data, 0, length, kOpenLoop, &output);
+
+  // TODO(ronw): Don't unconditionally overwrite files that are
+  // checked in to the repository on every test run.
+  WriteNAPOutput(output, "cpp_nap_output_1_long_test.txt", 0);
+  WriteNAPOutput(output, "cpp_nap_output_2_long_test.txt", 1);
+
+  deque<vector<ArrayX>> expected_nap =
+      LoadTestData("long_test_nap", n_timepoints, n_ears, n_ch);
+  AssertCARFACOutputNear(expected_nap, output.nap(),
+                         n_timepoints, n_ears, n_ch);
+  deque<vector<ArrayX>> expected_bm =
+      LoadTestData("long_test_bm", n_timepoints, n_ears, n_ch);
+  AssertCARFACOutputNear(expected_bm, output.bm(),
+                         n_timepoints, n_ears, n_ch);
 }
-
-TEST(CARFACTest, Long_Output_test) {
-  int num_timepoints = 2000;
-  int num_channels = 83;
-  int num_ears = 2;
-  string filename = "long_test_nap1.txt";
-  vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "long_test_bm1.txt";
-  vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "long_test_nap2.txt";
-  vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "long_test_bm2.txt";
-  vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_channels);
-  filename = "file_signal_long_test.txt";
-  vector<vector<float>> sound_data = Load2dAudioVector(filename, num_timepoints,
-                                                       num_ears);
-  CARParams car_params;
-  IHCParams ihc_params;
-  AGCParams agc_params;
-  CARFAC mycf(num_ears, 44100, car_params, ihc_params, agc_params);
-  CARFACOutput my_output(true, true, false, false);
-  const bool kOpenLoop = false;
-  const int length = sound_data[0].size();
-  mycf.RunSegment(sound_data, 0, length, kOpenLoop, &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 < num_timepoints; ++timepoint) {
-    for (int channel = 0; channel < num_channels; ++channel) {
-      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
-      FPType matlab = nap1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-      cplusplus = my_output.bm()[timepoint][ear](channel);
-      matlab = bm1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-    }
-  }
-  ear = 1;
-  for (int timepoint = 0; timepoint < num_timepoints; ++timepoint) {
-    for (int channel = 0; channel < num_channels; ++channel) {
-      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
-      FPType matlab = nap2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-      cplusplus = my_output.bm()[timepoint][ear](channel);
-      matlab = bm2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
-    }
-  }
-}
--- a/trunk/carfac/common.h	Tue Jun 18 18:07:02 2013 +0000
+++ b/trunk/carfac/common.h	Mon Jun 24 16:35:21 2013 +0000
@@ -30,7 +30,7 @@
 // The 'FPType' typedef is used to enable easy switching in precision level.
 // It's currently set to double for during the unit testing phase of the
 // project.
-typedef float FPType;
+typedef double FPType;
 // A typedef is used to define a one-dimensional Eigen array with the same
 // precision level as FPType.
 typedef Eigen::Array<FPType, Eigen::Dynamic, 1> ArrayX;