changeset 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 16918ffbf975
children 6ec6b50f13da
files trunk/matlab/bmm/carfac/CARFAC_AGC_Step.m trunk/matlab/bmm/carfac/CARFAC_Close_AGC_Loop.m trunk/matlab/bmm/carfac/CARFAC_Design.m trunk/matlab/bmm/carfac/CARFAC_Init.m trunk/matlab/bmm/carfac/CARFAC_SAI_hacking.m trunk/matlab/bmm/carfac/CARFAC_Spatial_Smooth.m trunk/matlab/bmm/carfac/SAI_BlendFrameIntoComposite.m trunk/matlab/bmm/carfac/SAI_DesignLayers.m trunk/matlab/bmm/carfac/SAI_RunLayered.m trunk/matlab/bmm/carfac/SAI_StabilizeLayer.m trunk/matlab/bmm/carfac/SAI_UpdateBuffers.m
diffstat 11 files changed, 189 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/matlab/bmm/carfac/CARFAC_AGC_Step.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_AGC_Step.m	Tue May 21 04:24:05 2013 +0000
@@ -23,7 +23,7 @@
 % one time step of the AGC state update; decimates internally
 
 stage = 1;
-AGC_in = coeffs.detect_scale * detects;
+AGC_in = coeffs(1).detect_scale * detects;
 [state, updated] = CARFAC_AGC_Recurse(coeffs, AGC_in, stage, state);
 
 
@@ -33,36 +33,37 @@
 %   stage, state)
 
 % decim factor for this stage, relative to input or prev. stage:
-decim = coeffs.decimation(stage);
+decim = coeffs(stage).decimation;
 % decim phase of this stage (do work on phase 0 only):
-decim_phase = mod(state(1).decim_phase(stage) + 1, decim);
-state.decim_phase(stage) = decim_phase;
+decim_phase = mod(state(stage).decim_phase + 1, decim);
+state(stage).decim_phase = decim_phase;
 
 % accumulate input for this stage from detect or previous stage:
-state.input_accum(:, stage) = state.input_accum(:, stage) + AGC_in;
+state(stage).input_accum = state(stage).input_accum + AGC_in;
 
 % nothing else to do if it's not the right decim_phase
 if decim_phase == 0
   % do lots of work, at decimated rate.
   % decimated inputs for this stage, and to be decimated more for next:
-  AGC_in = state.input_accum(:, stage) / decim;
-  state.input_accum(:, stage) = 0;  % reset accumulator
+  AGC_in = state(stage).input_accum / decim;
+  state(stage).input_accum(:) = 0;  % reset accumulator
   
-  if stage < length(coeffs.decimation)  % recurse to evaluate next stage(s)
+  if stage < coeffs(1).n_AGC_stages
     state = CARFAC_AGC_Recurse(coeffs, AGC_in, stage+1, state);
     % and add its output to this stage input, whether it updated or not:
-    AGC_in = AGC_in + coeffs.AGC_stage_gain * state.AGC_memory(:, stage+1);
+    AGC_in = AGC_in + ...
+      coeffs(stage).AGC_stage_gain * state(stage + 1).AGC_memory;
   end
   
-  AGC_stage_state = state.AGC_memory(:, stage);
+  AGC_stage_state = state(stage).AGC_memory;
   % first-order recursive smoothing filter update, in time:
   AGC_stage_state = AGC_stage_state + ...
-    coeffs.AGC_epsilon(stage) * (AGC_in - AGC_stage_state);
+    coeffs(stage).AGC_epsilon * (AGC_in - AGC_stage_state);
   % spatial smooth:
   AGC_stage_state = ...
-    CARFAC_Spatial_Smooth(coeffs, stage, AGC_stage_state);
+    CARFAC_Spatial_Smooth(coeffs(stage), AGC_stage_state);
   % and store the state back (in C++, do it all in place?)
-  state.AGC_memory(:, stage) = AGC_stage_state;
+  state(stage).AGC_memory = AGC_stage_state;
   
   updated = 1;  % bool to say we have new state
 else
