changeset 702:7acfa23cde23

Factor out common test utility functions and clean up carfac_test.
author ronw@google.com
date Mon, 01 Jul 2013 19:02:32 +0000
parents bf89b122a683
children 2bd09040ecf0
files trunk/carfac/carfac_test.cc trunk/carfac/test_util.h
diffstat 2 files changed, 126 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/carfac/carfac_test.cc	Mon Jul 01 19:02:28 2013 +0000
+++ b/trunk/carfac/carfac_test.cc	Mon Jul 01 19:02:32 2013 +0000
@@ -26,6 +26,8 @@
 #include <string>
 #include <vector>
 
+#include <Eigen/Core>
+
 #include "gtest/gtest.h"
 
 #include "agc.h"
@@ -33,75 +35,38 @@
 #include "carfac_output.h"
 #include "common.h"
 #include "ihc.h"
+#include "test_util.h"
 
 using std::deque;
-using std::ifstream;
 using std::ofstream;
 using std::string;
 using std::vector;
 
-// 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* kTestDataDir = "./test_data/";
+// Reads a two dimensional vector of audio data from a text file
+// containing the output of the Matlab wavread() function.
+vector<vector<float>> LoadAudio(const string& filename, int timepoints,
+                                int num_channels) {
+  return LoadMatrix<vector<float>, false>(filename, timepoints, num_channels);
+}
 
 // Writes the CARFAC NAP output to a text file.
 void WriteNAPOutput(const CARFACOutput& output, const string& filename,
                     int ear) {
   string fullfile = kTestDataDir + filename;
   ofstream ofile(fullfile.c_str());
-  ofile.precision(9);
+  const int kPrecision = 9;
+  ofile.precision(kPrecision);
   int32_t num_timepoints = output.nap().size();
   int channels = output.nap()[0][0].size();
+  Eigen::IOFormat ioformat(kPrecision, Eigen::DontAlignCols);
   if (ofile.is_open()) {
     for (int32_t i = 0; i < num_timepoints; ++i) {
-      for (int j = 0; j < channels; ++j) {
-        ofile << output.nap()[i][ear](j);
-        if (j < channels - 1) {
-          ofile << " ";
-        }
-      }
-      ofile << "\n";
+      ofile << output.nap()[i][ear].transpose().format(ioformat) << std::endl;
     }
   }
   ofile.close();
 }
 
