changeset 679:594b410c2aed

Sixth revision of Alex Brandmeyer's C++ implementation. Only small changes in response to Lyon's comments from r285. Note: I tried to use a consistent indentation with two spaces, but also preserving parenthetical structure to make reading the longer equations easier. Please advise if this is OK. Additional documentation and tests with non-standard parameter sets will be added in a subsequent revision.
author alexbrandmeyer
date Tue, 28 May 2013 15:54:54 +0000
parents 7f424c1a8b78
children be55786eeb04
files trunk/carfac/SConstruct trunk/carfac/agc_params.cc trunk/carfac/agc_params.h trunk/carfac/car_params.cc trunk/carfac/car_params.h trunk/carfac/carfac.cc trunk/carfac/carfac.h trunk/carfac/carfac_common.h trunk/carfac/carfac_output.cc trunk/carfac/carfac_output.h trunk/carfac/carfac_test.cc trunk/carfac/ear.cc trunk/carfac/ear.h trunk/carfac/ihc_params.cc trunk/carfac/ihc_params.h
diffstat 15 files changed, 204 insertions(+), 315 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/carfac/SConstruct	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/SConstruct	Tue May 28 15:54:54 2013 +0000
@@ -43,14 +43,14 @@
 import os
 
 carfac_sources = [
-        'carfac_common.cc',
-    'agc_params.cc',
+    'carfac_common.cc',
+    'agc_params.h',
     'agc_coeffs.h',
     'agc_state.h',		
     'carfac_output.cc',
-    'car_params.cc',
+    'car_params.h',
     'car_coeffs.h',
-    'ihc_params.cc',
+    'ihc_params.h',
     'car_state.h',
     'ihc_coeffs.h',
     'ihc_state.h',
--- a/trunk/carfac/agc_params.cc	Mon May 27 16:36:54 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-//
-//  agc_params.cc
-//  CARFAC Open Source C++ Library
-//
-//  Created by Alex Brandmeyer on 5/10/13.
-//
-// 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.
-
-#include "agc_params.h"
-
-// The default constructor for AGCParams initializes with the settings from
-// Lyon's book 'Human and Machine Hearing'
-AGCParams::AGCParams() {
-  n_stages_ = 4;
-  agc_stage_gain_ = 2.0;
-  time_constants_.resize(n_stages_);
-  agc1_scales_.resize(n_stages_);
-  agc2_scales_.resize(n_stages_);
-  agc1_scales_[0] = 1.0;
-  agc2_scales_[0] = 1.65;
-  time_constants_[0] = 0.002;
-  for (int i = 1; i < n_stages_; ++i) {
-    agc1_scales_[i] = agc1_scales_[i - 1] * sqrt(2.0);
-    agc2_scales_[i] = agc2_scales_[i - 1] * sqrt(2.0);
-    time_constants_[i] = time_constants_[i - 1] * 4.0;
-  }
-  decimation_ = {8, 2, 2, 2};
-  agc_mix_coeff_ = 0.5;
-}
\ No newline at end of file
--- a/trunk/carfac/agc_params.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/agc_params.h	Tue May 28 15:54:54 2013 +0000
@@ -26,7 +26,23 @@
 #include "carfac_common.h"
 
 struct AGCParams {
-  AGCParams();
+  AGCParams() {
+    n_stages_ = 4;
+    agc_stage_gain_ = 2.0;
+    time_constants_.resize(n_stages_);
+    agc1_scales_.resize(n_stages_);
+    agc2_scales_.resize(n_stages_);
+    agc1_scales_[0] = 1.0;
+    agc2_scales_[0] = 1.65;
+    time_constants_[0] = 0.002;
+    for (int i = 1; i < n_stages_; ++i) {
+      agc1_scales_[i] = agc1_scales_[i - 1] * sqrt(2.0);
+      agc2_scales_[i] = agc2_scales_[i - 1] * sqrt(2.0);
+      time_constants_[i] = time_constants_[i - 1] * 4.0;
+    }
+    decimation_ = {8, 2, 2, 2};
+    agc_mix_coeff_ = 0.5;
+  }
   int n_stages_;
   FPType agc_stage_gain_;
   FPType agc_mix_coeff_;
--- a/trunk/carfac/car_params.cc	Mon May 27 16:36:54 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-//
-//  car_params.cc
-//  CARFAC Open Source C++ Library
-//
-//  Created by Alex Brandmeyer on 5/10/13.
-//
-// 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.
-
-#include "car_params.h"
-
-CARParams::CARParams() {
-  velocity_scale_ = 0.1;
-  v_offset_ = 0.04;
-  min_zeta_ = 0.1;
-  max_zeta_ = 0.35;
-  first_pole_theta_ = 0.85 * PI;
-  zero_ratio_ = sqrt(2.0);
-  high_f_damping_compression_ = 0.5;
-  erb_per_step_ = 0.5;
-  min_pole_hz_ = 30;
-  erb_break_freq_ = 165.3;  // This is the Greenwood map's break frequency.
-  // This represents Glassberg and Moore's high-cf ratio.
-  erb_q_ = 1000/(24.7*4.37);
-};
\ No newline at end of file
--- a/trunk/carfac/car_params.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/car_params.h	Tue May 28 15:54:54 2013 +0000
@@ -26,7 +26,21 @@
 #include "carfac_common.h"
 
 struct CARParams {
-  CARParams();  // The constructor initializes using default parameter values.
+  // The constructor initializes using default parameter values.
+  CARParams() {
+    velocity_scale_ = 0.1;
+    v_offset_ = 0.04;
+    min_zeta_ = 0.1;
+    max_zeta_ = 0.35;
+    first_pole_theta_ = 0.85 * kPi;
+    zero_ratio_ = sqrt(2.0);
+    high_f_damping_compression_ = 0.5;
+    erb_per_step_ = 0.5;
+    min_pole_hz_ = 30;
+    erb_break_freq_ = 165.3;  // This is the Greenwood map's break frequency.
+    // This represents Glassberg and Moore's high-cf ratio.
+    erb_q_ = 1000/(24.7*4.37);
+  };
   FPType velocity_scale_; // This is used for the velocity nonlinearity.
   FPType v_offset_;  // The offset gives us quadratic part.
   FPType min_zeta_;  // This is the minimum damping factor in mid-freq channels.
--- a/trunk/carfac/carfac.cc	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac.cc	Tue May 28 15:54:54 2013 +0000
@@ -30,14 +30,14 @@
   fs_ = fs;
   ears_.resize(n_ears_);
   n_ch_ = 0;
-  FPType pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
+  FPType pole_hz = car_params.first_pole_theta_ * fs / (2 * kPi);
   while (pole_hz > car_params.min_pole_hz_) {
     ++n_ch_;
     pole_hz = pole_hz - car_params.erb_per_step_ *
     ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
   }
   pole_freqs_.resize(n_ch_);
-  pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
+  pole_hz = car_params.first_pole_theta_ * fs / (2 * kPi);
   for (int ch = 0; ch < n_ch_; ++ch) {
     pole_freqs_(ch) = pole_hz;
     pole_hz = pole_hz - car_params.erb_per_step_ *
@@ -53,7 +53,7 @@
   DesignAGCCoeffs(agc_params, fs, &agc_coeffs);
   // Once we have the coefficient structure we can design the ears.
   for (auto& ear : ears_) {
-    ear.DesignEar(n_ch_, fs_, car_coeffs, ihc_coeffs,
+    ear.InitEar(n_ch_, fs_, car_coeffs, ihc_coeffs,
                 agc_coeffs);
   }
 }
@@ -87,8 +87,6 @@
                         const bool open_loop, CARFACOutput* seg_output) {
   // A nested loop structure is used to iterate through the individual samples
   // for each ear (audio channel).
-  FloatArray car_out(n_ch_);
-  FloatArray ihc_out(n_ch_);
   bool updated;  // This variable is used by the AGC stage.
   for (int32_t i = 0; i < length; ++i) {
     for (int j = 0; j < n_ears_; ++j) {
@@ -98,11 +96,11 @@
       FPType input = sound_data[j][start+i];
       // Now we apply the three stages of the model in sequence to the current
       // audio sample.
-      ear.CARStep(input, &car_out);
-      ear.IHCStep(car_out, &ihc_out);
-      updated = ear.AGCStep(ihc_out);
+      ear.CARStep(input);
+      ear.IHCStep(ear.car_out());
+      updated = ear.AGCStep(ear.ihc_out());
     }
-    seg_output->StoreOutput(&ears_);
+    seg_output->StoreOutput(ears_);
     if (updated) {
       if (n_ears_ > 1) {
         CrossCouple();
@@ -161,12 +159,12 @@
   car_coeffs->h_coeffs_.resize(n_ch_);
   car_coeffs->g0_coeffs_.resize(n_ch_);
   FPType f = car_params.zero_ratio_ * car_params.zero_ratio_ - 1.0;
-  FloatArray theta = pole_freqs * ((2.0 * PI) / fs);
+  FloatArray theta = pole_freqs * ((2.0 * kPi) / fs);
   car_coeffs->c0_coeffs_ = theta.sin();
   car_coeffs->a0_coeffs_ = theta.cos();
   FPType ff = car_params.high_f_damping_compression_;
-  FloatArray x = theta / PI;
-  car_coeffs->zr_coeffs_ = PI * (x - (ff * (x*x*x)));
+  FloatArray x = theta / kPi;
+  car_coeffs->zr_coeffs_ = kPi * (x - (ff * (x*x*x)));
   FPType max_zeta = car_params.max_zeta_;
   FPType min_zeta = car_params.min_zeta_;
   car_coeffs->r1_coeffs_ = (1.0 - (car_coeffs->zr_coeffs_ * max_zeta));
@@ -183,9 +181,8 @@
   FloatArray r = car_coeffs->r1_coeffs_ + (car_coeffs->zr_coeffs_ *
                                            relative_undamping);
   car_coeffs->g0_coeffs_ = (1.0 - (2.0 * r * car_coeffs->a0_coeffs_) + (r*r)) /
-  (1 - (2 * r * car_coeffs->a0_coeffs_) +
-   (car_coeffs->h_coeffs_ * r * car_coeffs->c0_coeffs_)
-   + (r*r));
+    (1 - (2 * r * car_coeffs->a0_coeffs_) +
+    (car_coeffs->h_coeffs_ * r * car_coeffs->c0_coeffs_) + (r*r));
 }
 
 void CARFAC::DesignIHCCoeffs(const IHCParams& ihc_params, const FPType fs,
@@ -242,7 +239,7 @@
       ihc_coeffs->rest_cap2_ = ihc_coeffs->cap2_voltage_;
     }
   }
-  ihc_coeffs->ac_coeff_ = 2 * PI * ihc_params.ac_corner_hz_ / fs;
+  ihc_coeffs->ac_coeff_ = 2 * kPi * ihc_params.ac_corner_hz_ / fs;
 }
 
 void CARFAC::DesignAGCCoeffs(const AGCParams& agc_params, const FPType fs,
--- a/trunk/carfac/carfac.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac.h	Tue May 28 15:54:54 2013 +0000
@@ -78,4 +78,4 @@
   FloatArray pole_freqs_;
 };
 
-#endif
+#endif
\ No newline at end of file
--- a/trunk/carfac/carfac_common.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac_common.h	Tue May 28 15:54:54 2013 +0000
@@ -51,32 +51,27 @@
 // occur.
 // <math.h> is used during coefficient calculations and runtime operations.
 #include <math.h>
-// <vector> is used in place of 2d Eigen Arrays for the AGC memory
+//<vector> is used to store two dimensional data structures.
 #include <vector>
-// The Eigen library is used extensively for 1d and 2d floating point arrays.
+// The Eigen library is used extensively for floating point arrays.
 // For more information, see: http://eigen.tuxfamily.org
 #include <Eigen/Dense>
+
 using namespace Eigen;
 
-// One constant value is defined here, but see my TODO regarding style issues.
-// A fixed value of PI is defined throughout the project.
-// TODO alexbrandmeyer: verify that this is OK with Google Style.
-#define PI 3.141592653589793238
-
-// Three typedefs are used for enabling quick switching of precision and array
-// usage. These names may be changed based on the discussions being had within
-// the AIMC group.
 // 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 double FPType;
 // A typedef is used to define a one-dimensional Eigen array with the same
 // precision level as FPType.
-typedef Eigen::Array<FPType, Dynamic, 1> FloatArray; 
+typedef Eigen::Array<FPType, Dynamic, 1> FloatArray;
+
+// A fixed value of PI is defined throughout the project.
+static const FPType kPi = 3.141592653589793238;
 
 // Two helper functions are defined here for use by the different model stages
 // in calculating coeffecients and during model runtime.
-
 // Function: ERBHz
 // Auditory filter nominal Equivalent Rectangular Bandwidth
 // Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138
@@ -88,4 +83,4 @@
 // values.  This is here because it is called both in design and run phases.
 FloatArray CARFACDetect (const FloatArray& x);
 
-#endif
+#endif
\ No newline at end of file
--- a/trunk/carfac/carfac_output.cc	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac_output.cc	Tue May 28 15:54:54 2013 +0000
@@ -21,6 +21,7 @@
 // limitations under the License.
 
 #include "carfac_output.h"
+
 using std::vector;
 
 void CARFACOutput::Init(const int n_ears, const bool store_nap,
@@ -35,29 +36,28 @@
 
 }
 
-void CARFACOutput::StoreOutput(std::vector<Ear>* ears) {
+void CARFACOutput::StoreOutput(const vector<Ear>& ears) {
   if (store_nap_) {
     nap_.push_back(vector<FloatArray>());
-    for (auto& ear : *ears) {
+    for (auto ear : ears) {
       nap_.back().push_back(ear.ihc_out());
     }
   }
   if (store_ohc_) {
     ohc_.push_back(vector<FloatArray>());
-    for (auto& ear : *ears) {
+    for (auto ear : ears) {
       ohc_.back().push_back(ear.za_memory());
     }
   }
   if (store_agc_) {
     agc_.push_back(vector<FloatArray>());
-    for (auto& ear : *ears) {
-
+    for (auto ear : ears) {
       agc_.back().push_back(ear.zb_memory());
     }
   }
   if (store_bm_) {
     bm_.push_back(vector<FloatArray>());
-    for (auto& ear : *ears) {
+    for (auto ear : ears) {
       bm_.back().push_back(ear.zy_memory());
     }
   }
--- a/trunk/carfac/carfac_output.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac_output.h	Tue May 28 15:54:54 2013 +0000
@@ -44,17 +44,14 @@
  public:
   void Init(const int n_ears, const bool store_nap, const bool  store_nap_decim,
              const bool store_bm, const bool store_ohc, const bool store_agc);
-  void StoreOutput(std::vector<Ear>* ears);
-  FPType nap(const int ear, const int32_t timepoint, const int channel) {
-    return nap_[timepoint][ear](channel); }
-  FPType bm(const int ear, const int32_t timepoint, const int channel) {
-    return bm_[timepoint][ear](channel); }
-  std::deque<std::vector<FloatArray>> nap_;
-  std::deque<std::vector<FloatArray>> nap_decim_;
-  std::deque<std::vector<FloatArray>> bm_;
-  std::deque<std::vector<FloatArray>> ohc_;
-  std::deque<std::vector<FloatArray>> agc_;
- 
+  void StoreOutput(const std::vector<Ear>& ears);
+  // Here we define several acessors for the data members.
+  const std::deque<std::vector<FloatArray>>& nap() { return nap_;}
+  const std::deque<std::vector<FloatArray>>& bm() { return bm_; }
+  const std::deque<std::vector<FloatArray>>& nap_decim() { return nap_decim_;}
+  const std::deque<std::vector<FloatArray>>& ohc() { return ohc_; }
+  const std::deque<std::vector<FloatArray>>& agc() { return agc_;}
+
  private:
   int n_ears_;
   bool store_nap_;
@@ -62,6 +59,11 @@
   bool store_bm_;
   bool store_ohc_;
   bool store_agc_;
+  std::deque<std::vector<FloatArray>> nap_;
+  std::deque<std::vector<FloatArray>> nap_decim_;
+  std::deque<std::vector<FloatArray>> bm_;
+  std::deque<std::vector<FloatArray>> ohc_;
+  std::deque<std::vector<FloatArray>> agc_;
 };
 
 #endif
\ No newline at end of file
--- a/trunk/carfac/carfac_test.cc	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/carfac_test.cc	Tue May 28 15:54:54 2013 +0000
@@ -25,27 +25,31 @@
 // GoogleTest is now included for running unit tests
 #include <gtest/gtest.h>
 #include "carfac.h"
+
 using std::vector;
 using std::string;
 using std::ifstream;
 using std::ofstream;
 
-#define TEST_SRC_DIR "./test_data/"
+// 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 (7 decimals).
-#define PRECISION_LEVEL 1.0e-07
+static const float kPrecisionLevel = 1.0e-7;
 
 // Three helper functions are defined here for loading the test data generated
 // by the Matlab version of CARFAC.
 // This loads one-dimensional FloatArrays from single-column text files.
 void WriteNAPOutput(CARFACOutput output, const string filename, int ear) {
-  string fullfile = TEST_SRC_DIR + filename;
+  string fullfile = kTestSourceDir + filename;
   ofstream ofile(fullfile.c_str());
-  int32_t n_timepoints = output.nap_.size();
-  int channels = output.nap_[0][0].size();
+  int32_t n_timepoints = output.nap().size();
+  int channels = output.nap()[0][0].size();
   if (ofile.is_open()) {
     for (int32_t i = 0; i < n_timepoints; ++i) {
       for (int j = 0; j < channels; ++j) {
-        ofile << output.nap(ear, i, j);
+        ofile << output.nap()[i][ear](j);
         if ( j < channels - 1) {
           ofile << " ";
         }
@@ -56,8 +60,8 @@
   ofile.close();
 }
 
-FloatArray LoadTestData(const std::string filename, const int number_points) {
-  string fullfile = TEST_SRC_DIR + filename;
+FloatArray LoadTestData(const string filename, const int number_points) {
+  string fullfile = kTestSourceDir + filename;
   ifstream file(fullfile.c_str());
   FPType myarray[number_points];
   FloatArray output(number_points);
@@ -74,7 +78,7 @@
 // This loads a vector of FloatArrays from multi-column text files.
 vector<FloatArray> Load2dTestData(const string filename, const int rows,
                             const int columns) {
-  string fullfile = TEST_SRC_DIR + filename;
+  string fullfile = kTestSourceDir + filename;
   ifstream file(fullfile.c_str());
   FPType myarray[rows][columns];
   vector<FloatArray> output;
@@ -98,7 +102,7 @@
 // Matlab using the wavread() function.
 vector<vector<float>> Load2dAudioVector(string filename, int timepoints,
                                         int channels) {
-  string fullfile = TEST_SRC_DIR + filename;
+  string fullfile = kTestSourceDir + filename;
   ifstream file(fullfile.c_str());
   vector<vector<float>> output;
   output.resize(channels);
@@ -120,22 +124,17 @@
   int n_timepoints = 882;
   int n_channels = 71;
   int n_ears = 2;
-  std::string filename = "binaural_test_nap1.txt";
-  std::vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints,
-                                                n_channels);
+  string filename = "binaural_test_nap1.txt";
+  vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "binaural_test_bm1.txt";
-  std::vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints,
-                                               n_channels);
+  vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "binaural_test_nap2.txt";
-  std::vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints,
-                                                n_channels);
+  vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "binaural_test_bm2.txt";
-  std::vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints,
-                                               n_channels);
+  vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "file_signal_binaural_test.txt";
-  std::vector<std::vector<float>> sound_data = Load2dAudioVector(filename,
-                                                                 n_timepoints,
-                                                                 n_ears);
+  vector<vector<float>> sound_data = Load2dAudioVector(filename, n_timepoints,
+                                                       n_ears);
   CARParams car_params;
   IHCParams ihc_params;
   AGCParams agc_params;
@@ -153,23 +152,23 @@
   int n_ch = 71;
   for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
     for (int channel = 0; channel < n_ch; ++channel) {
-      FPType cplusplus = my_output.nap(ear, timepoint, channel);
+      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
       FPType matlab = nap1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
-      cplusplus = my_output.bm(ear, timepoint, channel);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
+      cplusplus = my_output.bm()[timepoint][ear](channel);
       matlab = bm1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
     }
   }
   ear = 1;
   for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
     for (int channel = 0; channel < n_ch; ++channel) {
-      FPType cplusplus = my_output.nap(ear, timepoint, channel);
+      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
       FPType matlab = nap2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
-      cplusplus = my_output.bm(ear, timepoint, channel);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
+      cplusplus = my_output.bm()[timepoint][ear](channel);
       matlab = bm2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
     }
   }
 }
@@ -179,21 +178,16 @@
   int n_channels = 83;
   int n_ears = 2;
   string filename = "long_test_nap1.txt";
-  vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints,
-                                                n_channels);
+  vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "long_test_bm1.txt";
-  vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints,
-                                               n_channels);
+  vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "long_test_nap2.txt";
-  vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints,
-                                                n_channels);
+  vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "long_test_bm2.txt";
-  vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints,
-                                               n_channels);
+  vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels);
   filename = "file_signal_long_test.txt";
-  vector<vector<float>> sound_data = Load2dAudioVector(filename,
-                                                                 n_timepoints,
-                                                                 n_ears);
+  vector<vector<float>> sound_data = Load2dAudioVector(filename, n_timepoints,
+                                                       n_ears);
   CARParams car_params;
   IHCParams ihc_params;
   AGCParams agc_params;
@@ -210,30 +204,23 @@
   int ear = 0;
   for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
     for (int channel = 0; channel < n_channels; ++channel) {
-      FPType cplusplus = my_output.nap(ear, timepoint, channel);
+      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
       FPType matlab = nap1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
-      cplusplus = my_output.bm(ear, timepoint, channel);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
+      cplusplus = my_output.bm()[timepoint][ear](channel);
       matlab = bm1[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
     }
   }
   ear = 1;
   for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) {
     for (int channel = 0; channel < n_channels; ++channel) {
-      FPType cplusplus = my_output.nap(ear, timepoint, channel);
+      FPType cplusplus = my_output.nap()[timepoint][ear](channel);
       FPType matlab = nap2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
-      cplusplus = my_output.bm(ear, timepoint, channel);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
+      cplusplus = my_output.bm()[timepoint][ear](channel);
       matlab = bm2[timepoint](channel);
-      ASSERT_NEAR(cplusplus, matlab, PRECISION_LEVEL);
+      ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel);
     }
   }
-}
-
-int main(int argc, char **argv) {
-  // This initializes the GoogleTest unit testing framework.
-  ::testing::InitGoogleTest(&argc, argv);
-  // This runs all of the tests that we've defined above.
-  return RUN_ALL_TESTS();
 }
\ No newline at end of file
--- a/trunk/carfac/ear.cc	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/ear.cc	Tue May 28 15:54:54 2013 +0000
@@ -21,16 +21,14 @@
 // limitations under the License.
 
 #include <assert.h>
-
 #include "ear.h"
 
 // The 'InitEar' function takes a set of model parameters and initializes the
 // design coefficients and model state variables needed for running the model
 // on a single audio channel.
-void Ear::DesignEar(const int n_ch, const FPType fs,
-                  const CARCoeffs& car_coeffs,
-                    const IHCCoeffs& ihc_coeffs,
-                    const std::vector<AGCCoeffs>& agc_coeffs) {
+void Ear::InitEar(const int n_ch, const FPType fs, const CARCoeffs& car_coeffs,
+                  const IHCCoeffs& ihc_coeffs,
+                  const std::vector<AGCCoeffs>& agc_coeffs) {
   // The first section of code determines the number of channels that will be
   // used in the model on the basis of the sample rate and the CAR parameters
   // that have been passed to this function.
@@ -81,7 +79,7 @@
   }
 }
 
-void Ear::CARStep(const FPType input, FloatArray* car_out) {
+void Ear::CARStep(const FPType input) {
   // This interpolates g.
   car_state_.g_memory_ = car_state_.g_memory_ + car_state_.dg_memory_;
   // This calculates the AGC interpolation state.
@@ -99,8 +97,8 @@
   FloatArray z1  = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) -
                         (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_));
   car_state_.z2_memory_ = r *
-  ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
-   (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
+    ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
+     (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
   car_state_.zy_memory_ = car_coeffs_.h_coeffs_ * car_state_.z2_memory_;
   // This section ripples the input-output path, to avoid delay...
   // It's the only part that doesn't get computed "in parallel":
@@ -112,7 +110,6 @@
     car_state_.zy_memory_(ch) = in_out;
   }
   car_state_.z1_memory_ = z1;
-  *car_out = car_state_.zy_memory_;
 }
 
 // We start with a quadratic nonlinear function, and limit it via a
@@ -127,46 +124,44 @@
 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
 // including the detection nonlinearity and either one or two capacitor state
 // variables.
-void Ear::IHCStep(const FloatArray& car_out, FloatArray* ihc_out) {
+void Ear::IHCStep(const FloatArray& car_out) {
   FloatArray ac_diff = car_out - ihc_state_.ac_coupler_;
   ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ +
-  (ihc_coeffs_.ac_coeff_ * ac_diff);
+    (ihc_coeffs_.ac_coeff_ * ac_diff);
   if (ihc_coeffs_.just_half_wave_rectify_) {
     FloatArray output(n_ch_);
     for (int ch = 0; ch < n_ch_; ++ch) {
       FPType a = (ac_diff(ch) > 0.0) ? ac_diff(ch) : 0.0;
       output(ch) = (a < 2) ? a : 2;
     }
-    *ihc_out = output;
+    ihc_state_.ihc_out_ = output;
   } else {
     FloatArray conductance = CARFACDetect(ac_diff);
     if (ihc_coeffs_.one_capacitor_) {
-      *ihc_out = conductance * ihc_state_.cap1_voltage_;
+      ihc_state_.ihc_out_ = conductance * ihc_state_.cap1_voltage_;
       ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
-                 (*ihc_out * ihc_coeffs_.out1_rate_) +
-                        ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
+        (ihc_state_.ihc_out_ * ihc_coeffs_.out1_rate_) +
+        ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
     } else {
-      *ihc_out = conductance * ihc_state_.cap2_voltage_;
+      ihc_state_.ihc_out_ = conductance * ihc_state_.cap2_voltage_;
       ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
-                          ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
-                          * ihc_coeffs_.out1_rate_) +
-                          ((1 - ihc_state_.cap1_voltage_) *
-                           ihc_coeffs_.in1_rate_);
+        ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
+         * ihc_coeffs_.out1_rate_) + ((1 - ihc_state_.cap1_voltage_) *
+                                      ihc_coeffs_.in1_rate_);
       ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ -
-                          (*ihc_out * ihc_coeffs_.out2_rate_) +
-                          ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
-                          * ihc_coeffs_.in2_rate_);
+        (ihc_state_.ihc_out_ * ihc_coeffs_.out2_rate_) +
+        ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
+         * ihc_coeffs_.in2_rate_);
     }
     // Here we smooth the output twice using a LPF.
