annotate carfac/carfac.cc @ 610:01986636257a

Second check-in of Alex Brandmeyer's C++ implementation of CARFAC. Addressed style issues and completed implementation of remaining functions. Still needs proper testing of the output stages against the MATLAB version, and runtime functions need improvements in efficiency.
author alexbrandmeyer
date Thu, 16 May 2013 17:33:23 +0000
parents aefe2ca0674f
children 0fbaf443ec82
rev   line source
alexbrandmeyer@609 1 //
alexbrandmeyer@609 2 // carfac.cc
alexbrandmeyer@609 3 // CARFAC Open Source C++ Library
alexbrandmeyer@609 4 //
alexbrandmeyer@609 5 // Created by Alex Brandmeyer on 5/10/13.
alexbrandmeyer@609 6 //
alexbrandmeyer@609 7 // This C++ file is part of an implementation of Lyon's cochlear model:
alexbrandmeyer@609 8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
alexbrandmeyer@609 9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
alexbrandmeyer@609 10 //
alexbrandmeyer@609 11 // Licensed under the Apache License, Version 2.0 (the "License");
alexbrandmeyer@609 12 // you may not use this file except in compliance with the License.
alexbrandmeyer@609 13 // You may obtain a copy of the License at
alexbrandmeyer@609 14 //
alexbrandmeyer@609 15 // http://www.apache.org/licenses/LICENSE-2.0
alexbrandmeyer@609 16 //
alexbrandmeyer@609 17 // Unless required by applicable law or agreed to in writing, software
alexbrandmeyer@609 18 // distributed under the License is distributed on an "AS IS" BASIS,
alexbrandmeyer@609 19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
alexbrandmeyer@609 20 // See the License for the specific language governing permissions and
alexbrandmeyer@609 21 // limitations under the License.
alexbrandmeyer@609 22
alexbrandmeyer@609 23 #include "carfac.h"
alexbrandmeyer@609 24 void CARFAC::Design(int n_ears, long fs, CARParams car_params,
alexbrandmeyer@610 25 IHCParams ihc_params, AGCParams agc_params) {
alexbrandmeyer@609 26 n_ears_ = n_ears;
alexbrandmeyer@609 27 fs_ = fs;
alexbrandmeyer@609 28 ears_ = new Ear[n_ears_];
alexbrandmeyer@610 29
alexbrandmeyer@610 30 n_ch_ = 0;
alexbrandmeyer@610 31 FPType pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
alexbrandmeyer@610 32 while (pole_hz > car_params.min_pole_hz_) {
alexbrandmeyer@610 33 n_ch_++;
alexbrandmeyer@610 34 pole_hz = pole_hz - car_params.erb_per_step_ *
alexbrandmeyer@610 35 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
alexbrandmeyer@609 36 }
alexbrandmeyer@610 37 FloatArray pole_freqs(n_ch_);
alexbrandmeyer@610 38 pole_hz = car_params.first_pole_theta_ * fs / (2 * PI);
alexbrandmeyer@610 39 for(int ch=0;ch < n_ch_; ch++) {
alexbrandmeyer@610 40 pole_freqs(ch) = pole_hz;
alexbrandmeyer@610 41 pole_hz = pole_hz - car_params.erb_per_step_ *
alexbrandmeyer@610 42 ERBHz(pole_hz, car_params.erb_break_freq_, car_params.erb_q_);
alexbrandmeyer@610 43 }
alexbrandmeyer@610 44 max_channels_per_octave_ = log(2) / log(pole_freqs(0) / pole_freqs(1));
alexbrandmeyer@610 45 // Once we have the basic information about the pole frequencies and the
alexbrandmeyer@610 46 // number of channels, we initialize the ear(s).
alexbrandmeyer@610 47 for (int i = 0; i < n_ears_; i++) {
alexbrandmeyer@610 48 ears_[i].InitEar(n_ch_, fs_, pole_freqs, car_params, ihc_params, agc_params);
alexbrandmeyer@610 49 }
alexbrandmeyer@609 50 }
alexbrandmeyer@609 51
alexbrandmeyer@610 52 CARFACOutput CARFAC::Run(FloatArray2d sound_data, bool open_loop) {
alexbrandmeyer@610 53 // We initialize one output object to store the final output.
alexbrandmeyer@609 54 CARFACOutput *output = new CARFACOutput();
alexbrandmeyer@610 55 // A second object is used to store the output for the individual segments.
alexbrandmeyer@609 56 CARFACOutput *seg_output = new CARFACOutput();
alexbrandmeyer@609 57 int n_audio_channels = int(sound_data.cols());
alexbrandmeyer@610 58 long seg_len = 441; // We use a fixed segment length for now.
alexbrandmeyer@609 59 long n_timepoints = sound_data.rows();
alexbrandmeyer@610 60 long n_segs = ceil((n_timepoints * 1.0) / seg_len);
alexbrandmeyer@609 61 output->InitOutput(n_audio_channels, n_ch_, n_timepoints);
alexbrandmeyer@609 62 seg_output->InitOutput(n_audio_channels, n_ch_, seg_len);
alexbrandmeyer@610 63 // These values store the start and endpoints for each segment
alexbrandmeyer@610 64 long start;
alexbrandmeyer@610 65 long length = seg_len;
alexbrandmeyer@610 66 // This section loops over the individual audio segments.
alexbrandmeyer@610 67 for (long i = 0; i < n_segs; i++) {
alexbrandmeyer@610 68 // For each segment we calculate the start point and the segment length.
alexbrandmeyer@610 69 start = i * seg_len;
alexbrandmeyer@610 70 if (i == n_segs - 1) {
alexbrandmeyer@610 71 // The last segment can be shorter than the rest.
alexbrandmeyer@610 72 length = n_timepoints - start;
alexbrandmeyer@609 73 }
alexbrandmeyer@610 74 // Once we've determined the start point and segment length, we run the
alexbrandmeyer@610 75 // CARFAC model on the current segment.
alexbrandmeyer@610 76 RunSegment(sound_data.block(start, 0, length, n_audio_channels),
alexbrandmeyer@610 77 seg_output, open_loop);
alexbrandmeyer@610 78 // Afterwards we merge the output for the current segment into the larger
alexbrandmeyer@610 79 // output structure for the entire audio file.
alexbrandmeyer@609 80 output->MergeOutput(*seg_output, start, length);
alexbrandmeyer@609 81 }
alexbrandmeyer@609 82 return *output;
alexbrandmeyer@609 83 }
alexbrandmeyer@609 84
alexbrandmeyer@610 85 void CARFAC::RunSegment(FloatArray2d sound_data, CARFACOutput *seg_output,
alexbrandmeyer@610 86 bool open_loop) {
alexbrandmeyer@610 87 // The number of timepoints is determined from the length of the audio
alexbrandmeyer@610 88 // segment.
alexbrandmeyer@609 89 long n_timepoints = sound_data.rows();
alexbrandmeyer@610 90 // The number of ears is equal to the number of audio channels. This could
alexbrandmeyer@610 91 // potentially be removed since we already know the n_ears_ during the design
alexbrandmeyer@610 92 // stage.
alexbrandmeyer@609 93 int n_ears = int(sound_data.cols());
alexbrandmeyer@610 94 // A nested loop structure is used to iterate through the individual samples
alexbrandmeyer@610 95 // for each ear (audio channel).
alexbrandmeyer@610 96 bool updated; // This variable is used by the AGC stage.
alexbrandmeyer@610 97 for (long i = 0; i < n_timepoints; i++) {
alexbrandmeyer@610 98 for (int j = 0; j < n_ears; j++) {
alexbrandmeyer@610 99 // This stores the audio sample currently being processed.
alexbrandmeyer@610 100 FPType input = sound_data(i, j);
alexbrandmeyer@610 101 // Now we apply the three stages of the model in sequence to the current
alexbrandmeyer@610 102 // audio sample.
alexbrandmeyer@609 103 FloatArray car_out = ears_[j].CARStep(input);
alexbrandmeyer@609 104 FloatArray ihc_out = ears_[j].IHCStep(car_out);
alexbrandmeyer@610 105 updated = ears_[j].AGCStep(ihc_out);
alexbrandmeyer@610 106 // These lines assign the output of the model for the current sample
alexbrandmeyer@610 107 // to the appropriate data members of the current ear in the output
alexbrandmeyer@610 108 // object.
alexbrandmeyer@610 109 seg_output->ears_[j].nap_.block(0, i, n_ch_, 1) = ihc_out;
alexbrandmeyer@610 110 // TODO alexbrandmeyer: Check with Dick to determine the C++ strategy for
alexbrandmeyer@610 111 // storing optional output structures.
alexbrandmeyer@610 112 seg_output->ears_[j].bm_.block(0, i, n_ch_, 1) = car_out;
alexbrandmeyer@610 113 seg_output->ears_[j].ohc_.block(0, 1, n_ch_, 1) =
alexbrandmeyer@610 114 ears_[j].ReturnZAMemory();
alexbrandmeyer@610 115 seg_output->ears_[j].agc_.block(0, i, n_ch_, 1) =
alexbrandmeyer@610 116 ears_[j].ReturnZBMemory();
alexbrandmeyer@610 117 }
alexbrandmeyer@610 118 if (updated && n_ears > 1) { CrossCouple(); }
alexbrandmeyer@610 119 if (! open_loop) { CloseAGCLoop(); }
alexbrandmeyer@610 120 }
alexbrandmeyer@610 121 }
alexbrandmeyer@610 122
alexbrandmeyer@610 123 void CARFAC::CrossCouple() {
alexbrandmeyer@610 124 for (int stage = 0; stage < ears_[0].ReturnAGCNStages(); stage++) {
alexbrandmeyer@610 125 if (ears_[0].ReturnAGCStateDecimPhase(stage) > 0) {
alexbrandmeyer@610 126 break;
alexbrandmeyer@610 127 } else {
alexbrandmeyer@610 128 FPType mix_coeff = ears_[0].ReturnAGCMixCoeff(stage);
alexbrandmeyer@610 129 if (mix_coeff > 0) {
alexbrandmeyer@610 130 FloatArray stage_state;
alexbrandmeyer@610 131 FloatArray this_stage_values = FloatArray::Zero(n_ch_);
alexbrandmeyer@610 132 for (int ear = 0; ear < n_ears_; ear++) {
alexbrandmeyer@610 133 stage_state = ears_[ear].ReturnAGCStateMemory(stage);
alexbrandmeyer@610 134 this_stage_values += stage_state;
alexbrandmeyer@610 135 }
alexbrandmeyer@610 136 this_stage_values /= n_ears_;
alexbrandmeyer@610 137 for (int ear = 0; ear < n_ears_; ear++) {
alexbrandmeyer@610 138 stage_state = ears_[ear].ReturnAGCStateMemory(stage);
alexbrandmeyer@610 139 ears_[ear].SetAGCStateMemory(stage,
alexbrandmeyer@610 140 stage_state + mix_coeff *
alexbrandmeyer@610 141 (this_stage_values - stage_state));
alexbrandmeyer@610 142 }
alexbrandmeyer@610 143 }
alexbrandmeyer@609 144 }
alexbrandmeyer@609 145 }
alexbrandmeyer@609 146 }
alexbrandmeyer@610 147
alexbrandmeyer@610 148 void CARFAC::CloseAGCLoop() {
alexbrandmeyer@610 149 for (int ear = 0; ear < n_ears_; ear++) {
alexbrandmeyer@610 150 FloatArray undamping = 1 - ears_[ear].ReturnAGCStateMemory(1);
alexbrandmeyer@610 151 // This updates the target stage gain for the new damping.
alexbrandmeyer@610 152 ears_[ear].SetCARStateDZBMemory(ears_[ear].ReturnZRCoeffs() * undamping -
alexbrandmeyer@610 153 ears_[ear].ReturnZBMemory() /
alexbrandmeyer@610 154 ears_[ear].ReturnAGCDecimation(1));
alexbrandmeyer@610 155 ears_[ear].SetCARStateDGMemory((ears_[ear].StageGValue(undamping) -
alexbrandmeyer@610 156 ears_[ear].ReturnGMemory()) /
alexbrandmeyer@610 157 ears_[ear].ReturnAGCDecimation(1));
alexbrandmeyer@610 158 }
alexbrandmeyer@610 159 }