--- a/trunk/matlab/bmm/carfac/CARFAC_Close_AGC_Loop.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_Close_AGC_Loop.m	Tue May 21 04:24:05 2013 +0000
@@ -24,7 +24,7 @@
 decim1 = CF.AGC_params.decimation(1);
 
 for ear = 1:CF.n_ears
-  undamping = 1 - CF.ears(ear).AGC_state.AGC_memory(:, 1); % stage 1 result
+  undamping = 1 - CF.ears(ear).AGC_state(1).AGC_memory; % stage 1 result
   % Update the target stage gain for the new damping:
   new_g = CARFAC_Stage_g(CF.ears(ear).CAR_coeffs, undamping);
   % set the deltas needed to get to the new damping:
--- 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;
--- a/trunk/matlab/bmm/carfac/CARFAC_Init.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_Init.m	Tue May 21 04:24:05 2013 +0000
@@ -48,13 +48,15 @@
 
 
 function state = AGC_Init_State(coeffs)
-n_ch = coeffs.n_ch;
-n_AGC_stages = coeffs.n_AGC_stages;
-state = struct( ...
-  'AGC_memory', zeros(n_ch, n_AGC_stages), ...
-  'input_accum', zeros(n_ch, n_AGC_stages), ...
-  'decim_phase', zeros(n_AGC_stages, 1) ... % integer decimator phase
-  );
+n_ch = coeffs(1).n_ch;
+n_AGC_stages = coeffs(1).n_AGC_stages;
+state = struct([]);
+for stage = 1:n_AGC_stages
+  % Initialize state recursively...
+  state(stage).AGC_memory = zeros(n_ch, 1);
+  state(stage).input_accum = zeros(n_ch, 1);
+  state(stage).decim_phase = 0;  % integer decimator phase
+end
 
 
 function state = IHC_Init_State(coeffs)
--- a/trunk/matlab/bmm/carfac/CARFAC_SAI_hacking.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_SAI_hacking.m	Tue May 21 04:24:05 2013 +0000
@@ -28,6 +28,8 @@
 dB_list = -40; %  -60:20:0
 
 wav_fn = 'plan.wav';
+wav_fn = 'Stiletto44.wav';
+wav_fn = 'You Can Call Me Al.wav';
 
 if ~exist(['./', wav_fn], 'file')
   error('wav file not found')
@@ -36,14 +38,14 @@
 wav_fn
 [file_signal, fs] = wavread(wav_fn);
 
-if fs == 44100
-  file_signal = (file_signal(1:2:end-1, :) + file_signal(2:2:end, :)) / 2;
-  fs = fs / 2;
-end
-
-if fs ~= 22050
-  error('unexpected sample rate')
-end
+% if fs == 44100
+%   file_signal = (file_signal(1:2:end-1, :) + file_signal(2:2:end, :)) / 2;
+%   fs = fs / 2;
+% end
+% 
+% if fs ~= 22050
+%   error('unexpected sample rate')
+% end
 
 file_signal = file_signal(:, 1);  % mono
 file_signal = [file_signal; zeros(fs, 1)];  % pad with a second of silence
@@ -56,7 +58,7 @@
 end
 
 %%
-CF_struct = CARFAC_Design(1);  % default design
+CF_struct = CARFAC_Design(1, fs);  % default design
 
 CF_struct = CARFAC_Init(CF_struct);
 
@@ -70,3 +72,4 @@
 %%
 system('rm -r frames');
 
+
--- a/trunk/matlab/bmm/carfac/CARFAC_Spatial_Smooth.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_Spatial_Smooth.m	Tue May 21 04:24:05 2013 +0000
@@ -17,17 +17,17 @@
 % See the License for the specific language governing permissions and
 % limitations under the License.
 
-function stage_state = CARFAC_Spatial_Smooth(coeffs, stage, stage_state)
+function stage_state = CARFAC_Spatial_Smooth(coeffs, stage_state)
 % function AGC_state = CARFAC_Spatial_Smooth( ...
 %   n_taps, n_iterations, FIR_coeffs, AGC_state)
 
-n_iterations = coeffs.AGC_spatial_iterations(stage);
+n_iterations = coeffs.AGC_spatial_iterations;
 
 use_FIR = n_iterations < 4;  % or whatever condition we want to try
 
 if use_FIR
