dicklyon@604
|
1 % Copyright 2013, Google, Inc.
|
dicklyon@604
|
2 % Author: Richard F. Lyon
|
dicklyon@604
|
3 %
|
dicklyon@604
|
4 % This Matlab file is part of an implementation of Lyon's cochlear model:
|
dicklyon@604
|
5 % "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
dicklyon@604
|
6 % to supplement Lyon's upcoming book "Human and Machine Hearing"
|
dicklyon@604
|
7 %
|
dicklyon@604
|
8 % Licensed under the Apache License, Version 2.0 (the "License");
|
dicklyon@604
|
9 % you may not use this file except in compliance with the License.
|
dicklyon@604
|
10 % You may obtain a copy of the License at
|
dicklyon@604
|
11 %
|
dicklyon@604
|
12 % http://www.apache.org/licenses/LICENSE-2.0
|
dicklyon@604
|
13 %
|
dicklyon@604
|
14 % Unless required by applicable law or agreed to in writing, software
|
dicklyon@604
|
15 % distributed under the License is distributed on an "AS IS" BASIS,
|
dicklyon@604
|
16 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
dicklyon@604
|
17 % See the License for the specific language governing permissions and
|
dicklyon@604
|
18 % limitations under the License.
|
dicklyon@604
|
19
|
dicklyon@604
|
20 function [frame_rate, num_frames] = SAI_RunLayered(CF, input_waves)
|
dicklyon@604
|
21 % function [CF, SAI_movie] = CARFAC_Run_Layered_SAI(CF, input_waves)
|
dicklyon@604
|
22 % This function runs the CARFAC and generates an SAI movie, dumped as PNG
|
dicklyon@604
|
23 % files for now.
|
dicklyon@604
|
24
|
dicklyon@604
|
25 % Layer 1 is not decimated from the 22050 rate; subsequent layers have
|
dicklyon@604
|
26 % smoothing and 2X decimation each. All layers get composited togehter
|
dicklyon@604
|
27 % into movie frames.
|
dicklyon@604
|
28
|
dicklyon@604
|
29 n_ch = CF.n_ch;
|
dicklyon@604
|
30 [n_samp, n_ears] = size(input_waves);
|
dicklyon@604
|
31 if n_ears ~= CF.n_ears
|
dicklyon@604
|
32 error('bad number of input_waves channels passed to CARFAC_Run')
|
dicklyon@604
|
33 end
|
dicklyon@604
|
34 fs = CF.fs;
|
dicklyon@604
|
35
|
dicklyon@604
|
36 % Design the composite log-lag SAI using these parameters and defaults.
|
dicklyon@604
|
37 n_layers = 10;
|
dicklyon@604
|
38 width_per_layer = 40;
|
dicklyon@604
|
39 [layer_array, total_width] = SAI_DesignLayers(n_layers, width_per_layer);
|
dicklyon@604
|
40
|
dicklyon@604
|
41 % Make the composite SAI image array.
|
dicklyon@604
|
42 composite_frame = zeros(n_ch, total_width);
|
dicklyon@604
|
43
|
dicklyon@604
|
44 seglen = round(fs * 0.020); % Pick about 20 ms segments
|
dicklyon@604
|
45 frame_rate = fs / seglen;
|
dicklyon@604
|
46 n_segs = ceil(n_samp / seglen);
|
dicklyon@604
|
47
|
dicklyon@604
|
48 % Make the history buffers in the layers_array:
|
dicklyon@604
|
49 for layer = 1:n_layers
|
dicklyon@604
|
50 layer_array(layer).nap_buffer = zeros(layer_array(layer).buffer_width, n_ch);
|
dicklyon@604
|
51 layer_array(layer).nap_fraction = 0; % leftover fraction to shift in.
|
dicklyon@604
|
52 end
|
dicklyon@604
|
53
|
dicklyon@604
|
54 for seg_num = 1:n_segs
|
dicklyon@604
|
55 % k_range is the range of input sample indices for this segment
|
dicklyon@604
|
56 if seg_num == n_segs
|
dicklyon@604
|
57 % The last segment may be short of seglen, but do it anyway:
|
dicklyon@604
|
58 k_range = (seglen*(seg_num - 1) + 1):n_samp;
|
dicklyon@604
|
59 else
|
dicklyon@604
|
60 k_range = seglen*(seg_num - 1) + (1:seglen);
|
dicklyon@604
|
61 end
|
dicklyon@604
|
62 % Process a segment to get a slice of decim_naps, and plot AGC state:
|
dicklyon@604
|
63 [seg_naps, CF] = CARFAC_Run_Segment(CF, input_waves(k_range, :));
|
dicklyon@604
|
64
|
dicklyon@604
|
65 seg_naps = max(0, seg_naps); % Rectify
|
dicklyon@604
|
66
|
dicklyon@604
|
67 if seg_num == n_segs % pad out the last result
|
dicklyon@604
|
68 seg_naps = [seg_naps; zeros(seglen - size(seg_naps,1), size(seg_naps, 2))];
|
dicklyon@604
|
69 end
|
dicklyon@604
|
70
|
dicklyon@604
|
71 % Shift new data into some or all of the layer buffers:
|
dicklyon@604
|
72 layer_array = SAI_UpdateBuffers(layer_array, seg_naps, seg_num);
|
dicklyon@604
|
73
|
dicklyon@604
|
74
|
dicklyon@604
|
75 for layer = n_layers:-1:1 % blend from coarse to fine
|
dicklyon@604
|
76 update_interval = layer_array(layer).update_interval;
|
dicklyon@604
|
77 if 0 == mod(seg_num, update_interval)
|
dicklyon@604
|
78 nap_buffer = real(layer_array(layer).nap_buffer);
|
dicklyon@604
|
79 n_buffer_times = size(nap_buffer, 1);
|
dicklyon@604
|
80 width = layer_array(layer).frame_width; % To render linear SAI to.
|
dicklyon@604
|
81 new_frame = zeros(n_ch, width);
|
dicklyon@604
|
82
|
dicklyon@604
|
83 % Make the window to use for all the channels at this layer.
|
dicklyon@604
|
84 layer_factor = 1.5;
|
dicklyon@604
|
85 window_size = layer_array(layer).window_width;
|
dicklyon@604
|
86 after_samples = layer_array(layer).future_lags;
|
dicklyon@604
|
87
|
dicklyon@604
|
88 window_range = (1:window_size) + ...
|
dicklyon@604
|
89 (n_buffer_times - window_size) - after_samples;
|
dicklyon@604
|
90 window = sin((1:window_size)' * pi / window_size);
|
dicklyon@604
|
91 % This should not go negative!
|
dicklyon@604
|
92 offset_range = (1:width) + ...
|
dicklyon@604
|
93 (n_buffer_times - width - window_size);
|
dicklyon@604
|
94 % CHECK
|
dicklyon@604
|
95 if any(offset_range < 0)
|
dicklyon@604
|
96 error;
|
dicklyon@604
|
97 end
|
dicklyon@604
|
98
|
dicklyon@604
|
99 % smooth across channels; more in later layers
|
dicklyon@604
|
100 smoothed_buffer = smooth1d(nap_buffer', 0.25*(layer - 2))';
|
dicklyon@604
|
101
|
dicklyon@604
|
102 % For each buffer column (channel), pick a trigger and align into SAI_frame
|
dicklyon@604
|
103 for ch = 1:n_ch
|
dicklyon@604
|
104 smooth_wave = smoothed_buffer(:, ch); % for the trigger
|
dicklyon@604
|
105
|
dicklyon@604
|
106 [peak_val, trigger_time] = max(smooth_wave(window_range) .* window);
|
dicklyon@604
|
107 nap_wave = nap_buffer(:, ch); % for the waveform
|
dicklyon@604
|
108 if peak_val <= 0 % just use window center instead
|
dicklyon@604
|
109 [peak_val, trigger_time] = max(window);
|
dicklyon@604
|
110 end
|
dicklyon@604
|
111 if layer == n_layers % mark the trigger points to display as imaginary.
|
dicklyon@604
|
112 layer_array(layer).nap_buffer(trigger_time + window_range(1) - 1, ch) = ...
|
dicklyon@604
|
113 layer_array(layer).nap_buffer(trigger_time + window_range(1) - 1, ch) + 1i;
|
dicklyon@604
|
114 end
|
dicklyon@604
|
115 new_frame(ch, :) = nap_wave(trigger_time + offset_range)';
|
dicklyon@604
|
116 end
|
dicklyon@604
|
117 composite_frame = SAI_BlendFrameIntoComposite(new_frame, ...
|
dicklyon@604
|
118 layer_array(layer), composite_frame);
|
dicklyon@604
|
119 end
|
dicklyon@604
|
120 end
|
dicklyon@604
|
121
|
dicklyon@604
|
122
|
dicklyon@604
|
123 lag_marginal = mean(composite_frame, 1); % means max out near 1 or 2
|
dicklyon@604
|
124 frame_bottom = zeros(size(composite_frame)); % will end up being 1/3
|
dicklyon@604
|
125 n_bottom_rows = size(frame_bottom, 1);
|
dicklyon@604
|
126 for height = 1:n_bottom_rows
|
dicklyon@604
|
127 big_ones = lag_marginal > 1*height/n_bottom_rows;
|
dicklyon@604
|
128 frame_bottom(n_bottom_rows - height + 1, big_ones) = 2; % 2 for black
|
dicklyon@604
|
129 end
|
dicklyon@604
|
130
|
dicklyon@604
|
131 if 0 == mod(seg_num, update_interval) || seg_num == 1
|
dicklyon@604
|
132 coc_gram = layer_array(end).nap_buffer';
|
dicklyon@604
|
133 [n_ch, n_width] = size(composite_frame);
|
dicklyon@604
|
134 coc_gram = [coc_gram, zeros(n_ch, n_width - size(coc_gram, 2))];
|
dicklyon@604
|
135 trigger_gram = 2 * (imag(coc_gram) ~= 0);
|
dicklyon@604
|
136 coc_gram = real(coc_gram);
|
dicklyon@604
|
137 end
|
dicklyon@604
|
138
|
dicklyon@604
|
139 display_frame = [coc_gram; trigger_gram; ...
|
dicklyon@604
|
140 composite_frame(floor(1:0.5:end), :); frame_bottom];
|
dicklyon@604
|
141
|
dicklyon@604
|
142 cmap = jet;
|
dicklyon@604
|
143 cmap = 1 - gray; % jet
|
dicklyon@604
|
144 figure(10)
|
dicklyon@604
|
145 image(32*display_frame);
|
dicklyon@604
|
146 colormap(cmap);
|
dicklyon@604
|
147
|
dicklyon@604
|
148 drawnow
|
dicklyon@604
|
149 imwrite(32*display_frame, cmap, sprintf('frames/frame%05d.png', seg_num));
|
dicklyon@604
|
150 end
|
dicklyon@604
|
151
|
dicklyon@604
|
152 num_frames = seg_num;
|
dicklyon@604
|
153
|
dicklyon@604
|
154 return
|
dicklyon@604
|
155
|
dicklyon@604
|
156
|
dicklyon@604
|
157
|
dicklyon@604
|
158
|