annotate C++/AGC.C @ 611:0fbaf443ec82

Carfac C++ revision 3, indluding more style improvements. The output structs are now classes again, and have separate storage methods for each output structure along with flags in the Run and RunSegment methods to allow for only storing NAPs if desired.
author alexbrandmeyer
date Fri, 17 May 2013 19:52:45 +0000
parents d838de2ce1b1
children c692afd86cc9
rev   line source
flatmax@592 1 // Copyright 2013 Matt R. Flax <flatmax\@> All Rights Reserved.
flatmax@593 2 // Author Matt Flax <flatmax@>
flatmax@592 3 //
flatmax@592 4 // This C++ file is part of an implementation of Lyon's cochlear model:
flatmax@592 5 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
flatmax@592 6 // to supplement Lyon's upcoming book "Human and Machine Hearing"
flatmax@592 7 //
flatmax@592 8 // Licensed under the Apache License, Version 2.0 (the "License");
flatmax@592 9 // you may not use this file except in compliance with the License.
flatmax@592 10 // You may obtain a copy of the License at
flatmax@592 11 //
flatmax@592 12 // http://www.apache.org/licenses/LICENSE-2.0
flatmax@592 13 //
flatmax@592 14 // Unless required by applicable law or agreed to in writing, software
flatmax@592 15 // distributed under the License is distributed on an "AS IS" BASIS,
flatmax@592 16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
flatmax@592 17 // See the License for the specific language governing permissions and
flatmax@592 18 // limitations under the License.
flatmax@592 19 /**
flatmax@592 20 \author {Matt Flax <flatmax\@>}
flatmax@592 21 \date 2013.02.08
flatmax@592 22 */
flatmax@592 23
flatmax@592 24 #include "AGC.H"
flatmax@592 25
flatmax@601 26 AGC::AGC() {
flatmax@592 27 }
flatmax@592 28
flatmax@601 29 AGC::~AGC() {
flatmax@592 30 }
flatmax@601 31
flatmax@601 32 void AGC::designAGC(FP_TYPE fs, int n_ch) {
flatmax@601 33 int n_AGC_stages = params.n_stages;
flatmax@601 34 //AGC_coeffs = struct( ...
flatmax@601 35 // 'n_ch', n_ch, ...
flatmax@601 36 // 'n_AGC_stages', n_AGC_stages, ...
flatmax@601 37 // 'AGC_stage_gain', AGC_params.AGC_stage_gain);
flatmax@601 38
flatmax@601 39 // AGC1 pass is smoothing from base toward apex;
flatmax@601 40 // AGC2 pass is back, which is done first now (in double exp. version)
flatmax@601 41 //AGC1_scales = AGC_params.AGC1_scales;
flatmax@601 42 //AGC2_scales = AGC_params.AGC2_scales;
flatmax@601 43
flatmax@601 44 coeffs.AGC_epsilon = Array<FP_TYPE, 1, Dynamic>::Zero(1, n_AGC_stages); // the 1/(tau*fs) roughly
flatmax@601 45 FP_TYPE decim = 1.;
flatmax@601 46 //AGC_coeffs.decimation = AGC_params.decimation;
flatmax@601 47
flatmax@601 48 FP_TYPE total_DC_gain = 0.;
flatmax@601 49 for (int stage = 1; stage<=n_AGC_stages; stage++) {
flatmax@601 50 FP_TYPE tau = params.time_constants(stage-1); // time constant in seconds
flatmax@601 51 decim = decim * params.decimation(stage-1); // net decim to this stage
flatmax@601 52 // epsilon is how much new input to take at each update step:
flatmax@601 53 coeffs.AGC_epsilon(stage-1) = 1. - exp(-decim / (tau * fs));
flatmax@601 54 // effective number of smoothings in a time constant:
flatmax@601 55 FP_TYPE ntimes = tau * (fs / decim); // typically 5 to 50
flatmax@601 56
flatmax@601 57 // decide on target spread (variance) and delay (mean) of impulse
flatmax@601 58 // response as a distribution to be convolved ntimes:
flatmax@601 59 // TODO (dicklyon): specify spread and delay instead of scales???
flatmax@601 60 FP_TYPE delay = (param.AGC2_scales(stage-1) - param.AGC1_scales(stage-1)) / ntimes;
flatmax@601 61 FP_TYPE spread_sq = (param.AGC1_scales(stage-1).pow(2.) + param.AGC2_scales(stage-1).pow(2)) / ntimes;
flatmax@601 62
flatmax@601 63 // get pole positions to better match intended spread and delay of
flatmax@601 64 // [[geometric distribution]] in each direction (see wikipedia)
flatmax@601 65 FP_TYPE u = 1. + 1. / spread_sq; // these are based on off-line algebra hacking.
flatmax@601 66 FP_TYPE p = u - sqrt(pow(u,2.) - 1.); // pole that would give spread if used twice.
flatmax@601 67 FP_TYPE dp = delay * (1. - 2.*p +pow(p,2.))/2.;
flatmax@601 68 FP_TYPE polez1 = p - dp;
flatmax@601 69 FP_TYPE polez2 = p + dp;
flatmax@601 70 coeffs.AGC_polez1(stage) = polez1;
flatmax@601 71 coeffs.AGC_polez2(stage) = polez2;
flatmax@601 72
flatmax@601 73 // try a 3- or 5-tap FIR as an alternative to the double exponential:
flatmax@601 74 Array<FP_TYPE,1, Dynamic> AGC_spatial_FIR;
flatmax@601 75 int n_taps = 0;
flatmax@601 76 int FIR_OK = 0;
flatmax@601 77 int n_iterations = 1;
flatmax@601 78 while (~FIR_OK) {
flatmax@601 79 switch (n_taps) {
flatmax@601 80 case 0:
flatmax@601 81 // first attempt a 3-point FIR to apply once:
flatmax@601 82 n_taps = 3;
flatmax@601 83 break;
flatmax@601 84 case 3:
flatmax@601 85 // second time through, go wider but stick to 1 iteration
flatmax@601 86 n_taps = 5;
flatmax@601 87 break;
flatmax@601 88 case 5:
flatmax@601 89 // apply FIR multiple times instead of going wider:
flatmax@601 90 n_iterations = n_iterations + 1;
flatmax@601 91 if (n_iterations > 16) {
flatmax@601 92 cerr<<"Too many n_iterations in CARFAC_DesignAGC"<<endl;
flatmax@601 93 exit(AGC_DESIGN_ITERATION_ERROR);
flatmax@601 94 }
flatmax@601 95 break;
flatmax@601 96 default:
flatmax@601 97 // to do other n_taps would need changes in CARFAC_Spatial_Smooth
flatmax@601 98 // and in Design_FIR_coeffs
flatmax@601 99 cerr<<"Bad n_taps in CARFAC_DesignAGC"<<endl;
flatmax@601 100 exit(AGC_DESIGN_TAPS_OOB_ERROR);
flatmax@601 101 break;
flatmax@601 102 }
flatmax@601 103 FIR_OK = Design_FIR_coeffs(n_taps, spread_sq, delay, n_iterations,AGC_spatial_FIR);
flatmax@601 104 }
flatmax@601 105 // when FIR_OK, store the resulting FIR design in coeffs:
flatmax@601 106 coeff.AGC_spatial_iterations(stage-1) = n_iterations;
flatmax@601 107 coeff.AGC_spatial_FIR.col(stage-1).block(0,AGC_spatial_FIR.size()) = AGC_spatial_FIR;
flatmax@601 108 coeff.AGC_spatial_n_taps(stage-1) = n_taps;
flatmax@601 109
flatmax@601 110 // accumulate DC gains from all the stages, accounting for stage_gain:
flatmax@601 111 total_DC_gain = total_DC_gain + params.AGC_stage_gain.pow(stage-1);
flatmax@601 112
flatmax@601 113 // TODO (dicklyon) -- is this the best binaural mixing plan?
flatmax@601 114 if (stage == 1)
flatmax@601 115 coeff.AGC_mix_coeffs(stage-1) = 0.;
flatmax@601 116 else
flatmax@601 117 coeff.AGC_mix_coeffs(stage-1) = param.AGC_mix_coeff / (tau * (fs / decim));
flatmax@601 118 }
flatmax@601 119
flatmax@601 120 coeff.AGC_gain = total_DC_gain;
flatmax@601 121
flatmax@601 122 // adjust the detect_scale to be the reciprocal DC gain of the AGC filters:
flatmax@601 123 AGC_coeffs.detect_scale = 1. / total_DC_gain;
flatmax@601 124
flatmax@601 125 }
flatmax@601 126
flatmax@601 127 int OK AGC::Design_FIR_coeffs(int n_taps, FP_TYPE var, FP_TYPE mn, int n_iter, Array<FP_TYPE,Dynamic,1> &FIR) {
flatmax@601 128 // reduce mean and variance of smoothing distribution by n_iterations:
flatmax@601 129 mn = mn / (FP_TYPE)n_iter;
flatmax@601 130 var = var / (FP_TYPE)n_iter;
flatmax@601 131 switch (n_taps) {
flatmax@601 132 case 3:
flatmax@601 133 // based on solving to match mean and variance of [a, 1-a-b, b]:
flatmax@601 134 a = (var + mn*mn - mn) / 2.;
flatmax@601 135 b = (var + mn*mn + mn) / 2.;
flatmax@601 136 FIR.resize(3,1);
flatmax@601 137 FIR<<a, 1. - a - b, b;
flatmax@601 138 OK = FIR(2) >= 0.2;
flatmax@601 139 case 5
flatmax@601 140 // based on solving to match [a/2, a/2, 1-a-b, b/2, b/2]:
flatmax@601 141 a = ((var + mn*mn)*2./5. - mn*2./3.) / 2.;
flatmax@601 142 b = ((var + mn*mn)*2./5. + mn*2./3.) / 2.;
flatmax@601 143 // first and last coeffs are implicitly duplicated to make 5-point FIR:
flatmax@601 144 FIR.resize(5,1);
flatmax@601 145 FIR<<a/2., 1. - a - b, b/2.;
flatmax@601 146 OK = FIR(2) >= 0.1;
flatmax@601 147 default:
flatmax@601 148 cerr<<"Bad n_taps in AGC_spatial_FIR"<<endl;
flatmax@601 149 exit(AGC_FIR_TAP_COUNT_ERROR);
flatmax@601 150 }
flatmax@601 151 }