-  FIR_coeffs = coeffs.AGC_spatial_FIR(:,stage);
-  switch coeffs.AGC_spatial_n_taps(stage)
+  FIR_coeffs = coeffs.AGC_spatial_FIR;
+  switch coeffs.AGC_spatial_n_taps
     case 3
       for iter = 1:n_iterations
         stage_state = ...
--- a/trunk/matlab/bmm/carfac/SAI_BlendFrameIntoComposite.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/SAI_BlendFrameIntoComposite.m	Tue May 21 04:24:05 2013 +0000
@@ -21,6 +21,21 @@
   layer_struct, composite_frame)
 
 new_frame = layer_struct.frame;
+n_ch = size(new_frame, 1);
+
+if layer_struct.right_overlap == 0  % A layer 1 hack only.
+  for row = 1:n_ch
+    % Taper new_frame down near zero lag for a nicer result...
+    taper_size = round(6 + 60*(row/n_ch)^2);  %  hack
+    zero_pos = layer_struct.frame_width - layer_struct.future_lags;
+    taper = [-taper_size:min(taper_size, layer_struct.future_lags)];
+    col_range = zero_pos + taper;
+    taper = (0.4 + 0.6*abs(taper) / taper_size) .^ 2;
+    taper(taper == 0) = 0.5;
+    new_frame(row, col_range) = new_frame(row, col_range) .* taper;
+  end
+end
+
 alpha = layer_struct.alpha;
 lag_curve = layer_struct.lag_curve;
 target_columns = layer_struct.target_indices;
--- a/trunk/matlab/bmm/carfac/SAI_DesignLayers.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/SAI_DesignLayers.m	Tue May 21 04:24:05 2013 +0000
@@ -17,8 +17,8 @@
 % See the License for the specific language governing permissions and
 % limitations under the License.
 
-function [layer_array, total_width] = SAI_DesignLayers( ...
-  n_layers, width_per_layer)
+function [layer_array, total_width, lag_period_samples] = SAI_DesignLayers( ...
+  n_layers, width_per_layer, seglen)
 % function [layer_array, total_width] = SAI_DesignLayers( ...
 %   n_layers, width_per_layer)
 %
@@ -41,24 +41,24 @@
 % Other fields could be added to hold state, such as history buffers for
 % each layer, or those could go in state struct array...
 
-
 % Elevate these to a param struct?
 if nargin < 1
-  n_layers = 11
+  n_layers = 12;
 end
 if nargin < 2
-  width_per_layer = 32;  % resolution "half life" in space
+  width_per_layer = 24;  % resolution "half life" in space; half-semitones
 end
-future_lags = 4 * width_per_layer;
-width_first_layer = future_lags + 1 * width_per_layer;
-width_extra_last_layer = 2 * width_per_layer;
-left_overlap = 15;
-right_overlap = 15;
-first_window_width = 400;  % or maybe use seglen?  or 0.020 * fs?
+future_lags = 0 * width_per_layer;
+width_extra_last_layer = 0 * width_per_layer;
+left_overlap = 10;
+right_overlap = 10;
+first_window_width = seglen;  % Less would be a problem.
 min_window_width = 1*width_per_layer;  % or somewhere on that order
 window_exponent = 1.4;
 alpha_max = 1; 
 
+width_first_layer = future_lags + 2 * width_per_layer;
+
 % Start with NAP_samples_per_SAI_sample, declining to 1 from here:
 max_samples_per = 2^(n_layers - 1);
 % Construct the overall lag-warping function:
@@ -66,7 +66,11 @@
   max_samples_per * ones(1, width_extra_last_layer), ...
   max_samples_per * ...
     2 .^ (-(1:(width_per_layer * (n_layers - 1))) / width_per_layer), ...
-  ones(1, width_first_layer)];
+  ones(1, width_first_layer), ];  % w/o future for now.
+
+lag_period_samples = cumsum(NAP_samples_per_SAI_sample(end:-1:1));
+lag_period_samples = lag_period_samples(end:-1:1);  % Put it back in order.
+lag_period_samples = lag_period_samples - lag_period_samples(end - future_lags);
 
 % Each layer needs a lag_warp for a portion of that, divided by
 % 2^(layer-1), where the portion includes some overlap into its neighbors