-    *ihc_out *= ihc_coeffs_.output_gain_;
+    ihc_state_.ihc_out_ *= ihc_coeffs_.output_gain_;
     ihc_state_.lpf1_state_ += ihc_coeffs_.lpf_coeff_ *
-                              (*ihc_out - ihc_state_.lpf1_state_);
+      (ihc_state_.ihc_out_ - ihc_state_.lpf1_state_);
     ihc_state_.lpf2_state_ += ihc_coeffs_.lpf_coeff_ *
-                              (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_);
-    *ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
+      (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_);
+    ihc_state_.ihc_out_ = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
   }
-  ihc_state_.ihc_out_ = *ihc_out;
-  ihc_state_.ihc_accum_ += *ihc_out;
+  ihc_state_.ihc_accum_ += ihc_state_.ihc_out_;
 }
 
 bool Ear::AGCStep(const FloatArray& ihc_out) {
@@ -203,14 +198,12 @@
       // Afterwards we add its output to this stage input, whether it updated or
       // not.
       agc_in += agc_coeffs.agc_stage_gain_ *
-                agc_state_[stage + 1].agc_memory_;
+        agc_state_[stage + 1].agc_memory_;
     }
-    FloatArray agc_stage_state = agc_state.agc_memory_;
     // This performs a first-order recursive smoothing filter update, in time.
