diff carfac/ear.cc @ 637:efc5b1b54f63

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 27f2d9b76075
children d08c02c8e26f
line wrap: on
line diff
--- a/carfac/ear.cc	Mon May 27 16:36:54 2013 +0000
+++ b/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