@@ -171,6 +175,11 @@
   layer_array(layer).n_window_pos = n_triggers;
   layer_array(layer).buffer_width = layer_array(layer).frame_width + ...
     floor((1 + (n_triggers - 1)/2) * layer_array(layer).window_width);
+  % Make sure it's big enough for next layer to shift in what it wants.
+  n_shift = ceil(seglen / (2.0^(layer - 1)));
+  if layer_array(layer).buffer_width < 6 + n_shift;
+    layer_array(layer).buffer_width = 6 + n_shift;
+  end
 end
 
 return
--- a/trunk/matlab/bmm/carfac/SAI_RunLayered.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/SAI_RunLayered.m	Tue May 21 04:24:05 2013 +0000
@@ -33,16 +33,32 @@
 end
 fs = CF.fs;
 
+seglen = round(fs / 30);  % Pick about 30 fps
+frame_rate = fs / seglen;
+
 % Design the composite log-lag SAI using these parameters and defaults.
-n_layers = 10;
-width_per_layer = 40;
-[layer_array, total_width] = SAI_DesignLayers(n_layers, width_per_layer);
+n_layers = 15
+width_per_layer = 36;
+[layer_array, total_width, lags] = ...
+  SAI_DesignLayers(n_layers, width_per_layer, seglen);
+
+% Find where in the lag curve corresponds to the piano black keys:
+pitches = fs ./ lags;
+key_indices = [];
+df = log(2)/width_per_layer;
+for f = [BlackKeyFrequencies, 8, 4, 2, 1-df, 1, 1+df, 0.5, 0.25, 0.125, ...
+    -2000, -1000, -500, -250, -125];  % Augment with beat.
+  [dist, index] = min((f - pitches).^2);
+  key_indices = [key_indices, index];
+end
+piano = zeros(1, total_width);
+piano(key_indices) = 1;
+piano = [piano; piano; piano];
+
 
 % Make the composite SAI image array.
 composite_frame = zeros(n_ch, total_width);
 
-seglen = round(fs / 30);  % Pick about 60 fps
-frame_rate = fs / seglen;
 n_segs = ceil(n_samp / seglen);
 
 % Make the history buffers in the layers_array:
@@ -53,10 +69,14 @@
   layer_array(layer).frame = zeros(n_ch, layer_array(layer).frame_width);
 end
 
-n_marginal_rows = 34;
+n_marginal_rows = 100;
 marginals = [];
+average_composite = 0;
 
-average_frame = 0;
+future_lags = layer_array(1).future_lags;
+% marginals_frame = zeros(total_width - future_lags + 2*n_ch, total_width);
+marginals_frame = zeros(n_ch, total_width);
+
 for seg_num = 1:n_segs
   % k_range is the range of input sample indices for this segment
   if seg_num == n_segs
@@ -77,40 +97,54 @@
   % Shift new data into some or all of the layer buffers:
   layer_array = SAI_UpdateBuffers(layer_array, seg_naps, seg_num);
 
-
-  for layer = n_layers:-1:1  % blend from coarse to fine
+  for layer = n_layers:-1:1  % Stabilize and blend from coarse to fine
     update_interval = layer_array(layer).update_interval;
     if 0 == mod(seg_num, update_interval)
       layer_array(layer) = SAI_StabilizeLayer(layer_array(layer));
-      new_frame = layer_array(layer).frame;
       composite_frame = SAI_BlendFrameIntoComposite( ...
         layer_array(layer), composite_frame);
     end
   end
+  
+  average_composite = average_composite + ...
+    0.01 * (composite_frame - average_composite);
  
   if isempty(marginals)
-    composite_width = size(composite_frame, 2);
-    marginals = zeros(n_marginal_rows, composite_width);
+    marginals = zeros(n_marginal_rows, total_width);
   end
   for row = n_marginal_rows:-1:11
     % smooth from row above (lower number)
     marginals(row, :) = marginals(row, :) + ...
-      2^((10 - row)/2) * (1.06*marginals(row - 1, :) - marginals(row, :));
+      2^((10 - row)/8) * (1.01*marginals(row - 1, :) - marginals(row, :));
   end
   lag_marginal = mean(composite_frame, 1);  % means max out near 1 or 2