-    agc_stage_state += agc_coeffs.agc_epsilon_ *
-                      (agc_in - agc_stage_state);
-    agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state);
-    agc_state.agc_memory_ = agc_stage_state;
+    agc_state.agc_memory_ += agc_coeffs.agc_epsilon_ *
+      (agc_in - agc_state.agc_memory_);
+    AGCSpatialSmooth(agc_coeffs_[stage], &agc_state.agc_memory_);
     updated = true;
   } else {
     updated = false;
@@ -218,90 +211,81 @@
   return updated;
 }
 
-// TODO (alexbrandmeyer): figure out how to operate directly on stage_state.
-// Using a pointer breaks the () indexing of the Eigen arrays, but there must
-// be a way around this.
-FloatArray Ear::AGCSpatialSmooth(const int stage, FloatArray stage_state) {
-  int n_iterations = agc_coeffs_[stage].agc_spatial_iterations_;
+void Ear::AGCSpatialSmooth(const AGCCoeffs& agc_coeffs,
+                           FloatArray* stage_state) {
+  int n_iterations = agc_coeffs.agc_spatial_iterations_;
   bool use_fir;
   use_fir = (n_iterations < 4) ? true : false;
   if (use_fir) {
-    FPType fir_coeffs_left = agc_coeffs_[stage].agc_spatial_fir_left_;
-    FPType fir_coeffs_mid = agc_coeffs_[stage].agc_spatial_fir_mid_;
-    FPType fir_coeffs_right = agc_coeffs_[stage].agc_spatial_fir_right_;
+    FPType fir_coeffs_left = agc_coeffs.agc_spatial_fir_left_;
+    FPType fir_coeffs_mid = agc_coeffs.agc_spatial_fir_mid_;
+    FPType fir_coeffs_right = agc_coeffs.agc_spatial_fir_right_;
     FloatArray ss_tap1(n_ch_);
     FloatArray ss_tap2(n_ch_);
     FloatArray ss_tap3(n_ch_);
     FloatArray ss_tap4(n_ch_);
-    int n_taps = agc_coeffs_[stage].agc_spatial_n_taps_;
+    int n_taps = agc_coeffs.agc_spatial_n_taps_;
     // This initializes the first two taps of stage state, which are used for
     // both possible cases.
-    ss_tap1(0) = stage_state(0);
-    ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state.block(0, 0, n_ch_ - 1, 1);
-    ss_tap2(n_ch_ - 1) = stage_state(n_ch_ - 1);
-    ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1);
+    ss_tap1(0) = (*stage_state)(0);
+    ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state->block(0, 0, n_ch_ - 1, 1);
+    ss_tap2(n_ch_ - 1) = (*stage_state)(n_ch_ - 1);
+    ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state->block(1, 0, n_ch_ - 1, 1);
     switch (n_taps) {
       case 3:
-        stage_state = (fir_coeffs_left * ss_tap1) +
-        (fir_coeffs_mid * stage_state) +
-        (fir_coeffs_right * ss_tap2);
+        *stage_state = (fir_coeffs_left * ss_tap1) +
+          (fir_coeffs_mid * *stage_state) + (fir_coeffs_right * ss_tap2);
         break;
       case 5:
         // Now we initialize last two taps of stage state, which are only used
         // for the 5-tap case.
-        ss_tap3(0) = stage_state(0);
-        ss_tap3(1) = stage_state(1);
+        ss_tap3(0) = (*stage_state)(0);
+        ss_tap3(1) = (*stage_state)(1);
         ss_tap3.block(2, 0, n_ch_ - 2, 1) =
-        stage_state.block(0, 0, n_ch_ - 2, 1);
-        ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1);
-        ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2);
+          stage_state->block(0, 0, n_ch_ - 2, 1);
+        ss_tap4(n_ch_ - 2) = (*stage_state)(n_ch_ - 1);
+        ss_tap4(n_ch_ - 1) = (*stage_state)(n_ch_ - 2);
         ss_tap4.block(0, 0, n_ch_ - 2, 1) =
-        stage_state.block(2, 0, n_ch_ - 2, 1);
-        stage_state = (fir_coeffs_left * (ss_tap3 + ss_tap1)) +
-        (fir_coeffs_mid * stage_state) +
-        (fir_coeffs_right * (ss_tap2 + ss_tap4));
+        stage_state->block(2, 0, n_ch_ - 2, 1);
+        *stage_state = (fir_coeffs_left * (ss_tap3 + ss_tap1)) +
+          (fir_coeffs_mid * *stage_state) +
+          (fir_coeffs_right * (ss_tap2 + ss_tap4));
         break;
       default:
         assert(true && "Bad n_taps in AGCSpatialSmooth; should be 3 or 5.");
         break;
     }
   } else {
-    stage_state = AGCSmoothDoubleExponential(stage_state,
-                                             agc_coeffs_[stage].agc_pole_z1_,
-                                             agc_coeffs_[stage].agc_pole_z2_);
+    AGCSmoothDoubleExponential(agc_coeffs.agc_pole_z1_,
+                               agc_coeffs.agc_pole_z2_, stage_state);
   }
