Mercurial > hg > aimc
diff trunk/matlab/bmm/carfac/CARFAC_Design.m @ 665:d0ff15c36828
Turn the AGC coeffs inside out: array of structs instead of a struct of little arrays.
In C++ use a vector<AGC_coeffs> for this; each of 4 stages has an entry; many fewer places
need to do indexing by stage, and this removes the temptation to use little eigen arrays
for the 4 stages.
Also latest version of experimental log-lag SAI hacks.
author | dicklyon@google.com |
---|---|
date | Tue, 21 May 2013 04:24:05 +0000 |
parents | 2767ce76a1b0 |
children | 2341bb90adb8 |
line wrap: on
line diff
--- a/trunk/matlab/bmm/carfac/CARFAC_Design.m Fri May 17 19:52:45 2013 +0000 +++ b/trunk/matlab/bmm/carfac/CARFAC_Design.m Tue May 21 04:24:05 2013 +0000 @@ -17,9 +17,10 @@ % See the License for the specific language governing permissions and % limitations under the License. -function CF = CARFAC_Design(n_ears, fs, CF_CAR_params, CF_AGC_params, CF_IHC_params) -% function CF = CARFAC_Design(fs, CF_CAR_params, ... -% CF_AGC_params, ERB_break_freq, ERB_Q, CF_IHC_params) +function CF = CARFAC_Design(n_ears, fs, ... + CF_CAR_params, CF_AGC_params, CF_IHC_params) +% function CF = CARFAC_Design(n_ears, fs, ... +% CF_CAR_params, CF_AGC_params, CF_IHC_params) % % This function designs the CARFAC (Cascade of Asymmetric Resonators with % Fast-Acting Compression); that is, it take bundles of parameters and @@ -125,16 +126,17 @@ pole_Hz = pole_Hz - CF_CAR_params.ERB_per_step * ... ERB_Hz(pole_Hz, CF_CAR_params.ERB_break_freq, CF_CAR_params.ERB_Q); end -% now we have n_ch, the number of channels, and pole_freqs array +% Now we have n_ch, the number of channels, and pole_freqs array. max_channels_per_octave = log(2) / log(pole_freqs(1)/pole_freqs(2)); -% convert to include an ear_array, each w coeffs and state... +% Convert to include an ear_array, each w coeffs and state... CAR_coeffs = CARFAC_DesignFilters(CF_CAR_params, fs, pole_freqs); AGC_coeffs = CARFAC_DesignAGC(CF_AGC_params, fs, n_ch); IHC_coeffs = CARFAC_DesignIHC(CF_IHC_params, fs, n_ch); -% copy same designed coeffs into each ear (can do differently in the -% future: + +% Copy same designed coeffs into each ear (can do differently in the +% future). for ear = 1:n_ears ears(ear).CAR_coeffs = CAR_coeffs; ears(ear).AGC_coeffs = AGC_coeffs; @@ -226,26 +228,30 @@ function AGC_coeffs = CARFAC_DesignAGC(AGC_params, fs, n_ch) n_AGC_stages = AGC_params.n_stages; -AGC_coeffs = struct( ... - 'n_ch', n_ch, ... - 'n_AGC_stages', n_AGC_stages, ... - 'AGC_stage_gain', AGC_params.AGC_stage_gain); % AGC1 pass is smoothing from base toward apex; % AGC2 pass is back, which is done first now (in double exp. version) AGC1_scales = AGC_params.AGC1_scales; AGC2_scales = AGC_params.AGC2_scales; -AGC_coeffs.AGC_epsilon = zeros(1, n_AGC_stages); % the 1/(tau*fs) roughly decim = 1; -AGC_coeffs.decimation = AGC_params.decimation; total_DC_gain = 0; + +%% +% Convert to vector of AGC coeffs +AGC_coeffs = struct([]); for stage = 1:n_AGC_stages + AGC_coeffs(stage).n_ch = n_ch; + AGC_coeffs(stage).n_AGC_stages = n_AGC_stages; + AGC_coeffs(stage).AGC_stage_gain = AGC_params.AGC_stage_gain; + + AGC_coeffs(stage).decimation = AGC_params.decimation(stage); tau = AGC_params.time_constants(stage); % time constant in seconds decim = decim * AGC_params.decimation(stage); % net decim to this stage % epsilon is how much new input to take at each update step: - AGC_coeffs.AGC_epsilon(stage) = 1 - exp(-decim / (tau * fs)); + AGC_coeffs(stage).AGC_epsilon = 1 - exp(-decim / (tau * fs)); + % effective number of smoothings in a time constant: ntimes = tau * (fs / decim); % typically 5 to 50 @@ -262,8 +268,8 @@ dp = delay * (1 - 2*p +p^2)/2; polez1 = p - dp; polez2 = p + dp; - AGC_coeffs.AGC_polez1(stage) = polez1; - AGC_coeffs.AGC_polez2(stage) = polez2; + AGC_coeffs(stage).AGC_polez1 = polez1; + AGC_coeffs(stage).AGC_polez2 = polez2; % try a 3- or 5-tap FIR as an alternative to the double exponential: n_taps = 0; @@ -292,46 +298,56 @@ n_taps, spread_sq, delay, n_iterations); end % when FIR_OK, store the resulting FIR design in coeffs: - AGC_coeffs.AGC_spatial_iterations(stage) = n_iterations; - AGC_coeffs.AGC_spatial_FIR(:,stage) = AGC_spatial_FIR; - AGC_coeffs.AGC_spatial_n_taps(stage) = n_taps; + AGC_coeffs(stage).AGC_spatial_iterations = n_iterations; + AGC_coeffs(stage).AGC_spatial_FIR = AGC_spatial_FIR; + AGC_coeffs(stage).AGC_spatial_n_taps = n_taps; % accumulate DC gains from all the stages, accounting for stage_gain: total_DC_gain = total_DC_gain + AGC_params.AGC_stage_gain^(stage-1); % TODO (dicklyon) -- is this the best binaural mixing plan? if stage == 1 - AGC_coeffs.AGC_mix_coeffs(stage) = 0; + AGC_coeffs(stage).AGC_mix_coeffs = 0; else - AGC_coeffs.AGC_mix_coeffs(stage) = AGC_params.AGC_mix_coeff / ... + AGC_coeffs(stage).AGC_mix_coeffs = AGC_params.AGC_mix_coeff / ... (tau * (fs / decim)); end end -AGC_coeffs.AGC_gain = total_DC_gain; - -% adjust the detect_scale to be the reciprocal DC gain of the AGC filters: -AGC_coeffs.detect_scale = 1 / total_DC_gain; +% adjust stage 1 detect_scale to be the reciprocal DC gain of the AGC filters: +AGC_coeffs(1).detect_scale = 1 / total_DC_gain; %% -function [FIR, OK] = Design_FIR_coeffs(n_taps, var, mn, n_iter) -% function [FIR, OK] = Design_FIR_coeffs(n_taps, spread_sq, delay, n_iter) +function [FIR, OK] = Design_FIR_coeffs(n_taps, delay_variance, ... + mean_delay, n_iter) +% function [FIR, OK] = Design_FIR_coeffs(n_taps, delay_variance, ... +% mean_delay, n_iter) +% The smoothing function is a space-domain smoothing, but it considered +% here by analogy to time-domain smoothing, which is why its potential +% off-centeredness is called a delay. Since it's a smoothing filter, it is +% also analogous to a discrete probability distribution (a p.m.f.), with +% mean corresponding to delay and variance corresponding to squared spatial +% spread (in samples, or channels, and the square thereof, respecitively). +% Here we design a filter implementation's coefficient via the method of +% moment matching, so we get the intended delay and spread, and don't worry +% too much about the shape of the distribution, which will be some kind of +% blob not too far from Gaussian if we run several FIR iterations. % reduce mean and variance of smoothing distribution by n_iterations: -mn = mn / n_iter; -var = var / n_iter; +mean_delay = mean_delay / n_iter; +delay_variance = delay_variance / n_iter; switch n_taps case 3 % based on solving to match mean and variance of [a, 1-a-b, b]: - a = (var + mn*mn - mn) / 2; - b = (var + mn*mn + mn) / 2; + a = (delay_variance + mean_delay*mean_delay - mean_delay) / 2; + b = (delay_variance + mean_delay*mean_delay + mean_delay) / 2; FIR = [a, 1 - a - b, b]; OK = FIR(2) >= 0.2; case 5 % based on solving to match [a/2, a/2, 1-a-b, b/2, b/2]: - a = ((var + mn*mn)*2/5 - mn*2/3) / 2; - b = ((var + mn*mn)*2/5 + mn*2/3) / 2; + a = ((delay_variance + mean_delay*mean_delay)*2/5 - mean_delay*2/3) / 2; + b = ((delay_variance + mean_delay*mean_delay)*2/5 + mean_delay*2/3) / 2; % first and last coeffs are implicitly duplicated to make 5-point FIR: FIR = [a/2, 1 - a - b, b/2]; OK = FIR(2) >= 0.1;