annotate matlab/bmm/carfac/CARFAC_Transfer_Functions.m @ 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 a0869cb1c99b
children
rev   line source
dicklyon@466 1 % Copyright 2012, Google, Inc.
dicklyon@466 2 % Author: Richard F. Lyon
dicklyon@466 3 %
dicklyon@466 4 % This Matlab file is part of an implementation of Lyon's cochlear model:
dicklyon@466 5 % "Cascade of Asymmetric Resonators with Fast-Acting Compression"
dicklyon@466 6 % to supplement Lyon's upcoming book "Human and Machine Hearing"
dicklyon@466 7 %
dicklyon@466 8 % Licensed under the Apache License, Version 2.0 (the "License");
dicklyon@466 9 % you may not use this file except in compliance with the License.
dicklyon@466 10 % You may obtain a copy of the License at
dicklyon@466 11 %
dicklyon@466 12 % http://www.apache.org/licenses/LICENSE-2.0
dicklyon@466 13 %
dicklyon@466 14 % Unless required by applicable law or agreed to in writing, software
dicklyon@466 15 % distributed under the License is distributed on an "AS IS" BASIS,
dicklyon@466 16 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
dicklyon@466 17 % See the License for the specific language governing permissions and
dicklyon@466 18 % limitations under the License.
dicklyon@466 19
dicklyon@465 20 function [complex_transfns_freqs, ...
dicklyon@472 21 stage_numerators, stage_denominators, group_delays] = ...
dicklyon@472 22 CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
dicklyon@465 23 % function [complex_transfns_freqs, ...
dicklyon@472 24 % stage_numerators, stage_denominators, group_delays] = ...
dicklyon@472 25 % CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
dicklyon@472 26 %
dicklyon@465 27 % Return transfer functions as polynomials in z (nums & denoms);
dicklyon@465 28 % And evaluate them at freqs if it's given, to selected output,
dicklyon@465 29 % optionally from selected starting points (from 0, input, by default).
dicklyon@465 30 % complex_transfns_freqs has a row of complex gains per to_channel.
dicklyon@465 31
dicklyon@465 32 % always start with the rational functions, whether we want to return
dicklyon@500 33 % them or not; this defaults to ear 1 only:
dicklyon@465 34 [stage_numerators, stage_denominators] = CARFAC_Rational_Functions(CF);
dicklyon@465 35
dicklyon@465 36 if nargin >= 2
dicklyon@465 37 % Evaluate at the provided list of frequencies.
dicklyon@465 38 if ~isrow(freqs)
dicklyon@465 39 if iscolumn(freqs)
dicklyon@465 40 freqs = freqs';
dicklyon@465 41 else
dicklyon@465 42 error('Bad freqs_row in CARFAC_Transfer_Functions');
dicklyon@465 43 end
dicklyon@465 44 end
dicklyon@465 45 if any(freqs < 0)
dicklyon@465 46 error('Negatives in freqs_row in CARFAC_Transfer_Functions');
dicklyon@465 47 end
dicklyon@465 48 z_row = exp((i * 2 * pi / CF.fs) * freqs); % z = exp(sT)
dicklyon@465 49 gains = Rational_Eval(stage_numerators, stage_denominators, z_row);
dicklyon@465 50
dicklyon@465 51 % Now multiply gains from input to output places; use logs?
dicklyon@465 52 log_gains = log(gains);
dicklyon@504 53 cum_log_gains = cumsum(log_gains); % accum across cascaded stages
dicklyon@465 54
dicklyon@465 55 % And figure out which cascade products we want:
dicklyon@465 56 n_ch = CF.n_ch;
dicklyon@465 57 if nargin < 3
dicklyon@465 58 to_channels = 1:n_ch;
dicklyon@465 59 end
dicklyon@465 60 if isempty(to_channels) || any(to_channels < 1 | to_channels > n_ch)
dicklyon@465 61 error('Bad to_channels in CARFAC_Transfer_Functions');
dicklyon@465 62 end
dicklyon@465 63 if nargin < 4 || isempty(from_channels)
dicklyon@465 64 from_channels = 0; % tranfuns from input, called channel 0.
dicklyon@465 65 end
dicklyon@465 66 if length(from_channels) == 1
dicklyon@467 67 from_channels = from_channels * ones(1,length(to_channels));
dicklyon@465 68 end
dicklyon@465 69 % Default to cum gain of 1 (log is 0), from input channel 0:
dicklyon@465 70 from_cum = zeros(length(to_channels), length(z_row));
dicklyon@465 71 not_input = from_channels > 0;
dicklyon@465 72 from_cum(not_input, :) = cum_log_gains(from_channels(not_input), :);
dicklyon@465 73 log_transfns = cum_log_gains(to_channels, :) - from_cum;
dicklyon@465 74 complex_transfns_freqs = exp(log_transfns);
dicklyon@472 75
dicklyon@472 76 if nargout >= 4
dicklyon@472 77 phases = imag(log_gains); % no wrapping problem on single stages
dicklyon@472 78 cum_phases = cumsum(phases); % so no wrapping here either
dicklyon@472 79 group_delays = -diff(cum_phases')'; % diff across frequencies
dicklyon@472 80 group_delays = group_delays ./ (2*pi*repmat(diff(freqs), n_ch, 1));
dicklyon@472 81 end
dicklyon@465 82 else
dicklyon@465 83 % If no freqs are provided, do nothing but return the stage info above:
dicklyon@465 84 complex_transfns_freqs = [];
dicklyon@465 85 end
dicklyon@465 86
dicklyon@465 87
dicklyon@465 88
dicklyon@465 89 function gains = Rational_Eval(numerators, denominators, z_row)
dicklyon@465 90 % function gains = Rational_Eval(numerators, denominators, z_row)
dicklyon@465 91 % Evaluate rational function at row of z values.
dicklyon@465 92
dicklyon@465 93 zz = [z_row .* z_row; z_row; ones(size(z_row))];
dicklyon@465 94 % dot product of each poly row with each [z2; z; 1] col:
dicklyon@465 95 gains = (numerators * zz) ./ (denominators * zz);
dicklyon@465 96
dicklyon@465 97
dicklyon@504 98
dicklyon@465 99 function [stage_numerators, stage_denominators] = ...
dicklyon@500 100 CARFAC_Rational_Functions(CF, ear)
dicklyon@465 101 % function [stage_z_numerators, stage_z_denominators] = ...
dicklyon@500 102 % CARFAC_Rational_Functions(CF, ear)
dicklyon@465 103 % Return transfer functions of all stages as rational functions.
dicklyon@465 104
dicklyon@500 105 if nargin < 2
dicklyon@500 106 ear = 1;
dicklyon@500 107 end
dicklyon@500 108
dicklyon@467 109 n_ch = CF.n_ch;
dicklyon@500 110 coeffs = CF.ears(ear).CAR_coeffs;
dicklyon@467 111
dicklyon@469 112 a0 = coeffs.a0_coeffs;
dicklyon@469 113 c0 = coeffs.c0_coeffs;
dicklyon@469 114 zr = coeffs.zr_coeffs;
dicklyon@467 115
dicklyon@467 116 % get r, adapted if we have state:
dicklyon@504 117 r1 = coeffs.r1_coeffs; % max-damping condition
dicklyon@500 118 if isfield(CF.ears(ear), 'CAR_state')
dicklyon@500 119 state = CF.ears(ear).CAR_state;
dicklyon@504 120 zB = state.zB_memory; % current delta-r from undamping
dicklyon@504 121 r = r1 + zB;
dicklyon@465 122 else
dicklyon@504 123 zB = 0; % HIGH-level linear condition by default
dicklyon@465 124 end
dicklyon@467 125
dicklyon@504 126 relative_undamping = zB ./ zr;
dicklyon@504 127 g = CARFAC_Stage_g(coeffs, relative_undamping);
dicklyon@467 128 a = a0 .* r;
dicklyon@467 129 c = c0 .* r;
dicklyon@465 130 r2 = r .* r;
dicklyon@467 131 h = coeffs.h_coeffs;
dicklyon@467 132
dicklyon@465 133 stage_denominators = [ones(n_ch, 1), -2 * a, r2];
dicklyon@465 134 stage_numerators = [g .* ones(n_ch, 1), g .* (-2 * a + h .* c), g .* r2];
dicklyon@465 135
dicklyon@465 136
dicklyon@465 137 %% example
dicklyon@465 138 % CF = CARFAC_Design
dicklyon@465 139 % f = (0:100).^2; % frequencies to 10 kHz, unequally spaced
dicklyon@465 140 % to_ch = 10:10:96; % selected output channels
dicklyon@465 141 % from_ch = to_ch - 10; % test the inclusion of 0 in from list
dicklyon@465 142 % tf = CARFAC_Transfer_Functions(CF, f, to_ch, from_ch);
dicklyon@465 143 % figure
dicklyon@465 144 % plot(f, 20*log(abs(tf)')/log(10)); % dB vs lin. freq for 10 taps
dicklyon@465 145