+  lag_marginal = lag_marginal - 0.75*smooth1d(lag_marginal, 30)';
+  
+  freq_marginal = mean(layer_array(1).nap_buffer);
+  % emphasize local peaks:
+  freq_marginal = freq_marginal - 0.5*smooth1d(freq_marginal, 5)';
+  
+  
+%   marginals_frame = [marginals_frame(:, 2:end), ...
+%     [lag_marginal(1:(end - future_lags)), freq_marginal(ceil((1:(2*end))/2))]'];
+  marginals_frame = [marginals_frame(:, 2:end), freq_marginal(1:end)'];
+  
   for row = 10:-1:1
-    marginals(row, :) = (lag_marginal - smooth1d(lag_marginal, 30)') - ...
-      (10 - row) / 40;
+    marginals(row, :) = lag_marginal - (10 - row) / 40;
   end
     
   if 0 == mod(seg_num, update_interval) || seg_num == 1
     coc_gram = layer_array(end).nap_buffer';
     [n_ch, n_width] = size(composite_frame);
     coc_gram = [coc_gram, zeros(n_ch, n_width - size(coc_gram, 2))];
+    coc_gram = coc_gram(:, (end-total_width+1):end);
   end
   
-  display_frame = [coc_gram; ...
-    composite_frame(floor(1:0.5:end), :); 20*max(0,marginals)];
+  display_frame = [ ...  % coc_gram; ...
+    4 * marginals_frame; ...
+    composite_frame(ceil((1:(2*end))/2), :); ...
+    piano; ...
+    10*max(0,marginals)];
   
   cmap = jet;
   cmap = 1 - gray;  % jet
@@ -127,5 +161,13 @@
 return
 
 
+function frequencies = BlackKeyFrequencies
+black_indices = [];
+for index = 0:87
+  if any(mod(index, 12) == [1 4 6 9 11])
+    black_indices = [black_indices, index];
+  end
+end
+frequencies = 27.5 * 2.^(black_indices / 12);
 
 
--- a/trunk/matlab/bmm/carfac/SAI_StabilizeLayer.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/SAI_StabilizeLayer.m	Tue May 21 04:24:05 2013 +0000
@@ -44,7 +44,6 @@
     if peak_val <= 0  % just use window center instead
       [peak_val, trigger_time] = max(window);
     end
-    % TODO(dicklyon):  alpha blend here.
     trigger_time = trigger_time + floor((w - 1) * d_win);
     alpha = (0.025 + peak_val) / (0.5 + peak_val);  % alpha 0.05 to near 1.0
     frame(ch, :) = alpha * nap_wave(trigger_time + offset_range)' + ...
--- a/trunk/matlab/bmm/carfac/SAI_UpdateBuffers.m	Fri May 17 19:52:45 2013 +0000
+++ b/trunk/matlab/bmm/carfac/SAI_UpdateBuffers.m	Tue May 21 04:24:05 2013 +0000
@@ -42,7 +42,7 @@
 %% 
 % Decimate using a 2-3-4-filter and partial differencing emphasize onsets:
 kernel = filter([1 1]/2, 1, filter([1 1 1]/3, 1, [1 1 1 1 0 0 0 0]/4));
-kernel = kernel + 2*diff([0, kernel]);
+% kernel = kernel + 2*diff([0, kernel]);
 % figure(1)
 % plot(kernel)
 
@@ -65,11 +65,13 @@
       n_shift = floor(n_shift);
       % Grab new stuff from new end (big time indices) of previous layer.
       % Take twice as many times as we need, + 5, for decimation, and do
-      % 343 smoothing to get new points.
+      % smoothing to get new points.
       new_chunk = ...
         layer_array(layer - 1).nap_buffer((end - 2*n_shift - 4):end, :);
       new_chunk = filter(kernel, 1, new_chunk);
-      new_chunk = gain * new_chunk(7:2:end, :);
+      % new_chunk = gain * new_chunk(7:2:end, :);
+      % try a little extra smoothing:
+      new_chunk = gain * (new_chunk(7:2:end, :) + new_chunk(6:2:(end-1), :))/2;
       
     end
     % Put new stuff in at latest time indices.