-  return stage_state;
 }
 
-// TODO (alexbrandmeyer): figure out how to operate directly on stage_state.
-// Same point as above for AGCSpatialSmooth.
-FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state,
-                                           const FPType pole_z1,
-                                           const FPType pole_z2) {
-  int32_t n_pts = stage_state.size();
+void Ear::AGCSmoothDoubleExponential(const FPType pole_z1,  const FPType pole_z2,
+                                FloatArray* stage_state) {
+  int32_t n_pts = stage_state->size();
   FPType input;
   FPType state = 0.0;
   // TODO (alexbrandmeyer): I'm assuming one dimensional input for now, but this
   // should be verified with Dick for the final version
-  for (int i = n_pts - 11; i < n_pts; ++i){
-    input = stage_state(i);
+  for (int i = n_pts - 11; i < n_pts; ++i) {
+    input = (*stage_state)(i);
     state = state + (1 - pole_z1) * (input - state);
   }
-  for (int i = n_pts - 1; i > -1; --i){
-    input = stage_state(i);
+  for (int i = n_pts - 1; i > -1; --i) {
+    input = (*stage_state)(i);
     state = state + (1 - pole_z2) * (input - state);
   }
-  for (int i = 0; i < n_pts; ++i){
-    input = stage_state(i);
+  for (int i = 0; i < n_pts; ++i) {
+    input = (*stage_state)(i);
     state = state + (1 - pole_z1) * (input - state);
-    stage_state(i) = state;
+    (*stage_state)(i) = state;
   }
-  return stage_state;
 }
 
 FloatArray Ear::StageGValue(const FloatArray& undamping) {
   FloatArray r = car_coeffs_.r1_coeffs_ + car_coeffs_.zr_coeffs_ * undamping;
   return (1 - 2 * r * car_coeffs_.a0_coeffs_ + (r * r)) /
-  (1 - 2 * r * car_coeffs_.a0_coeffs_ +
-   car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_ + (r * r));
-}
+    (1 - 2 * r * car_coeffs_.a0_coeffs_ + car_coeffs_.h_coeffs_ * r *
+     car_coeffs_.c0_coeffs_ + (r * r));
+}
\ No newline at end of file
--- a/trunk/carfac/ear.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/ear.h	Tue May 28 15:54:54 2013 +0000
@@ -31,19 +31,22 @@
  public:
   // This is the primary initialization function that is called for each
   // Ear object in the CARFAC 'Design' method.
-  void DesignEar(const int n_ch, const FPType fs,
+  void InitEar(const int n_ch, const FPType fs,
                const CARCoeffs& car_params, const IHCCoeffs& ihc_params,
                  const std::vector<AGCCoeffs>& agc_params);
   // These three methods apply the different stages of the model in sequence
   // to individual audio samples.
-  void CARStep(const FPType input, FloatArray* car_out);
-  void IHCStep(const FloatArray& car_out, FloatArray* ihc_out);
+  void CARStep(const FPType input);
+  void IHCStep(const FloatArray& car_out);
   bool AGCStep(const FloatArray& ihc_out);
   // These accessor functions return portions of the CAR state for storage in
   // the CAROutput structures.
   const FloatArray& za_memory() { return car_state_.za_memory_; }
   const FloatArray& zb_memory() { return car_state_.zb_memory_; }
+  // The zy_memory_ of the CARState is equivalent to the CAR output. A second
+  // accessor function is included for documentation purposes.
   const FloatArray& zy_memory() { return car_state_.zy_memory_; }
+  const FloatArray& car_out() { return car_state_.zy_memory_; }
   const FloatArray& g_memory() { return car_state_.g_memory_; }
   // This returns the IHC output for storage.
   const FloatArray& ihc_out() { return ihc_state_.ihc_out_; }
@@ -82,10 +85,9 @@
   void OHCNonlinearFunction(const FloatArray& velocities,
                             FloatArray* nonlinear_fun);
   bool AGCRecurse(const int stage, FloatArray agc_in);
-  FloatArray AGCSpatialSmooth(const int stage, FloatArray stage_state);
-  FloatArray AGCSmoothDoubleExponential(FloatArray stage_state,
-                                        const FPType pole_z1,
-                                        const FPType pole_z2);
+  void AGCSpatialSmooth(const AGCCoeffs& agc_coeffs , FloatArray* stage_state);
+  void AGCSmoothDoubleExponential(const FPType pole_z1, const FPType pole_z2,
+                                  FloatArray* stage_state);
   // These are the private data members that store the state and coefficient
   // information.
   CARCoeffs car_coeffs_;
--- a/trunk/carfac/ihc_params.cc	Mon May 27 16:36:54 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-//
-//  ihc_params.cc
-//  CARFAC Open Source C++ Library
-//
-//  Created by Alex Brandmeyer on 5/10/13.
-//
-// 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.
-
-#include "ihc_params.h"
-
-// The default constructor for IHCParams initializes with the settings from
-// Lyon's book 'Human and Machine Hearing'
-IHCParams::IHCParams() {
-  just_half_wave_rectify_ = false;
-  one_capacitor_ = true;
-  tau_lpf_ = 0.000080;
-  tau1_out_ = 0.0005;
-  tau1_in_ = 0.010;
-  tau2_out_ = 0.0025;
-  tau2_in_ = 0.005;
-  ac_corner_hz_ = 20.0;
-}
\ No newline at end of file
--- a/trunk/carfac/ihc_params.h	Mon May 27 16:36:54 2013 +0000
+++ b/trunk/carfac/ihc_params.h	Tue May 28 15:54:54 2013 +0000
@@ -26,7 +26,16 @@
 #include "carfac_common.h"
 
 struct IHCParams {
-  IHCParams();
+  IHCParams() {
+    just_half_wave_rectify_ = false;
+    one_capacitor_ = true;
+    tau_lpf_ = 0.000080;
+    tau1_out_ = 0.0005;
+    tau1_in_ = 0.010;
+    tau2_out_ = 0.0025;
+    tau2_in_ = 0.005;
+    ac_corner_hz_ = 20.0;
+  };
   bool just_half_wave_rectify_;
   bool one_capacitor_;
   FPType tau_lpf_;