dicklyon@615
|
1 % Copyright 2013, Google, Inc.
|
dicklyon@615
|
2 % Author: Richard F. Lyon
|
dicklyon@615
|
3 %
|
dicklyon@615
|
4 % This Matlab file is part of an implementation of Lyon's cochlear model:
|
dicklyon@615
|
5 % "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
dicklyon@615
|
6 % to supplement Lyon's upcoming book "Human and Machine Hearing"
|
dicklyon@615
|
7 %
|
dicklyon@615
|
8 % Licensed under the Apache License, Version 2.0 (the "License");
|
dicklyon@615
|
9 % you may not use this file except in compliance with the License.
|
dicklyon@615
|
10 % You may obtain a copy of the License at
|
dicklyon@615
|
11 %
|
dicklyon@615
|
12 % http://www.apache.org/licenses/LICENSE-2.0
|
dicklyon@615
|
13 %
|
dicklyon@615
|
14 % Unless required by applicable law or agreed to in writing, software
|
dicklyon@615
|
15 % distributed under the License is distributed on an "AS IS" BASIS,
|
dicklyon@615
|
16 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
dicklyon@615
|
17 % See the License for the specific language governing permissions and
|
dicklyon@615
|
18 % limitations under the License.
|
dicklyon@615
|
19
|
dicklyon@615
|
20 function [frame_rate, num_frames] = SAI_RunLayered(CF, input_waves)
|
ronw@675
|
21 % function [CF, SAI_movie] = CARFAC_RunLayered(CF, input_waves)
|
dicklyon@615
|
22 % This function runs the CARFAC and generates an SAI movie, dumped as PNG
|
dicklyon@615
|
23 % files for now.
|
ronw@676
|
24 %
|
ronw@676
|
25 % Computes a "layered" SAI composed of images computed at several
|
ronw@676
|
26 % time scales.
|
ronw@676
|
27 %
|
dicklyon@615
|
28 % Layer 1 is not decimated from the 22050 rate; subsequent layers have
|
ronw@675
|
29 % smoothing and 2X decimation each. All layers get composited together
|
dicklyon@615
|
30 % into movie frames.
|
dicklyon@615
|
31
|
dicklyon@615
|
32 n_ch = CF.n_ch;
|
dicklyon@615
|
33 [n_samp, n_ears] = size(input_waves);
|
dicklyon@615
|
34 if n_ears ~= CF.n_ears
|
dicklyon@615
|
35 error('bad number of input_waves channels passed to CARFAC_Run')
|
dicklyon@615
|
36 end
|
dicklyon@615
|
37 fs = CF.fs;
|
dicklyon@615
|
38
|
dicklyon@665
|
39 seglen = round(fs / 30); % Pick about 30 fps
|
dicklyon@665
|
40 frame_rate = fs / seglen;
|
ronw@676
|
41 n_segs = ceil(n_samp / seglen);
|
ronw@676
|
42
|
dicklyon@665
|
43
|
dicklyon@615
|
44 % Design the composite log-lag SAI using these parameters and defaults.
|
dicklyon@665
|
45 n_layers = 15
|
dicklyon@665
|
46 width_per_layer = 36;
|
dicklyon@665
|
47 [layer_array, total_width, lags] = ...
|
dicklyon@665
|
48 SAI_DesignLayers(n_layers, width_per_layer, seglen);
|
dicklyon@665
|
49
|
dicklyon@665
|
50 % Find where in the lag curve corresponds to the piano black keys:
|
dicklyon@665
|
51 pitches = fs ./ lags;
|
dicklyon@665
|
52 key_indices = [];
|
dicklyon@665
|
53 df = log(2)/width_per_layer;
|
dicklyon@665
|
54 for f = [BlackKeyFrequencies, 8, 4, 2, 1-df, 1, 1+df, 0.5, 0.25, 0.125, ...
|
dicklyon@665
|
55 -2000, -1000, -500, -250, -125]; % Augment with beat.
|
dicklyon@665
|
56 [dist, index] = min((f - pitches).^2);
|
dicklyon@665
|
57 key_indices = [key_indices, index];
|
dicklyon@665
|
58 end
|
dicklyon@665
|
59 piano = zeros(1, total_width);
|
dicklyon@665
|
60 piano(key_indices) = 1;
|
dicklyon@665
|
61 piano = [piano; piano; piano];
|
dicklyon@665
|
62
|
dicklyon@615
|
63
|
dicklyon@615
|
64 % Make the composite SAI image array.
|
dicklyon@615
|
65 composite_frame = zeros(n_ch, total_width);
|
dicklyon@615
|
66
|
dicklyon@615
|
67 % Make the history buffers in the layers_array:
|
dicklyon@615
|
68 for layer = 1:n_layers
|
dicklyon@615
|
69 layer_array(layer).nap_buffer = zeros(layer_array(layer).buffer_width, n_ch);
|
dicklyon@615
|
70 layer_array(layer).nap_fraction = 0; % leftover fraction to shift in.
|
dicklyon@619
|
71 % The SAI frame is transposed to be image-like.
|
dicklyon@619
|
72 layer_array(layer).frame = zeros(n_ch, layer_array(layer).frame_width);
|
dicklyon@615
|
73 end
|
dicklyon@615
|
74
|
dicklyon@665
|
75 n_marginal_rows = 100;
|
dicklyon@619
|
76 marginals = [];
|
dicklyon@665
|
77 average_composite = 0;
|
dicklyon@619
|
78
|
dicklyon@665
|
79 future_lags = layer_array(1).future_lags;
|
dicklyon@665
|
80 % marginals_frame = zeros(total_width - future_lags + 2*n_ch, total_width);
|
dicklyon@665
|
81 marginals_frame = zeros(n_ch, total_width);
|
dicklyon@665
|
82
|
dicklyon@615
|
83 for seg_num = 1:n_segs
|
ronw@676
|
84 % seg_range is the range of input sample indices for this segment
|
dicklyon@615
|
85 if seg_num == n_segs
|
dicklyon@615
|
86 % The last segment may be short of seglen, but do it anyway:
|
ronw@676
|
87 seg_range = (seglen*(seg_num - 1) + 1):n_samp;
|
dicklyon@615
|
88 else
|
ronw@676
|
89 seg_range = seglen*(seg_num - 1) + (1:seglen);
|
dicklyon@615
|
90 end
|
dicklyon@615
|
91 % Process a segment to get a slice of decim_naps, and plot AGC state:
|
ronw@676
|
92 [seg_naps, CF] = CARFAC_Run_Segment(CF, input_waves(seg_range, :));
|
dicklyon@615
|
93
|
dicklyon@615
|
94 seg_naps = max(0, seg_naps); % Rectify
|
dicklyon@615
|
95
|
dicklyon@615
|
96 if seg_num == n_segs % pad out the last result
|
dicklyon@615
|
97 seg_naps = [seg_naps; zeros(seglen - size(seg_naps,1), size(seg_naps, 2))];
|
dicklyon@615
|
98 end
|
dicklyon@615
|
99
|
dicklyon@615
|
100 % Shift new data into some or all of the layer buffers:
|
dicklyon@615
|
101 layer_array = SAI_UpdateBuffers(layer_array, seg_naps, seg_num);
|
dicklyon@615
|
102
|
dicklyon@665
|
103 for layer = n_layers:-1:1 % Stabilize and blend from coarse to fine
|
dicklyon@615
|
104 update_interval = layer_array(layer).update_interval;
|
dicklyon@615
|
105 if 0 == mod(seg_num, update_interval)
|
dicklyon@619
|
106 layer_array(layer) = SAI_StabilizeLayer(layer_array(layer));
|
dicklyon@619
|
107 composite_frame = SAI_BlendFrameIntoComposite( ...
|
dicklyon@615
|
108 layer_array(layer), composite_frame);
|
dicklyon@615
|
109 end
|
dicklyon@615
|
110 end
|
dicklyon@665
|
111
|
dicklyon@665
|
112 average_composite = average_composite + ...
|
dicklyon@665
|
113 0.01 * (composite_frame - average_composite);
|
dicklyon@619
|
114
|
dicklyon@619
|
115 if isempty(marginals)
|
dicklyon@665
|
116 marginals = zeros(n_marginal_rows, total_width);
|
dicklyon@619
|
117 end
|
dicklyon@619
|
118 for row = n_marginal_rows:-1:11
|
dicklyon@619
|
119 % smooth from row above (lower number)
|
dicklyon@619
|
120 marginals(row, :) = marginals(row, :) + ...
|
dicklyon@665
|
121 2^((10 - row)/8) * (1.01*marginals(row - 1, :) - marginals(row, :));
|
dicklyon@619
|
122 end
|
dicklyon@615
|
123 lag_marginal = mean(composite_frame, 1); % means max out near 1 or 2
|
dicklyon@665
|
124 lag_marginal = lag_marginal - 0.75*smooth1d(lag_marginal, 30)';
|
dicklyon@665
|
125
|
dicklyon@665
|
126 freq_marginal = mean(layer_array(1).nap_buffer);
|
dicklyon@665
|
127 % emphasize local peaks:
|
dicklyon@665
|
128 freq_marginal = freq_marginal - 0.5*smooth1d(freq_marginal, 5)';
|
dicklyon@665
|
129
|
dicklyon@665
|
130
|
dicklyon@665
|
131 % marginals_frame = [marginals_frame(:, 2:end), ...
|
dicklyon@665
|
132 % [lag_marginal(1:(end - future_lags)), freq_marginal(ceil((1:(2*end))/2))]'];
|
dicklyon@665
|
133 marginals_frame = [marginals_frame(:, 2:end), freq_marginal(1:end)'];
|
dicklyon@665
|
134
|
dicklyon@619
|
135 for row = 10:-1:1
|
dicklyon@665
|
136 marginals(row, :) = lag_marginal - (10 - row) / 40;
|
dicklyon@615
|
137 end
|
dicklyon@615
|
138
|
dicklyon@615
|
139 if 0 == mod(seg_num, update_interval) || seg_num == 1
|
dicklyon@615
|
140 coc_gram = layer_array(end).nap_buffer';
|
dicklyon@615
|
141 [n_ch, n_width] = size(composite_frame);
|
dicklyon@615
|
142 coc_gram = [coc_gram, zeros(n_ch, n_width - size(coc_gram, 2))];
|
dicklyon@665
|
143 coc_gram = coc_gram(:, (end-total_width+1):end);
|
dicklyon@615
|
144 end
|
dicklyon@615
|
145
|
dicklyon@665
|
146 display_frame = [ ... % coc_gram; ...
|
dicklyon@665
|
147 4 * marginals_frame; ...
|
dicklyon@665
|
148 composite_frame(ceil((1:(2*end))/2), :); ...
|
dicklyon@665
|
149 piano; ...
|
dicklyon@665
|
150 10*max(0,marginals)];
|
dicklyon@615
|
151
|
dicklyon@615
|
152 cmap = jet;
|
dicklyon@615
|
153 cmap = 1 - gray; % jet
|
dicklyon@615
|
154 figure(10)
|
dicklyon@615
|
155 image(32*display_frame);
|
dicklyon@615
|
156 colormap(cmap);
|
dicklyon@615
|
157
|
dicklyon@615
|
158 drawnow
|
dicklyon@615
|
159 imwrite(32*display_frame, cmap, sprintf('frames/frame%05d.png', seg_num));
|
dicklyon@615
|
160 end
|
dicklyon@615
|
161
|
dicklyon@615
|
162 num_frames = seg_num;
|
dicklyon@615
|
163
|
dicklyon@615
|
164 return
|
dicklyon@615
|
165
|
dicklyon@615
|
166
|
dicklyon@665
|
167 function frequencies = BlackKeyFrequencies
|
dicklyon@665
|
168 black_indices = [];
|
dicklyon@665
|
169 for index = 0:87
|
dicklyon@665
|
170 if any(mod(index, 12) == [1 4 6 9 11])
|
dicklyon@665
|
171 black_indices = [black_indices, index];
|
dicklyon@665
|
172 end
|
dicklyon@665
|
173 end
|
dicklyon@665
|
174 frequencies = 27.5 * 2.^(black_indices / 12);
|
dicklyon@615
|
175
|
dicklyon@615
|
176
|