-// 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, int rows,
-                                 int columns) {
-  string fullfile = kTestDataDir + filename;
-  ifstream file(fullfile.c_str());
-  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) {
-        if (ColMajor) {
-          file >> output[i][j];
-        } else {
-          file >> output[j][i];
-        }
-      }
-    }
-  }
-  file.close();
-  return output;
-}
-
-// 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 num_channels) {
-  return Load2dTestData<vector<float>, false>(filename, timepoints,
-                                              num_channels);
-}
-
 class CARFACTest : public testing::Test {
  protected:
   deque<vector<ArrayX>> LoadTestData(const string& basename,
@@ -111,7 +76,7 @@
     deque<vector<ArrayX>> test_data(num_samples, vector<ArrayX>(num_ears));
     for (int ear = 0; ear < num_ears; ++ear) {
       string filename = basename + std::to_string(ear + 1) + ".txt";
-      vector<ArrayX> data = Load2dTestData(filename, num_samples, num_channels);
+      vector<ArrayX> data = LoadMatrix(filename, num_samples, num_channels);
       for (int i = 0; i < num_samples; ++i) {
         test_data[i][ear] = data[i];
       }
@@ -122,75 +87,62 @@
   void AssertCARFACOutputNear(const deque<vector<ArrayX>>& expected,
                               const deque<vector<ArrayX>>& actual,
                               int num_samples,
-                              int num_ears,
-                              int num_channels) const {
+                              int num_ears) const {
     for (int timepoint = 0; timepoint < num_samples; ++timepoint) {
       for (int ear = 0; ear < num_ears; ++ear) {
-        for (int channel = 0; channel < num_channels; ++channel) {
-          const float kPrecisionLevel = 1.0e-7;
-          ASSERT_NEAR(expected[timepoint][ear](channel),
-                      actual[timepoint][ear](channel),
-                      kPrecisionLevel);
+        const float kPrecisionLevel = 1.0e-7;
+        ASSERT_TRUE(ArraysNear(expected[timepoint][ear],
+                               actual[timepoint][ear],
+                               kPrecisionLevel));
         }
       }
-    }
   }
 
-  CARParams car_params_;
-  IHCParams ihc_params_;
-  AGCParams agc_params_;
+  void RunCARFACAndCompareWithMatlab(const string& test_name,
+                                     int num_samples,
+                                     int num_ears,
+                                     int num_channels,
+                                     FPType sample_rate) const {
+    vector<vector<float>> sound_data =
+        LoadAudio(test_name + "-audio.txt", num_samples, num_ears);
+
+    CARParams car_params;
+    IHCParams ihc_params;
+    AGCParams agc_params;
+    CARFAC carfac(num_ears, sample_rate, 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, test_name + "-cpp-nap1.txt", 0);
+    WriteNAPOutput(output, test_name + "-cpp-nap2.txt", 1);
+
+    deque<vector<ArrayX>> expected_nap = LoadTestData(
+        test_name + "-matlab-nap", num_samples, num_ears, num_channels);
+    AssertCARFACOutputNear(expected_nap, output.nap(), num_samples, num_ears);
+    deque<vector<ArrayX>> expected_bm = LoadTestData(
+        test_name + "-matlab-bm", num_samples, num_ears, num_channels);
+    AssertCARFACOutputNear(expected_bm, output.bm(), num_samples, num_ears);
+  }
 };
 
-TEST_F(CARFACTest, BinauralData) {
+TEST_F(CARFACTest, MatchesMatlabOnBinauralData) {
   const int kNumSamples = 882;
   const int kNumEars = 2;
   const int kNumChannels = 71;
-  vector<vector<float>> sound_data =
-      Load2dAudioVector("binaural_test-audio.txt", kNumSamples, kNumEars);
-  CARFAC carfac(kNumEars, 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, "binaural_test-cpp-nap1.txt", 0);
-  WriteNAPOutput(output, "binaural_test-cpp-nap2.txt", 1);
-
-  deque<vector<ArrayX>> expected_nap = LoadTestData(
-      "binaural_test-matlab-nap", kNumSamples, kNumEars, kNumChannels);
-  AssertCARFACOutputNear(expected_nap, output.nap(),
-                         kNumSamples, kNumEars, kNumChannels);
-  deque<vector<ArrayX>> expected_bm = LoadTestData(
-      "binaural_test-matlab-bm", kNumSamples, kNumEars, kNumChannels);
-  AssertCARFACOutputNear(expected_bm, output.bm(),
-                         kNumSamples, kNumEars, kNumChannels);
+  const FPType kSampleRate = 22050.0;
+  RunCARFACAndCompareWithMatlab(
+      "binaural_test", kNumSamples, kNumEars, kNumChannels, kSampleRate);
 }
 
-TEST_F(CARFACTest, LongBinauralData) {
+TEST_F(CARFACTest, MatchesMatlabOnLongBinauralData) {
   const int kNumSamples = 2000;
   const int kNumEars = 2;
   const int kNumChannels = 83;
-  vector<vector<float>> sound_data =
-      Load2dAudioVector("long_test-audio.txt", kNumSamples, kNumEars);
-  CARFAC carfac(kNumEars, 44100, 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, "long_test-cpp-nap1.txt", 0);
-  WriteNAPOutput(output, "long_test-cpp-nap2.txt", 1);
-
-  deque<vector<ArrayX>> expected_nap = LoadTestData(
-      "long_test-matlab-nap", kNumSamples, kNumEars, kNumChannels);
-  AssertCARFACOutputNear(expected_nap, output.nap(),
-                         kNumSamples, kNumEars, kNumChannels);
-  deque<vector<ArrayX>> expected_bm = LoadTestData(
-      "long_test-matlab-bm", kNumSamples, kNumEars, kNumChannels);
-  AssertCARFACOutputNear(expected_bm, output.bm(),
-                         kNumSamples, kNumEars, kNumChannels);
+  const FPType kSampleRate = 44100.0;
+  RunCARFACAndCompareWithMatlab(
+      "long_test", kNumSamples, kNumEars, kNumChannels, kSampleRate);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/carfac/test_util.h	Mon Jul 01 19:02:32 2013 +0000
@@ -0,0 +1,70 @@
+// Copyright 2013, Google, Inc.
+// Author: Ron Weiss <ronw@google.com>
+//
+// This C++ file is part of an implementation of Lyon's cochlear model:
+// "Cascade of Asymmetric Resonators with Fast-Acting Compression"
+// to supplement Lyon's upcoming book "Human and Machine Hearing"
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Shared test utilities.
+
+#ifndef CARFAC_TEST_UTIL_H
+#define CARFAC_TEST_UTIL_H
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <Eigen/Core>
+
+#include "common.h"
+
+// Location of the text files produced by 'CARFAC_GenerateTestData.m' for
+// comparing the ouput of the Matlab implementation with the C++ one.
+static const char* kTestDataDir = "./test_data/";
+
+// Reads a matrix (size rows vector of size columns Container objects)
+// from a text file written using the Matlab dlmwrite function.
+template <typename Container = ArrayX, bool ColMajor = true>
+std::vector<Container> LoadMatrix(const std::string& filename, int rows,
+                                  int columns) {
+  std::string fullfile = kTestDataDir + filename;
+  std::ifstream file(fullfile.c_str());
+  std::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) {
+        if (ColMajor) {
+          file >> output[i][j];
+        } else {
+          file >> output[j][i];
+        }
+      }
+    }
+  }
+  file.close();
+  return output;
+}
+
+bool ArraysNear(const ArrayX& expected, const ArrayX& actual,
+                double precision) {
+  return (expected - actual).cwiseAbs().maxCoeff() <= precision;
+}
+
+#endif  // CARFAC_TEST_UTIL_H