Mercurial > hg > aimc
changeset 685:d0612798f6de
Additional changes to C++ CARFAC on the basis of ronw's comments on r289. Moved CARFAC::Design to CARFAC::CARFAC and CARFAC::Reset(), moved carfac_common.h to common.h, CARFACDetect to carfac_util.h/cc, FloatArray and Float2dArray to ArrayX and ArrayXX, improved variable naming, made a start on improved commenting documentation.
author | alexbrandmeyer |
---|---|
date | Tue, 04 Jun 2013 18:30:22 +0000 |
parents | 49af9a8d5a53 |
children | c3012b2943b2 |
files | trunk/carfac/SConstruct trunk/carfac/agc.h trunk/carfac/agc_coeffs.h trunk/carfac/agc_params.h trunk/carfac/agc_state.h trunk/carfac/car.h trunk/carfac/car_coeffs.h trunk/carfac/car_params.h trunk/carfac/car_state.h trunk/carfac/carfac.cc trunk/carfac/carfac.h trunk/carfac/carfac_common.cc trunk/carfac/carfac_common.h trunk/carfac/carfac_output.cc trunk/carfac/carfac_output.h trunk/carfac/carfac_test.cc trunk/carfac/carfac_util.cc trunk/carfac/carfac_util.h trunk/carfac/common.h trunk/carfac/ear.cc trunk/carfac/ear.h trunk/carfac/ihc.h trunk/carfac/ihc_coeffs.h trunk/carfac/ihc_params.h trunk/carfac/ihc_state.h trunk/carfac/sai.cc trunk/carfac/sai.h trunk/carfac/sai_test.cc |
diffstat | 28 files changed, 729 insertions(+), 876 deletions(-) [+] |
line wrap: on
line diff
--- a/trunk/carfac/SConstruct Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/SConstruct Tue Jun 04 18:30:22 2013 +0000 @@ -51,20 +51,15 @@ env.MergeFlags(['-std=c++11']) carfac_sources = [ - 'agc_coeffs.h', - 'agc_params.h', - 'agc_state.h', - 'car_coeffs.h', + 'agc.h', + 'car.h', + 'ihc.h', 'carfac.cc', - 'carfac_common.cc', + 'common.h', + 'carfac_util.h', 'carfac_output.cc', - 'car_params.h', - 'car_state.h', 'ear.cc', - 'ihc_coeffs.h', - 'ihc_params.h', - 'ihc_state.h', - 'sai.cc', + 'sai.cc' ] env.Library(target = 'carfac', source = carfac_sources)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/agc.h Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,80 @@ +// +// agc.h +// CARFAC Open Source C++ Library +// +// Created by Alex Brandmeyer on 5/30/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. + +#ifndef CARFAC_AGC_H +#define CARFAC_AGC_H + +#include <vector> +#include "common.h" + +struct AGCParams { + AGCParams() { + num_stages = 4; + agc_stage_gain = 2.0; + time_constants.resize(num_stages); + agc1_scales.resize(num_stages); + agc2_scales.resize(num_stages); + agc1_scales[0] = 1.0; + agc2_scales[0] = 1.65; + time_constants[0] = 0.002; + for (int i = 1; i < num_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 num_stages; + FPType agc_stage_gain; + FPType agc_mix_coeff; + std::vector<FPType> time_constants; + std::vector<int> decimation; + std::vector<FPType> agc1_scales; + std::vector<FPType> agc2_scales; +}; + +struct AGCCoeffs { + int num_agc_stages; + FPType agc_stage_gain; + FPType agc_epsilon; + int decimation; + FPType agc_pole_z1; + FPType agc_pole_z2; + int agc_spatial_iterations; + FPType agc_spatial_fir_left; + FPType agc_spatial_fir_mid; + FPType agc_spatial_fir_right; + int agc_spatial_n_taps; + FPType agc_mix_coeffs; + FPType agc_gain; + FPType detect_scale; + FPType decim; +}; + +struct AGCState { + ArrayX agc_memory; + ArrayX input_accum; + int decim_phase; +}; + +#endif // CARFAC_AGC_H \ No newline at end of file
--- a/trunk/carfac/agc_coeffs.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -// -// agc_coeffs.h -// 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. - -#ifndef CARFAC_AGC_COEFFS_H -#define CARFAC_AGC_COEFFS_H - -#include "carfac_common.h" - -struct AGCCoeffs { - int n_agc_stages; - FPType agc_stage_gain; - FPType agc_epsilon; - int decimation; - FPType agc_pole_z1; - FPType agc_pole_z2; - int agc_spatial_iterations; - FPType agc_spatial_fir_left; - FPType agc_spatial_fir_mid; - FPType agc_spatial_fir_right; - int agc_spatial_n_taps; - FPType agc_mix_coeffs; - FPType agc_gain; - FPType detect_scale; - FPType decim; -}; - -#endif // CARFAC_AGC_COEFFS_H \ No newline at end of file
--- a/trunk/carfac/agc_params.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -// -// agc_params.h -// 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. - -#ifndef CARFAC_AGC_PARAMS_H -#define CARFAC_AGC_PARAMS_H - -#include <vector> -#include "carfac_common.h" - -struct 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; - std::vector<FPType> time_constants; - std::vector<int> decimation; - std::vector<FPType> agc1_scales; - std::vector<FPType> agc2_scales; -}; - -#endif // CARFAC_AGC_PARAMS_H \ No newline at end of file
--- a/trunk/carfac/agc_state.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -// -// agc_state.h -// 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. - -#ifndef CARFAC_AGC_STATE_H -#define CARFAC_AGC_STATE_H - -#include "carfac_common.h" - -struct AGCState { - int n_ch; - FloatArray agc_memory; - FloatArray input_accum; - int decim_phase; -}; - -#endif // CARFAC_AGC_STATE_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/car.h Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,86 @@ +// +// car.h +// 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. + +#ifndef CARFAC_CAR_H +#define CARFAC_CAR_H + +#include "common.h" + +// A CARParams structure stores the necessary information needed by a CARFAC +// object to design the set of coefficients implementing 'The Cascade of +// Asymmetric Resonators' described in the chapter of the same name in Lyon's +// book "Human and Machine Hearing". +struct CARParams { + // 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. + FPType max_zeta; // This is the maximum damping factor in mid-freq channels. + FPType first_pole_theta; + FPType zero_ratio; // This is how far zero is above the pole. + FPType high_f_damping_compression; // A range from 0 to 1 to compress theta. + FPType erb_per_step; + FPType min_pole_hz; + FPType erb_break_freq; + FPType erb_q; +}; + +// The CAR coefficients are designed by the CARFAC::DesignCARCoeffs method, +// which is called during initial construction and resetting of a CARFAC object. +struct CARCoeffs { + FPType velocity_scale; + FPType v_offset; + ArrayX r1_coeffs; + ArrayX a0_coeffs; + ArrayX c0_coeffs; + ArrayX h_coeffs; + ArrayX g0_coeffs; + ArrayX zr_coeffs; +}; + + +struct CARState { + ArrayX z1_memory; + ArrayX z2_memory; + ArrayX za_memory; + ArrayX zb_memory; + ArrayX dzb_memory; + ArrayX zy_memory; + ArrayX g_memory; + ArrayX dg_memory; +}; + +#endif // CARFAC_CAR_H \ No newline at end of file
--- a/trunk/carfac/car_coeffs.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -// -// car_coeffs.h -// 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. - -#ifndef CARFAC_CAR_COEFFS_H -#define CARFAC_CAR_COEFFS_H - -#include "carfac_common.h" - -struct CARCoeffs { - int n_ch; - FPType velocity_scale; - FPType v_offset; - FloatArray r1_coeffs; - FloatArray a0_coeffs; - FloatArray c0_coeffs; - FloatArray h_coeffs; - FloatArray g0_coeffs; - FloatArray zr_coeffs; -}; - -#endif // CARFAC_CAR_COEFFS_H \ No newline at end of file
--- a/trunk/carfac/car_params.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -// -// car_params.h -// 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. - -#ifndef CARFAC_CAR_PARAMS_H -#define CARFAC_CAR_PARAMS_H - -#include "carfac_common.h" - -struct CARParams { - // 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. - FPType max_zeta; // This is the maximum damping factor in mid-freq channels. - FPType first_pole_theta; - FPType zero_ratio; // This is how far zero is above the pole. - FPType high_f_damping_compression; // A range from 0 to 1 to compress theta. - FPType erb_per_step; - FPType min_pole_hz; - FPType erb_break_freq; - FPType erb_q; -}; - -#endif // CARFAC_CAR_PARAMS_H \ No newline at end of file
--- a/trunk/carfac/car_state.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -// -// car_state.h -// 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. - -#ifndef CARFAC_CAR_STATE_H -#define CARFAC_CAR_STATE_H - -#include "carfac_common.h" - -struct CARState { - FloatArray z1_memory; - FloatArray z2_memory; - FloatArray za_memory; - FloatArray zb_memory; - FloatArray dzb_memory; - FloatArray zy_memory; - FloatArray g_memory; - FloatArray dg_memory; -}; - -#endif // CARFAC_CAR_STATE_H \ No newline at end of file
--- a/trunk/carfac/carfac.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/carfac.cc Tue Jun 04 18:30:22 2013 +0000 @@ -21,41 +21,49 @@ // limitations under the License. #include <assert.h> + #include "carfac.h" + using std::vector; -void CARFAC::Design(const int n_ears, const FPType fs, +CARFAC::CARFAC(const int num_ears, const FPType sample_rate, const CARParams& car_params, const IHCParams& ihc_params, const AGCParams& agc_params) { - n_ears_ = n_ears; - fs_ = fs; - ears_.resize(n_ears_); - n_ch_ = 0; - 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); + num_ears_ = num_ears; + sample_rate_ = sample_rate; + ears_.resize(num_ears_); + car_params_ = car_params; + ihc_params_ = ihc_params; + agc_params_ = agc_params; + Reset(); +} + +void CARFAC::Reset() { + num_channels_ = 0; + FPType pole_hz = car_params_.first_pole_theta * sample_rate_ / (2 * kPi); + while (pole_hz > car_params_.min_pole_hz) { + ++num_channels_; + 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 * kPi); - for (int ch = 0; ch < n_ch_; ++ch) { - pole_freqs_(ch) = pole_hz; - pole_hz = pole_hz - car_params.erb_per_step * - ERBHz(pole_hz, car_params.erb_break_freq, car_params.erb_q); + pole_freqs_.resize(num_channels_); + pole_hz = car_params_.first_pole_theta * sample_rate_ / (2 * kPi); + for (int channel = 0; channel < num_channels_; ++channel) { + pole_freqs_(channel) = pole_hz; + pole_hz = pole_hz - car_params_.erb_per_step * + ERBHz(pole_hz, car_params_.erb_break_freq, car_params_.erb_q); } max_channels_per_octave_ = log(2) / log(pole_freqs_(0) / pole_freqs_(1)); CARCoeffs car_coeffs; IHCCoeffs ihc_coeffs; std::vector<AGCCoeffs> agc_coeffs; - DesignCARCoeffs(car_params, fs, pole_freqs_, &car_coeffs); - DesignIHCCoeffs(ihc_params, fs, &ihc_coeffs); + DesignCARCoeffs(car_params_, sample_rate_, pole_freqs_, &car_coeffs); + DesignIHCCoeffs(ihc_params_, sample_rate_, &ihc_coeffs); // This code initializes the coefficients for each of the AGC stages. - DesignAGCCoeffs(agc_params, fs, &agc_coeffs); + DesignAGCCoeffs(agc_params_, sample_rate_, &agc_coeffs); // Once we have the coefficient structure we can design the ears. for (auto& ear : ears_) { - ear.InitEar(n_ch_, fs_, car_coeffs, ihc_coeffs, - agc_coeffs); + ear.Init(num_channels_, car_coeffs, ihc_coeffs, agc_coeffs); } } @@ -65,21 +73,22 @@ // A nested loop structure is used to iterate through the individual samples // for each ear (audio channel). 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) { + for (int32_t timepoint = 0; timepoint < length; ++timepoint) { + for (int audio_channel = 0; audio_channel < num_ears_; ++audio_channel) { // First we create a reference to the current Ear object. - Ear& ear = ears_[j]; + Ear& ear = ears_[audio_channel]; // This stores the audio sample currently being processed. - FPType input = sound_data[j][start+i]; + FPType input = sound_data[audio_channel][start + timepoint]; + // Now we apply the three stages of the model in sequence to the current // audio sample. ear.CARStep(input); ear.IHCStep(ear.car_out()); updated = ear.AGCStep(ear.ihc_out()); } - seg_output->StoreOutput(ears_); + seg_output->AppendOutput(ears_); if (updated) { - if (n_ears_ > 1) { + if (num_ears_ > 1) { CrossCouple(); } if (! open_loop) { @@ -90,19 +99,19 @@ } void CARFAC::CrossCouple() { - for (int stage = 0; stage < ears_[0].agc_nstages(); ++stage) { + for (int stage = 0; stage < ears_[0].agc_num_stages(); ++stage) { if (ears_[0].agc_decim_phase(stage) > 0) { break; } else { FPType mix_coeff = ears_[0].agc_mix_coeff(stage); if (mix_coeff > 0) { - FloatArray stage_state; - FloatArray this_stage_values = FloatArray::Zero(n_ch_); + ArrayX stage_state; + ArrayX this_stage_values = ArrayX::Zero(num_channels_); for (auto& ear : ears_) { stage_state = ear.agc_memory(stage); this_stage_values += stage_state; } - this_stage_values /= n_ears_; + this_stage_values /= num_ears_; for (auto& ear : ears_) { stage_state = ear.agc_memory(stage); ear.set_agc_memory(stage, stage_state + mix_coeff * @@ -115,7 +124,7 @@ void CARFAC::CloseAGCLoop() { for (auto& ear: ears_) { - FloatArray undamping = 1 - ear.agc_memory(0); + ArrayX undamping = 1 - ear.agc_memory(0); // This updates the target stage gain for the new damping. ear.set_dzb_memory((ear.zr_coeffs() * undamping - ear.zb_memory()) / ear.agc_decimation(0)); @@ -124,51 +133,52 @@ } } -void CARFAC::DesignCARCoeffs(const CARParams& car_params, const FPType fs, - const FloatArray& pole_freqs, +void CARFAC::DesignCARCoeffs(const CARParams& car_params, + const FPType sample_rate, + const ArrayX& pole_freqs, CARCoeffs* car_coeffs) { - n_ch_ = pole_freqs.size(); + num_channels_ = pole_freqs.size(); car_coeffs->velocity_scale = car_params.velocity_scale; car_coeffs->v_offset = car_params.v_offset; - car_coeffs->r1_coeffs.resize(n_ch_); - car_coeffs->a0_coeffs.resize(n_ch_); - car_coeffs->c0_coeffs.resize(n_ch_); - car_coeffs->h_coeffs.resize(n_ch_); - car_coeffs->g0_coeffs.resize(n_ch_); + car_coeffs->r1_coeffs.resize(num_channels_); + car_coeffs->a0_coeffs.resize(num_channels_); + car_coeffs->c0_coeffs.resize(num_channels_); + car_coeffs->h_coeffs.resize(num_channels_); + car_coeffs->g0_coeffs.resize(num_channels_); FPType f = car_params.zero_ratio * car_params.zero_ratio - 1.0; - FloatArray theta = pole_freqs * ((2.0 * kPi) / fs); + ArrayX theta = pole_freqs * ((2.0 * kPi) / sample_rate); car_coeffs->c0_coeffs = theta.sin(); car_coeffs->a0_coeffs = theta.cos(); FPType ff = car_params.high_f_damping_compression; - FloatArray x = theta / kPi; + ArrayX 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)); - FloatArray erb_freqs(n_ch_); - for (int ch=0; ch < n_ch_; ++ch) { - erb_freqs(ch) = ERBHz(pole_freqs(ch), car_params.erb_break_freq, + ArrayX erb_freqs(num_channels_); + for (int channel = 0; channel < num_channels_; ++channel) { + erb_freqs(channel) = ERBHz(pole_freqs(channel), car_params.erb_break_freq, car_params.erb_q); } - FloatArray min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) - + ArrayX min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) - min_zeta)); car_coeffs->zr_coeffs *= max_zeta - min_zetas; car_coeffs->h_coeffs = car_coeffs->c0_coeffs * f; - FloatArray relative_undamping = FloatArray::Ones(n_ch_); - FloatArray r = car_coeffs->r1_coeffs + (car_coeffs->zr_coeffs * + ArrayX relative_undamping = ArrayX::Ones(num_channels_); + ArrayX 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)); } -void CARFAC::DesignIHCCoeffs(const IHCParams& ihc_params, const FPType fs, - IHCCoeffs* ihc_coeffs) { +void CARFAC::DesignIHCCoeffs(const IHCParams& ihc_params, + const FPType sample_rate, IHCCoeffs* ihc_coeffs) { if (ihc_params.just_half_wave_rectify) { ihc_coeffs->just_half_wave_rectify = ihc_params.just_half_wave_rectify; } else { // This section calculates conductance values using two pre-defined scalars. - FloatArray x(1); + ArrayX x(1); FPType conduct_at_10, conduct_at_0; x(0) = 10.0; x = CARFACDetect(x); @@ -185,9 +195,9 @@ FPType current = 1 / (ri + r0); ihc_coeffs->cap1_voltage = 1 - (current * ri); ihc_coeffs->just_half_wave_rectify = false; - ihc_coeffs->lpf_coeff = 1 - exp( -1 / (ihc_params.tau_lpf * fs)); - ihc_coeffs->out1_rate = ro / (ihc_params.tau1_out * fs); - ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * fs); + ihc_coeffs->lpf_coeff = 1 - exp( -1 / (ihc_params.tau_lpf * sample_rate)); + ihc_coeffs->out1_rate = ro / (ihc_params.tau1_out * sample_rate); + ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * sample_rate); ihc_coeffs->one_capacitor = ihc_params.one_capacitor; ihc_coeffs->output_gain = 1 / (saturation_output - current); ihc_coeffs->rest_output = current / (saturation_output - current); @@ -204,11 +214,11 @@ ihc_coeffs->cap1_voltage = 1 - (current * r1); ihc_coeffs->cap2_voltage = ihc_coeffs->cap1_voltage - (current * r2); ihc_coeffs->just_half_wave_rectify = false; - ihc_coeffs->lpf_coeff = 1 - exp(-1 / (ihc_params.tau_lpf * fs)); - ihc_coeffs->out1_rate = 1 / (ihc_params.tau1_out * fs); - ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * fs); - ihc_coeffs->out2_rate = ro / (ihc_params.tau2_out * fs); - ihc_coeffs->in2_rate = 1 / (ihc_params.tau2_in * fs); + ihc_coeffs->lpf_coeff = 1 - exp(-1 / (ihc_params.tau_lpf * sample_rate)); + ihc_coeffs->out1_rate = 1 / (ihc_params.tau1_out * sample_rate); + ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * sample_rate); + ihc_coeffs->out2_rate = ro / (ihc_params.tau2_out * sample_rate); + ihc_coeffs->in2_rate = 1 / (ihc_params.tau2_in * sample_rate); ihc_coeffs->one_capacitor = ihc_params.one_capacitor; ihc_coeffs->output_gain = 1 / (saturation_output - current); ihc_coeffs->rest_output = current / (saturation_output - current); @@ -216,17 +226,18 @@ ihc_coeffs->rest_cap2 = ihc_coeffs->cap2_voltage; } } - ihc_coeffs->ac_coeff = 2 * kPi * ihc_params.ac_corner_hz / fs; + ihc_coeffs->ac_coeff = 2 * kPi * ihc_params.ac_corner_hz / sample_rate; } -void CARFAC::DesignAGCCoeffs(const AGCParams& agc_params, const FPType fs, +void CARFAC::DesignAGCCoeffs(const AGCParams& agc_params, + const FPType sample_rate, vector<AGCCoeffs>* agc_coeffs) { - agc_coeffs->resize(agc_params.n_stages); + agc_coeffs->resize(agc_params.num_stages); FPType previous_stage_gain = 0.0; FPType decim = 1.0; - for (int stage = 0; stage < agc_params.n_stages; ++stage) { + for (int stage = 0; stage < agc_params.num_stages; ++stage) { AGCCoeffs& agc_coeff = agc_coeffs->at(stage); - agc_coeff.n_agc_stages = agc_params.n_stages; + agc_coeff.num_agc_stages = agc_params.num_stages; agc_coeff.agc_stage_gain = agc_params.agc_stage_gain; vector<FPType> agc1_scales = agc_params.agc1_scales; vector<FPType> agc2_scales = agc_params.agc2_scales; @@ -238,8 +249,9 @@ FPType tau = time_constants[stage]; agc_coeff.decim = decim; agc_coeff.decim *= agc_coeff.decimation; - agc_coeff.agc_epsilon = 1 - exp((-1 * agc_coeff.decim) / (tau * fs)); - FPType n_times = tau * (fs / agc_coeff.decim); + agc_coeff.agc_epsilon = 1 - exp((-1 * agc_coeff.decim) / + (tau * sample_rate)); + FPType n_times = tau * (sample_rate / agc_coeff.decim); FPType delay = (agc2_scales[stage] - agc1_scales[stage]) / n_times; FPType spread_sq = (pow(agc1_scales[stage], 2) + pow(agc2_scales[stage], 2)) / n_times; @@ -317,10 +329,15 @@ agc_coeff.agc_spatial_fir_right = fir_right; total_dc_gain += pow(agc_coeff.agc_stage_gain, stage); agc_coeff.agc_mix_coeffs = stage == 0 ? 0 : mix_coeff / - (tau * (fs / agc_coeff.decim)); + (tau * (sample_rate / agc_coeff.decim)); agc_coeff.agc_gain = total_dc_gain; agc_coeff.detect_scale = 1 / total_dc_gain; previous_stage_gain = agc_coeff.agc_gain; decim = agc_coeff.decim; } } + +FPType CARFAC::ERBHz (const FPType center_frequency_hz, + const FPType erb_break_freq, const FPType erb_q) { + return (erb_break_freq + center_frequency_hz) / erb_q; +} \ No newline at end of file
--- a/trunk/carfac/carfac.h Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/carfac.h Tue Jun 04 18:30:22 2013 +0000 @@ -19,69 +19,76 @@ // 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. -// -// ***************************************************************************** -// Class: CARFAC -// ***************************************************************************** -// The CARFAC class is the top-level class implementing the CAR-FAC C++ model. -// A CARFAC object knows how to design its details from a modest set of -// parameters, and knows how to process sound signals to produce "neural -// activity patterns" (NAPs) which are contained in a CARFACOutput object. -// -// The 'Design' method is used to intialize the CARFAC model, and is passed -// a set of CAR, IHC and AGC parameters along with sound file information -// (channels and sample rate). -// -// The two methods 'Run' and 'RunSegment' are responsible for -// processing sound signals. These both take two dimensional Eigen float arrays -// (samples x channels) as arguments and return CARFACOutput objects. #ifndef CARFAC_CARFAC_H #define CARFAC_CARFAC_H #include <vector> -#include "carfac_common.h" -#include "car_params.h" -#include "car_coeffs.h" -#include "ihc_params.h" -#include "ihc_coeffs.h" -#include "agc_params.h" -#include "agc_coeffs.h" + +#include "common.h" +#include "carfac_util.h" +#include "car.h" +#include "ihc.h" +#include "agc.h" #include "ear.h" #include "carfac_output.h" +// This is the top-level class implementing the CAR-FAC C++ model. See the +// chapter entitled 'The CAR-FAC Digital Cochlear Model' in Lyon's book "Human +// and Machine Hearing" for an overview. +// +// A CARFAC object knows how to design its details from a modest set of +// parameters, and knows how to process sound signals to produce "neural +// activity patterns" (NAPs) which are stored in a CARFACOutput object during +// the call to CARFAC::RunSegment. class CARFAC { public: // The 'Design' method takes a set of CAR, IHC and AGC parameters along with // arguments specifying the number of 'ears' (audio file channels) and sample // rate. This initializes a vector of 'Ear' objects -- one for mono, two for - // stereo, or more. Each 'Ear' includes various sub-objects representing the - // parameters, designs (coeffs) ,and states of different parts of the CAR-FAC - // model. - void Design(const int n_ears, const FPType fs, const CARParams& car_params, - const IHCParams& ihc_params, const AGCParams& agc_params); - // The 'RunSegment' method processes individual sound segments + // stereo, or more. + CARFAC(const int num_ears, const FPType sample_rate, + const CARParams& car_params, const IHCParams& ihc_params, + const AGCParams& agc_params); + + // The 'RunSegment' method processes individual sound segments and stores the + // output of the model in a CARFACOutput object. void RunSegment(const std::vector<std::vector<float>>& sound_data, const int32_t start, const int32_t length, const bool open_loop, CARFACOutput* seg_output); + void Reset(); private: - void DesignCARCoeffs(const CARParams& car_params, const FPType fs, - const FloatArray& pole_freqs, CARCoeffs* car_coeffs); - void DesignIHCCoeffs(const IHCParams& ihc_params, const FPType fs, + // TODO (alexbrandmeyer): figure out why this breaks object initialization. + //DISALLOW_COPY_AND_ASSIGN(CARFAC); + void DesignCARCoeffs(const CARParams& car_params, const FPType sample_rate, + const ArrayX& pole_freqs, CARCoeffs* car_coeffs); + void DesignIHCCoeffs(const IHCParams& ihc_params, const FPType sample_rate, IHCCoeffs* ihc_coeffs); - void DesignAGCCoeffs(const AGCParams& agc_params, const FPType fs, + void DesignAGCCoeffs(const AGCParams& agc_params, const FPType sample_rate, std::vector<AGCCoeffs>* agc_coeffs); void CrossCouple(); void CloseAGCLoop(); + + // Function: ERBHz + // Auditory filter nominal Equivalent Rectangular Bandwidth + // Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138 + // See also the section 'Auditory Frequency Scales' of the chapter 'Acoustic + // Approaches and Auditory Influence' in "Human and Machine Hearing". + FPType ERBHz(const FPType center_frequency_hz, const FPType erb_break_freq, + const FPType erb_q); - int n_ears_; // This is the number of ears. - FPType fs_; // This is our current sample rate. - int n_ch_; // This is the number of channels in the CARFAC model. + CARParams car_params_; + IHCParams ihc_params_; + AGCParams agc_params_; + int num_ears_; + FPType sample_rate_; + int num_channels_; FPType max_channels_per_octave_; - // We store an array of Ear objects for mono/stereo/multichannel processing: + + // We store a vector of Ear objects for mono/stereo/multichannel processing: std::vector<Ear> ears_; - FloatArray pole_freqs_; + ArrayX pole_freqs_; }; -#endif // CARFAC_CARFAC_H +#endif // CARFAC_CARFAC_H \ No newline at end of file
--- a/trunk/carfac/carfac_common.cc Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -// -// carfac_common.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 "carfac_common.h" - -FPType ERBHz (const FPType cf_hz, const FPType erb_break_freq, - const FPType erb_q) { - return (erb_break_freq + cf_hz) / erb_q; -} - -FloatArray CARFACDetect (const FloatArray& x) { - FloatArray conductance, z, set; - FPType a = 0.175; - // This offsets the low-end tail into negative x territory. - // The parameter is adjusted for the book, to make the 20% DC response - // threshold at 0.1. - z = x + a; - // Zero is the final answer for many points. - conductance = (z < 0).select(0.0, (z*z*z) / (z*z*z + z*z + 0.1)); - return conductance; -} \ No newline at end of file
--- a/trunk/carfac/carfac_common.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -// -// carfac_common.h -// 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. -// -// ***************************************************************************** -// carfac_common.h -// ***************************************************************************** -// This file contains the base level definitions and includes used within the -// CARFAC C++ library. It also defines some low level functions which are used -// during the calculation of the various coefficient sets required by the model. -// -// The current implementation of the library is dependent on the use of the -// Eigen C++ library for linear algebra. Specifically, Eigen Arrays are used -// extensively for coefficient wise operations during both the design and run -// stages of the model. -// -// The 'FPType' typedef is specified in this file in order to enable quick -// switching between precision levels (i.e. float vs. double) throughout the -// library. The remainder of the code uses this type for specifying floating -// point scalars. -// -// An additional typedefs are defined for one dimensional arrays: FloatArray. -// These in turn make use of FPType so that the precision level across floating -// point data is consistent. -// -// The functions 'ERBHz' and 'CARFACDetect' are defined here, and are used -// during the design stage of a CARFAC model. - -#ifndef CARFAC_CARFAC_COMMON_H -#define CARFAC_CARFAC_COMMON_H - -// The Eigen library is used extensively for floating point arrays. -// For more information, see: http://eigen.tuxfamily.org -#include <Eigen/Dense> - -using namespace Eigen; - -// 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, Dynamic> Float2dArray; - -// 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 -FPType ERBHz(const FPType cf_hz, const FPType erb_break_freq, - const FPType erb_q); - -// Function CARFACDetect -// This returns the IHC detection nonilnearity function of the filter output -// values. This is here because it is called both in design and run phases. -FloatArray CARFACDetect(const FloatArray& x); - -#endif // CARFAC_CARFAC_COMMON_H
--- a/trunk/carfac/carfac_output.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/carfac_output.cc Tue Jun 04 18:30:22 2013 +0000 @@ -24,10 +24,9 @@ using std::vector; -void CARFACOutput::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) { - n_ears_ = n_ears; +CARFACOutput::CARFACOutput(const bool store_nap, const bool store_nap_decim, + const bool store_bm, const bool store_ohc, + const bool store_agc) { store_nap_ = store_nap; store_nap_decim_ = store_nap_decim; store_bm_ = store_bm; @@ -36,27 +35,27 @@ } -void CARFACOutput::StoreOutput(const vector<Ear>& ears) { +void CARFACOutput::AppendOutput(const vector<Ear>& ears) { if (store_nap_) { - nap_.push_back(vector<FloatArray>()); + nap_.push_back(vector<ArrayX>()); for (auto ear : ears) { nap_.back().push_back(ear.ihc_out()); } } if (store_ohc_) { - ohc_.push_back(vector<FloatArray>()); + ohc_.push_back(vector<ArrayX>()); for (auto ear : ears) { ohc_.back().push_back(ear.za_memory()); } } if (store_agc_) { - agc_.push_back(vector<FloatArray>()); + agc_.push_back(vector<ArrayX>()); for (auto ear : ears) { agc_.back().push_back(ear.zb_memory()); } } if (store_bm_) { - bm_.push_back(vector<FloatArray>()); + bm_.push_back(vector<ArrayX>()); for (auto ear : ears) { bm_.back().push_back(ear.zy_memory()); }
--- a/trunk/carfac/carfac_output.h Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/carfac_output.h Tue Jun 04 18:30:22 2013 +0000 @@ -19,53 +19,53 @@ // 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. -// -// ***************************************************************************** -// Class: CARFACOutput -// ***************************************************************************** -// The CARFACOutput object stores an array of EarOuput objects. It is meant as a -// container for the output generated by the CARFAC object's 'Run' and -// 'RunSegment' methods. Depending on the number of audio channels in the input -// data, the CARFACOutput will have 1 or more EarOutput obects, each of which -// contains a set of two dimensional float arrays (FloatArray2d) representing -// the neural activation patterns (NAPs) generated by the CARFAC model. -// -// The 'InitOutput' method is used to initialize the arrays in each of the -// EarOutput sub-objects once the target data dimensions ears (n_ears), channels -// (n_ch) and timepoints (n_tp) are known. #ifndef CARFAC_CARFAC_OUTPUT_H #define CARFAC_CARFAC_OUTPUT_H #include <deque> #include <vector> -#include "carfac_common.h" + +#include "common.h" #include "ear.h" +// A CARFACOutput object can store up to 5 different types of output from a +// CARFAC model, and is provided as an argument to the CARFAC::RunSegment +// method. class CARFACOutput { 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(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_; } + // The constructor takes five boolean values as arguments which indicate + // the portions of the CARFAC model's output to be stored. + CARFACOutput(const bool store_nap, const bool store_nap_decim, + const bool store_bm, const bool store_ohc, const bool store_agc); + + // The AppendOutput method is called on a sample by sample basis by the + // CARFAC::RunSegemtn method, appending a single frame of n_ears x n_channels + // data to the end of the individual data members selected for storage. + void AppendOutput(const std::vector<Ear>& ears); + const std::deque<std::vector<ArrayX>>& nap() const { return nap_; } + const std::deque<std::vector<ArrayX>>& bm() const { return bm_; } + const std::deque<std::vector<ArrayX>>& nap_decim() const { + return nap_decim_; } + const std::deque<std::vector<ArrayX>>& ohc() const { return ohc_; } + const std::deque<std::vector<ArrayX>>& agc() const { return agc_; } private: - int n_ears_; + // TODO (alexbrandmeyer): figure out why this breaks object initialization. + //DISALLOW_COPY_AND_ASSIGN(CARFACOutput); bool store_nap_; bool store_nap_decim_; 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_; + + // CARFAC outputs are stored in nested containers with dimensions: + // n_frames x n_ears x n_channels. + std::deque<std::vector<ArrayX>> nap_; + std::deque<std::vector<ArrayX>> nap_decim_; + std::deque<std::vector<ArrayX>> bm_; + std::deque<std::vector<ArrayX>> ohc_; + std::deque<std::vector<ArrayX>> agc_; }; #endif // CARFAC_CARFAC_OUTPUT_H \ No newline at end of file
--- a/trunk/carfac/carfac_test.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/carfac_test.cc Tue Jun 04 18:30:22 2013 +0000 @@ -25,10 +25,12 @@ #include <vector> #include "gtest/gtest.h" -#include "car_params.h" -#include "ihc_params.h" -#include "agc_params.h" + +#include "car.h" +#include "ihc.h" +#include "agc.h" #include "carfac.h" +#include "common.h" using std::vector; using std::string; @@ -39,19 +41,19 @@ // 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). -static const float kPrecisionLevel = 1.0e-7; +// Here we specify the level to which the output should match (2 decimals). +static const float kPrecisionLevel = 1.0e-2; // 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) { +// This loads one-dimensional ArrayXs from single-column text files. +void WriteNAPOutput(CARFACOutput& output, const string filename, int ear) { string fullfile = kTestSourceDir + filename; ofstream ofile(fullfile.c_str()); - int32_t n_timepoints = output.nap().size(); + int32_t num_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 (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) { @@ -64,11 +66,11 @@ ofile.close(); } -FloatArray LoadTestData(const string filename, const int number_points) { +ArrayX 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); + ArrayX output(number_points); if (file.is_open()) { for (int i = 0; i < number_points; ++i) { file >> myarray[i]; @@ -79,13 +81,13 @@ return output; } -// This loads a vector of FloatArrays from multi-column text files. -vector<FloatArray> Load2dTestData(const string filename, const int rows, +// 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<FloatArray> output; + vector<ArrayX> output; output.resize(rows); for (auto& timepoint : output) { timepoint.resize(columns); @@ -125,28 +127,25 @@ } TEST(CARFACTest, Binaural_Output_test) { - int n_timepoints = 882; - int n_channels = 71; - int n_ears = 2; + int num_timepoints = 882; + int num_channels = 71; + int num_ears = 2; string filename = "binaural_test_nap1.txt"; - vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels); filename = "binaural_test_bm1.txt"; - vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels); filename = "binaural_test_nap2.txt"; - vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels); filename = "binaural_test_bm2.txt"; - vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_channels); filename = "file_signal_binaural_test.txt"; - vector<vector<float>> sound_data = Load2dAudioVector(filename, n_timepoints, - n_ears); + vector<vector<float>> sound_data = Load2dAudioVector(filename, num_timepoints, + num_ears); CARParams car_params; IHCParams ihc_params; AGCParams agc_params; - CARFAC mycf; - mycf.Design(n_ears, 22050, car_params, ihc_params, - agc_params); - CARFACOutput my_output; - my_output.Init(n_ears, true, false, true, false, false); + CARFAC mycf(num_ears, 22050, car_params, ihc_params, agc_params); + CARFACOutput my_output(true, false, true, false, false); const bool kOpenLoop = false; const int length = sound_data[0].size(); mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output); @@ -156,7 +155,7 @@ WriteNAPOutput(my_output, filename, 1); int ear = 0; int n_ch = 71; - for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { + 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); @@ -167,7 +166,7 @@ } } ear = 1; - for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { + 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); @@ -180,28 +179,25 @@ } TEST(CARFACTest, Long_Output_test) { - int n_timepoints = 2000; - int n_channels = 83; - int n_ears = 2; + int num_timepoints = 2000; + int num_channels = 83; + int num_ears = 2; string filename = "long_test_nap1.txt"; - vector<FloatArray> nap1 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> nap1 = Load2dTestData(filename, num_timepoints, num_channels); filename = "long_test_bm1.txt"; - vector<FloatArray> bm1 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> bm1 = Load2dTestData(filename, num_timepoints, num_channels); filename = "long_test_nap2.txt"; - vector<FloatArray> nap2 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> nap2 = Load2dTestData(filename, num_timepoints, num_channels); filename = "long_test_bm2.txt"; - vector<FloatArray> bm2 = Load2dTestData(filename, n_timepoints, n_channels); + vector<ArrayX> bm2 = Load2dTestData(filename, num_timepoints, num_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, num_timepoints, + num_ears); CARParams car_params; IHCParams ihc_params; AGCParams agc_params; - CARFAC mycf; - mycf.Design(n_ears, 44100, car_params, ihc_params, - agc_params); - CARFACOutput my_output; - my_output.Init(n_ears, true, false, true, false, false); + CARFAC mycf(num_ears, 44100, car_params, ihc_params, agc_params); + CARFACOutput my_output(true, false, true, false, false); const bool kOpenLoop = false; const int length = sound_data[0].size(); mycf.RunSegment(sound_data, 0, length, kOpenLoop, &my_output); @@ -210,8 +206,8 @@ filename = "cpp_nap_output_2_long_test.txt"; WriteNAPOutput(my_output, filename, 1); int ear = 0; - for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { - for (int channel = 0; channel < n_channels; ++channel) { + 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); @@ -221,8 +217,8 @@ } } ear = 1; - for (int timepoint = 0; timepoint < n_timepoints; ++timepoint) { - for (int channel = 0; channel < n_channels; ++channel) { + 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); @@ -231,4 +227,4 @@ ASSERT_NEAR(cplusplus, matlab, kPrecisionLevel); } } -} +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/carfac_util.cc Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,35 @@ +// +// carfac_util.cc +// CARFAC Open Source C++ Library +// +// Created by Alex Brandmeyer on 6/3/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 "carfac_util.h" + +ArrayX CARFACDetect (const ArrayX& x) { + ArrayX conductance, z, set; + FPType a = 0.175; + // This offsets the low-end tail into negative x territory. + // The parameter is adjusted for the book, to make the 20% DC response + // threshold at 0.1. + z = x + a; + // Zero is the final answer for many points. + conductance = (z < 0).select(0.0, (z*z*z) / (z*z*z + z*z + 0.1)); + return conductance; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/carfac_util.h Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,33 @@ +// +// carfac_util.h +// 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. + +#ifndef CARFAC_CARFAC_UTIL_H +#define CARFAC_CARFAC_UTIL_H + +#include "common.h" + +// Function CARFACDetect +// This returns the IHC detection nonilnearity function of the filter output +// values. This is here because it is called both in design and run phases. +ArrayX CARFACDetect(const ArrayX& x); + +#endif // CARFAC_CARFAC_UTIL_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/common.h Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,48 @@ +// +// common.h +// 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. + +#ifndef CARFAC_COMMON_H +#define CARFAC_COMMON_H + +// This macro disallows the copy constructor and operator= functions. +// This should be used in the private: declarations for a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// The Eigen library is used extensively for floating point arrays. +// For more information, see: http://eigen.tuxfamily.org +#include <Eigen/Dense> + +// 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; +// 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; +typedef Eigen::Array<FPType, Eigen::Dynamic, Eigen::Dynamic> ArrayXX; + +// A fixed value of PI is defined throughout the project. +static const FPType kPi = 3.141592653589793238; + +#endif // CARFAC_COMMON_H \ No newline at end of file
--- a/trunk/carfac/ear.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/ear.cc Tue Jun 04 18:30:22 2013 +0000 @@ -23,48 +23,40 @@ #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::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. - n_ch_ = n_ch; +void Ear::Init(const int num_channels, const CARCoeffs& car_coeffs, + const IHCCoeffs& ihc_coeffs, + const std::vector<AGCCoeffs>& agc_coeffs) { + num_channels_ = num_channels; car_coeffs_ = car_coeffs; ihc_coeffs_ = ihc_coeffs; agc_coeffs_ = agc_coeffs; - // Once the coefficients have been determined, we can initialize the state - // variables that will be used during runtime. InitCARState(); InitIHCState(); InitAGCState(); } void Ear::InitCARState() { - car_state_.z1_memory.setZero(n_ch_); - car_state_.z2_memory.setZero(n_ch_); - car_state_.za_memory.setZero(n_ch_); + car_state_.z1_memory.setZero(num_channels_); + car_state_.z2_memory.setZero(num_channels_); + car_state_.za_memory.setZero(num_channels_); car_state_.zb_memory = car_coeffs_.zr_coeffs; - car_state_.dzb_memory.setZero(n_ch_); - car_state_.zy_memory.setZero(n_ch_); + car_state_.dzb_memory.setZero(num_channels_); + car_state_.zy_memory.setZero(num_channels_); car_state_.g_memory = car_coeffs_.g0_coeffs; - car_state_.dg_memory.setZero(n_ch_); + car_state_.dg_memory.setZero(num_channels_); } void Ear::InitIHCState() { - ihc_state_.ihc_accum = FloatArray::Zero(n_ch_); + ihc_state_.ihc_accum = ArrayX::Zero(num_channels_); if (! ihc_coeffs_.just_half_wave_rectify) { - ihc_state_.ac_coupler.setZero(n_ch_); - ihc_state_.lpf1_state.setConstant(n_ch_, ihc_coeffs_.rest_output); - ihc_state_.lpf2_state.setConstant(n_ch_, ihc_coeffs_.rest_output); + ihc_state_.ac_coupler.setZero(num_channels_); + ihc_state_.lpf1_state.setConstant(num_channels_, ihc_coeffs_.rest_output); + ihc_state_.lpf2_state.setConstant(num_channels_, ihc_coeffs_.rest_output); if (ihc_coeffs_.one_capacitor) { - ihc_state_.cap1_voltage.setConstant(n_ch_, ihc_coeffs_.rest_cap1); + ihc_state_.cap1_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap1); } else { - ihc_state_.cap1_voltage.setConstant(n_ch_, ihc_coeffs_.rest_cap1); - ihc_state_.cap2_voltage.setConstant(n_ch_, ihc_coeffs_.rest_cap2); + ihc_state_.cap1_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap1); + ihc_state_.cap2_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap2); } } } @@ -74,8 +66,8 @@ agc_state_.resize(n_agc_stages); for (auto& stage_state : agc_state_) { stage_state.decim_phase = 0; - stage_state.agc_memory.setZero(n_ch_); - stage_state.input_accum.setZero(n_ch_); + stage_state.agc_memory.setZero(num_channels_); + stage_state.input_accum.setZero(num_channels_); } } @@ -86,16 +78,15 @@ car_state_.zb_memory = car_state_.zb_memory + car_state_.dzb_memory; // This updates the nonlinear function of 'velocity' along with zA, which is // a delay of z2. - FloatArray nonlinear_fun(n_ch_); - FloatArray velocities = car_state_.z2_memory - car_state_.za_memory; + ArrayX nonlinear_fun(num_channels_); + ArrayX velocities = car_state_.z2_memory - car_state_.za_memory; OHCNonlinearFunction(velocities, &nonlinear_fun); // Here, zb_memory_ * nonlinear_fun is "undamping" delta r. - FloatArray r = car_coeffs_.r1_coeffs + (car_state_.zb_memory * - nonlinear_fun); + ArrayX r = car_coeffs_.r1_coeffs + (car_state_.zb_memory * nonlinear_fun); car_state_.za_memory = car_state_.z2_memory; // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs. - FloatArray z1 = r * ((car_coeffs_.a0_coeffs * car_state_.z1_memory) - - (car_coeffs_.c0_coeffs * car_state_.z2_memory)); + ArrayX 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)); @@ -103,11 +94,12 @@ // This section ripples the input-output path, to avoid delay... // It's the only part that doesn't get computed "in parallel": FPType in_out = input; - for (int ch = 0; ch < n_ch_; ch++) { - z1(ch) = z1(ch) + in_out; + for (int channel = 0; channel < num_channels_; channel++) { + z1(channel) = z1(channel) + in_out; // This performs the ripple, and saves the final channel outputs in zy. - in_out = car_state_.g_memory(ch) * (in_out + car_state_.zy_memory(ch)); - car_state_.zy_memory(ch) = in_out; + in_out = car_state_.g_memory(channel) * + (in_out + car_state_.zy_memory(channel)); + car_state_.zy_memory(channel) = in_out; } car_state_.z1_memory = z1; } @@ -115,8 +107,8 @@ // We start with a quadratic nonlinear function, and limit it via a // rational function. This makes the result go to zero at high // absolute velocities, so it will do nothing there. -void Ear::OHCNonlinearFunction(const FloatArray& velocities, - FloatArray* nonlinear_fun) { +void Ear::OHCNonlinearFunction(const ArrayX& velocities, + ArrayX* nonlinear_fun) { *nonlinear_fun = (1 + ((velocities * car_coeffs_.velocity_scale) + car_coeffs_.v_offset).square()).inverse(); } @@ -124,19 +116,19 @@ // 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 ac_diff = car_out - ihc_state_.ac_coupler; +void Ear::IHCStep(const ArrayX& car_out) { + ArrayX ac_diff = car_out - ihc_state_.ac_coupler; ihc_state_.ac_coupler = ihc_state_.ac_coupler + (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; + ArrayX output(num_channels_); + for (int channel = 0; channel < num_channels_; ++channel) { + FPType a = (ac_diff(channel) > 0.0) ? ac_diff(channel) : 0.0; + output(channel) = (a < 2) ? a : 2; } ihc_state_.ihc_out = output; } else { - FloatArray conductance = CARFACDetect(ac_diff); + ArrayX conductance = CARFACDetect(ac_diff); if (ihc_coeffs_.one_capacitor) { ihc_state_.ihc_out = conductance * ihc_state_.cap1_voltage; ihc_state_.cap1_voltage = ihc_state_.cap1_voltage - @@ -164,15 +156,15 @@ ihc_state_.ihc_accum += ihc_state_.ihc_out; } -bool Ear::AGCStep(const FloatArray& ihc_out) { +bool Ear::AGCStep(const ArrayX& ihc_out) { int stage = 0; - int n_stages = agc_coeffs_[0].n_agc_stages; - FPType detect_scale = agc_coeffs_[n_stages - 1].detect_scale; + int num_stages = agc_coeffs_[0].num_agc_stages; + FPType detect_scale = agc_coeffs_[num_stages - 1].detect_scale; bool updated = AGCRecurse(stage, detect_scale * ihc_out); return updated; } -bool Ear::AGCRecurse(const int stage, FloatArray agc_in) { +bool Ear::AGCRecurse(const int stage, ArrayX agc_in) { bool updated = true; const auto& agc_coeffs = agc_coeffs_[stage]; auto& agc_state = agc_state_[stage]; @@ -191,7 +183,7 @@ // decimated at the next stage. agc_in = agc_state.input_accum / decim; // This resets the accumulator. - agc_state.input_accum = FloatArray::Zero(n_ch_); + agc_state.input_accum = ArrayX::Zero(num_channels_); if (stage < (agc_coeffs_.size() - 1)) { // Now we recurse to evaluate the next stage(s). updated = AGCRecurse(stage + 1, agc_in); @@ -212,25 +204,27 @@ } void Ear::AGCSpatialSmooth(const AGCCoeffs& agc_coeffs, - FloatArray* stage_state) { - int n_iterations = agc_coeffs.agc_spatial_iterations; + ArrayX* stage_state) { + int num_iterations = agc_coeffs.agc_spatial_iterations; bool use_fir; - use_fir = (n_iterations < 4) ? true : false; + use_fir = (num_iterations < 4) ? true : false; if (use_fir) { 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_); + ArrayX ss_tap1(num_channels_); + ArrayX ss_tap2(num_channels_); + ArrayX ss_tap3(num_channels_); + ArrayX ss_tap4(num_channels_); 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.block(1, 0, num_channels_ - 1, 1) = + stage_state->block(0, 0, num_channels_ - 1, 1); + ss_tap2(num_channels_ - 1) = (*stage_state)(num_channels_ - 1); + ss_tap2.block(0, 0, num_channels_ - 1, 1) = + stage_state->block(1, 0, num_channels_ - 1, 1); switch (n_taps) { case 3: *stage_state = (fir_coeffs_left * ss_tap1) + @@ -241,12 +235,12 @@ // for the 5-tap case. 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); - ss_tap4.block(0, 0, n_ch_ - 2, 1) = - stage_state->block(2, 0, n_ch_ - 2, 1); + ss_tap3.block(2, 0, num_channels_ - 2, 1) = + stage_state->block(0, 0, num_channels_ - 2, 1); + ss_tap4(num_channels_ - 2) = (*stage_state)(num_channels_ - 1); + ss_tap4(num_channels_ - 1) = (*stage_state)(num_channels_ - 2); + ss_tap4.block(0, 0, num_channels_ - 2, 1) = + stage_state->block(2, 0, num_channels_ - 2, 1); *stage_state = (fir_coeffs_left * (ss_tap3 + ss_tap1)) + (fir_coeffs_mid * *stage_state) + (fir_coeffs_right * (ss_tap2 + ss_tap4)); @@ -261,30 +255,31 @@ } } -void Ear::AGCSmoothDoubleExponential(const FPType pole_z1, const FPType pole_z2, - FloatArray* stage_state) { - int32_t n_pts = stage_state->size(); +void Ear::AGCSmoothDoubleExponential(const FPType pole_z1, + const FPType pole_z2, + ArrayX* stage_state) { + int32_t num_points = 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) { + for (int i = num_points - 11; i < num_points; ++i) { input = (*stage_state)(i); state = state + (1 - pole_z1) * (input - state); } - for (int i = n_pts - 1; i > -1; --i) { + for (int i = num_points - 1; i > -1; --i) { input = (*stage_state)(i); state = state + (1 - pole_z2) * (input - state); } - for (int i = 0; i < n_pts; ++i) { + for (int i = 0; i < num_points; ++i) { input = (*stage_state)(i); state = state + (1 - pole_z1) * (input - state); (*stage_state)(i) = state; } } -FloatArray Ear::StageGValue(const FloatArray& undamping) { - FloatArray r = car_coeffs_.r1_coeffs + car_coeffs_.zr_coeffs * undamping; +ArrayX Ear::StageGValue(const ArrayX& undamping) { + ArrayX 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));
--- a/trunk/carfac/ear.h Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/ear.h Tue Jun 04 18:30:22 2013 +0000 @@ -24,84 +24,97 @@ #define CARFAC_EAR_H #include <vector> -#include "carfac_common.h" -#include "car_coeffs.h" -#include "ihc_coeffs.h" -#include "agc_coeffs.h" -#include "car_state.h" -#include "ihc_state.h" -#include "agc_state.h" +#include "common.h" +#include "carfac_util.h" +#include "car.h" +#include "agc.h" +#include "ihc.h" + +// The Ear object carries out the three steps of the CARFAC model on a single +// channel of audio data, and stores information about the CAR, IHC and AGC +// coefficients and states. class Ear { public: + // This is the primary initialization function that is called for each - // Ear object in the CARFAC 'Design' method. - void InitEar(const int n_ch, const FPType fs, - const CARCoeffs& car_coeffs, const IHCCoeffs& ihc_coeffs, - const std::vector<AGCCoeffs>& agc_coeffs); - // These three methods apply the different stages of the model in sequence - // to individual audio samples. + // Ear object by the CARFAC::Design method. + void Init(const int num_channels, const CARCoeffs& car_coeffs, + const IHCCoeffs& ihc_coeffs, + const std::vector<AGCCoeffs>& agc_coeffs); + + // These three methods apply the different steps of the model in sequence + // to individual audio samples during the call to CARFAC::RunSegment. void CARStep(const FPType input); - void IHCStep(const FloatArray& car_out); - bool AGCStep(const FloatArray& ihc_out); + void IHCStep(const ArrayX& car_out); + bool AGCStep(const ArrayX& 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; } + const ArrayX& za_memory() const { return car_state_.za_memory; } + const ArrayX& zb_memory() const { 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; } - const FloatArray& dzb_memory() { return car_state_.dzb_memory; } - // These accessor functions return CAR coefficients. - const FloatArray& zr_coeffs() { return car_coeffs_.zr_coeffs; } + const ArrayX& zy_memory() const { return car_state_.zy_memory; } + const ArrayX& car_out() const { return car_state_.zy_memory; } + const ArrayX& g_memory() const { return car_state_.g_memory; } + const ArrayX& ihc_out() const { return ihc_state_.ihc_out; } + const ArrayX& dzb_memory() const { return car_state_.dzb_memory; } + const ArrayX& zr_coeffs() const { return car_coeffs_.zr_coeffs; } + // These accessor functions return portions of the AGC state during the cross // coupling of the ears. - const int agc_nstages() { return agc_coeffs_.size(); } - const int agc_decim_phase(const int stage) { + const int agc_num_stages() const { return agc_coeffs_.size(); } + const int agc_decim_phase(const int stage) const { return agc_state_[stage].decim_phase; } - const FPType agc_mix_coeff(const int stage) { + const FPType agc_mix_coeff(const int stage) const { return agc_coeffs_[stage].agc_mix_coeffs; } - const FloatArray& agc_memory(const int stage) { + const ArrayX& agc_memory(const int stage) const { return agc_state_[stage].agc_memory; } - const int agc_decimation(const int stage) { + const int agc_decimation(const int stage) const { return agc_coeffs_[stage].decimation; } + // This returns the stage G value during the closing of the AGC loop. - FloatArray StageGValue(const FloatArray& undamping); + ArrayX StageGValue(const ArrayX& undamping); + // This function sets the AGC memory during the cross coupling stage. - void set_agc_memory(const int stage, const FloatArray& new_values) { + void set_agc_memory(const int stage, const ArrayX& new_values) { agc_state_[stage].agc_memory = new_values; } + // These are the setter functions for the CAR memory states. - void set_dzb_memory(const FloatArray& new_values) { + void set_dzb_memory(const ArrayX& new_values) { car_state_.dzb_memory = new_values; } - void set_dg_memory(const FloatArray& new_values) { + void set_dg_memory(const ArrayX& new_values) { car_state_.dg_memory = new_values; } private: - // These are the corresponding methods that initialize the model state - // variables before runtime using the model coefficients. + // TODO (alexbrandmeyer): figure out why this breaks object initialization. + //DISALLOW_COPY_AND_ASSIGN(Ear); + + // These methodsinitialize the model state variables prior to runtime. void InitIHCState(); void InitAGCState(); void InitCARState(); - // These are the various helper functions called during the model runtime. - void OHCNonlinearFunction(const FloatArray& velocities, - FloatArray* nonlinear_fun); - bool AGCRecurse(const int stage, FloatArray agc_in); - void AGCSpatialSmooth(const AGCCoeffs& agc_coeffs , FloatArray* stage_state); + + // These are the helper sub-functions called during the model runtime. + void OHCNonlinearFunction(const ArrayX& velocities, + ArrayX* nonlinear_fun); + bool AGCRecurse(const int stage, ArrayX agc_in); + void AGCSpatialSmooth(const AGCCoeffs& agc_coeffs , ArrayX* 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. + ArrayX* stage_state); + CARCoeffs car_coeffs_; + CARState car_state_; IHCCoeffs ihc_coeffs_; + IHCState ihc_state_; + + // The AGC coefficient and state variables are both stored in vectors + // containing one element for each stage (default = 4). std::vector<AGCCoeffs> agc_coeffs_; - CARState car_state_; - IHCState ihc_state_; std::vector<AGCState> agc_state_; - int n_ch_; + int num_channels_; }; #endif // CARFAC_EAR_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/carfac/ihc.h Tue Jun 04 18:30:22 2013 +0000 @@ -0,0 +1,76 @@ +// +// ihc.h +// CARFAC Open Source C++ Library +// +// Created by Alex Brandmeyer on 5/30/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. + +#ifndef CARFAC_IHC_H +#define CARFAC_IHC_H + +#include "common.h" + +struct 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; + FPType tau1_out; + FPType tau1_in; + FPType tau2_out; + FPType tau2_in; + FPType ac_corner_hz; +}; + +struct IHCCoeffs { + bool just_half_wave_rectify; + bool one_capacitor; + FPType lpf_coeff; + FPType out1_rate; + FPType in1_rate; + FPType out2_rate; + FPType in2_rate; + FPType output_gain; + FPType rest_output; + FPType rest_cap1; + FPType rest_cap2; + FPType ac_coeff; + FPType cap1_voltage; + FPType cap2_voltage; +}; + +struct IHCState { + ArrayX ihc_out; + ArrayX ihc_accum; + ArrayX cap1_voltage; + ArrayX cap2_voltage; + ArrayX lpf1_state; + ArrayX lpf2_state; + ArrayX ac_coupler; +}; + +#endif // CARFAC_IHC_H \ No newline at end of file
--- a/trunk/carfac/ihc_coeffs.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -// -// ihc_coeffs.h -// 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. - -#ifndef CARFAC_IHC_COEFFS_H -#define CARFAC_IHC_COEFFS_H - -#include "carfac_common.h" - -struct IHCCoeffs { - bool just_half_wave_rectify; - bool one_capacitor; - FPType lpf_coeff; - FPType out1_rate; - FPType in1_rate; - FPType out2_rate; - FPType in2_rate; - FPType output_gain; - FPType rest_output; - FPType rest_cap1; - FPType rest_cap2; - FPType ac_coeff; - FPType cap1_voltage; - FPType cap2_voltage; -}; - -#endif // CARFAC_IHC_COEFFS_H \ No newline at end of file
--- a/trunk/carfac/ihc_params.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -// -// ihc_params.h -// 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. - -#ifndef CARFAC_IHC_PARAMS_H -#define CARFAC_IHC_PARAMS_H - -#include "carfac_common.h" - -struct 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; - FPType tau1_out; - FPType tau1_in; - FPType tau2_out; - FPType tau2_in; - FPType ac_corner_hz; -}; - -#endif // CARFAC_IHC_PARAMS_H \ No newline at end of file
--- a/trunk/carfac/ihc_state.h Fri May 31 21:46:48 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -// -// ihc_state.h -// 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. - -#ifndef CARFAC_IHC_STATE_H -#define CARFAC_IHC_STATE_H - -#include "carfac_common.h" - -struct IHCState { - FloatArray ihc_out; - FloatArray ihc_accum; - FloatArray cap1_voltage; - FloatArray cap2_voltage; - FloatArray lpf1_state; - FloatArray lpf2_state; - FloatArray ac_coupler; -}; - -#endif // CARFAC_IHC_STATE_H \ No newline at end of file
--- a/trunk/carfac/sai.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/sai.cc Tue Jun 04 18:30:22 2013 +0000 @@ -35,8 +35,8 @@ .sin(); } -void SAI::RunSegment(const std::vector<FloatArray>& input, - Float2dArray* output_frame) { +void SAI::RunSegment(const std::vector<ArrayX>& input, + ArrayXX* output_frame) { assert(!input.empty() || input.size() <= params_.window_width && "Unexpected input size."); assert(input[0].size() == params_.n_ch && @@ -60,8 +60,8 @@ *output_frame = output_buffer_; } -void SAI::StabilizeSegment(const Float2dArray& input_buffer, - Float2dArray* output_buffer) const { +void SAI::StabilizeSegment(const ArrayXX& input_buffer, + ArrayXX* output_buffer) const { // Windows are always approximately 50% overlapped. float window_hop = params_.window_width / 2; int window_start = (input_buffer.cols() - params_.window_width) - @@ -72,14 +72,14 @@ for (int i = 0; i < params_.n_ch; ++i) { // TODO(ronw): Rename this here and in the Matlab code since the // input doesn't have to contain naps. - const FloatArray& nap_wave = input_buffer.row(i); + const ArrayX& nap_wave = input_buffer.row(i); // TODO(ronw): Smooth row. for (int w = 0; w < params_.n_window_pos; ++w) { int current_window_offset = w * window_hop; // Choose a trigger point. int trigger_time; - const FloatArray& trigger_window = + const ArrayX& trigger_window = nap_wave.segment(window_range_start + current_window_offset, params_.window_width); FPType peak_val = (trigger_window * window_).maxCoeff(&trigger_time);
--- a/trunk/carfac/sai.h Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/sai.h Tue Jun 04 18:30:22 2013 +0000 @@ -22,8 +22,7 @@ #include <vector> -// TODO(ronw): Rename this file to common.h -#include "carfac_common.h" +#include "common.h" // Design parameters for a single SAI. struct SAIParams { @@ -57,25 +56,25 @@ // // The input should have dimensionality of params_.window_width by // params_.n_ch. Inputs containing too few frames are zero-padded. - // FIXME: Float2DArray input type would be less awkward. - void RunSegment(const std::vector<FloatArray>& input, - Float2dArray* output_output_frame); + // FIXME: ArrayXX input type would be less awkward. + void RunSegment(const std::vector<ArrayX>& input, + ArrayXX* output_output_frame); private: // Process successive windows within input_buffer, choose trigger // points, and blend each window into output_buffer. - void StabilizeSegment(const Float2dArray& input_buffer, - Float2dArray* output_buffer) const; + void StabilizeSegment(const ArrayXX& input_buffer, + ArrayXX* output_buffer) const; SAIParams params_; // Window function to apply before selecting a trigger point. // Size: params_.window_width. - FloatArray window_; + ArrayX window_; // Buffer to store a large enough window of input frames to compute // a full SAI frame. Size: params_.n_ch by params_.buffer_width. - Float2dArray input_buffer_; + ArrayXX input_buffer_; // Output frame buffer. Size: params_.n_ch by params_.width. - Float2dArray output_buffer_; + ArrayXX output_buffer_; }; -#endif // CARFAC_SAI_H_ +#endif // CARFAC_SAI_H_ \ No newline at end of file
--- a/trunk/carfac/sai_test.cc Fri May 31 21:46:48 2013 +0000 +++ b/trunk/carfac/sai_test.cc Tue Jun 04 18:30:22 2013 +0000 @@ -27,15 +27,15 @@ using testing::Values; using std::vector; -vector<FloatArray> CreateZeroSegment(int n_ch, int length) { - vector<FloatArray> segment; +vector<ArrayX> CreateZeroSegment(int n_ch, int length) { + vector<ArrayX> segment; for (int i = 0; i < length; ++i) { - segment.push_back(FloatArray::Zero(n_ch)); + segment.push_back(ArrayX::Zero(n_ch)); } return segment; } -bool HasPeakAt(const Float2dArray& frame, int index) { +bool HasPeakAt(const ArrayXX& frame, int index) { if (index == 0) { return frame(index) > frame(index + 1); } else if (index == frame.size() - 1) { @@ -57,7 +57,7 @@ }; TEST_P(SAIPeriodicInputTest, SingleChannelPulseTrain) { - vector<FloatArray> segment = CreateZeroSegment(1, 38); + vector<ArrayX> segment = CreateZeroSegment(1, 38); for (int i = phase_; i < segment.size(); i += period_) { segment[i](0) = 1; } @@ -72,7 +72,7 @@ sai_params.n_window_pos = 2; SAI sai(sai_params); - Float2dArray sai_frame; + ArrayXX sai_frame; sai.RunSegment(segment, &sai_frame); // The output should have peaks at the same positions, regardless of