Mercurial > hg > aimc
comparison matlab/bmm/carfac/SAI_DesignLayers.m @ 604:ec3a1c74ec54
Add files for making log-lag SAI from CARFAC's NAP output. The file
SAI_RunLayered.m dumps frames to PNG files. The hacking script calls
ffmpeg to assemble them with the soundtrack into a movie.
author | dicklyon@google.com |
---|---|
date | Thu, 09 May 2013 03:48:44 +0000 |
parents | |
children | fc353426eaad |
comparison
equal
deleted
inserted
replaced
603:087f3b3c36d3 | 604:ec3a1c74ec54 |
---|---|
1 % Copyright 2013, Google, Inc. | |
2 % Author: Richard F. Lyon | |
3 % | |
4 % This Matlab file is part of an implementation of Lyon's cochlear model: | |
5 % "Cascade of Asymmetric Resonators with Fast-Acting Compression" | |
6 % to supplement Lyon's upcoming book "Human and Machine Hearing" | |
7 % | |
8 % Licensed under the Apache License, Version 2.0 (the "License"); | |
9 % you may not use this file except in compliance with the License. | |
10 % You may obtain a copy of the License at | |
11 % | |
12 % http://www.apache.org/licenses/LICENSE-2.0 | |
13 % | |
14 % Unless required by applicable law or agreed to in writing, software | |
15 % distributed under the License is distributed on an "AS IS" BASIS, | |
16 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
17 % See the License for the specific language governing permissions and | |
18 % limitations under the License. | |
19 | |
20 function [layer_array, total_width] = SAI_DesignLayers( ... | |
21 n_layers, width_per_layer) | |
22 % function [layer_array, total_width] = SAI_DesignLayers( ... | |
23 % n_layers, width_per_layer) | |
24 % | |
25 % The layer_array is a struct array containing an entry for each layer | |
26 % in a layer of power-of-2 decimated pieces of SAI that get composited | |
27 % into a log-lag SAI. | |
28 % Each struct has the following fields: | |
29 % .width - number of pixels occupied in the final composite SAI, | |
30 % not counting the overlap into pixels counted for other layers. | |
31 % .target_indices - column indices in the final composite SAI, | |
32 % counting the overlap region(s). | |
33 % .lag_curve - for each point in the final composite SAI, the float index | |
34 % in the layer's buffer to interp from. | |
35 % .alpha - the blending coefficent, mostly 1, tapering toward 0 in the overlap | |
36 % region(s). | |
37 % Layer 1 has no overlap to it right, and layer n_layers has none to its | |
38 % left, but sizes of the target_indices, lag_curve, and alpha vectors are | |
39 % otherwise width + left_overlap + right_overlap. The total width of the | |
40 % final composite SAI is the sum of the widths. | |
41 % Other fields could be added to hold state, such as history buffers for | |
42 % each layer, or those could go in state struct array... | |
43 | |
44 | |
45 % Elevate these to a param struct? | |
46 if nargin < 1 | |
47 n_layers = 11 | |
48 end | |
49 if nargin < 2 | |
50 width_per_layer = 32; % resolution "half life" in space | |
51 end | |
52 future_lags = 3 * width_per_layer; | |
53 width_first_layer = future_lags + 2 * width_per_layer; | |
54 width_extra_last_layer = 2 * width_per_layer; | |
55 left_overlap = 15; | |
56 right_overlap = 15; | |
57 first_window_width = 400; % or maybe use seglen? or 0.020 * fs? | |
58 min_window_width = 2*width_per_layer; % or somewhere on that order | |
59 window_exponent = 1.4; | |
60 alpha_max = 0.5; | |
61 | |
62 % Start with NAP_samples_per_SAI_sample, declining to 1 from here: | |
63 max_samples_per = 2^(n_layers - 1); | |
64 % Construct the overall lag-warping function: | |
65 NAP_samples_per_SAI_sample = [ ... | |
66 max_samples_per * ones(1, width_extra_last_layer), ... | |
67 max_samples_per * ... | |
68 2 .^ (-(1:(width_per_layer * (n_layers - 1))) / width_per_layer), ... | |
69 ones(1, width_first_layer)]; | |
70 | |
71 % Each layer needs a lag_warp for a portion of that, divided by | |
72 % 2^(layer-1), where the portion includes some overlap into its neighbors | |
73 % with higher layer numbers on left, lower on right. | |
74 | |
75 % Layer 1, rightmost, representing recent, current and near-future (negative | |
76 % lag) relative to trigger time, has 1 NAP sample per SAI sample. Other | |
77 % layers map more than one NAP sample into 1 SAI sample. Layer 2 is | |
78 % computed as 2X decimated, 2 NAP samples per SAI sample, but then gets | |
79 % interpolated to between 1 and 2 (and outside that range in the overlap | |
80 % regions) to connect up smoothly. Each layer is another 2X decimated. | |
81 % The last layer limits out at 1 (representing 2^(n_layers) SAI samples) | |
82 % at the width_extra_last_layer SAI samples that extend to the far past. | |
83 | |
84 layer_array = []; % to hold a struct array | |
85 for layer = 1:n_layers | |
86 layer_array(layer).width = width_per_layer; | |
87 layer_array(layer).left_overlap = left_overlap; | |
88 layer_array(layer).right_overlap = right_overlap; | |
89 layer_array(layer).future_lags = 0; | |
90 % Layer decimation factors: 1 1 1 1 2 2 2 4 4 4 8 ... | |
91 layer_array(layer).update_interval = max(1, 2 ^ floor((layer - 2) / 3)); | |
92 end | |
93 % Patch up the exceptions. | |
94 layer_array(1).width = width_first_layer; | |
95 layer_array(end).width = layer_array(end).width + width_extra_last_layer; | |
96 layer_array(1).right_overlap = 0; | |
97 layer_array(end).left_overlap = 0; | |
98 layer_array(1).future_lags = future_lags; | |
99 | |
100 % For each layer, working backwards, from left, find the locations they | |
101 % they render into in the final SAI. | |
102 offset = 0; | |
103 for layer = n_layers:-1:1 | |
104 width = layer_array(layer).width; | |
105 left = layer_array(layer).left_overlap; | |
106 right = layer_array(layer).right_overlap; | |
107 | |
108 % Size of the vectors needed. | |
109 n_final_lags = left + width + right; | |
110 layer_array(layer).n_final_lags = n_final_lags; | |
111 | |
112 % Integer indices into the final composite SAI for this layer. | |
113 target_indices = ((1 - left):(width + right)) + offset; | |
114 layer_array(layer).target_indices = target_indices; | |
115 | |
116 % Make a blending coefficient alpha, ramped in the overlap zone. | |
117 alpha = ones(1, n_final_lags); | |
118 alpha(1:left) = alpha(1:left) .* (1:left)/(left + 1); | |
119 alpha(end + 1 - (1:right)) = ... | |
120 alpha(end + 1 - (1:right)) .* (1:right)/(right + 1); | |
121 layer_array(layer).alpha = alpha * alpha_max; | |
122 | |
123 offset = offset + width; % total width from left through this layer. | |
124 end | |
125 total_width = offset; % Return size of SAI this will make. | |
126 | |
127 % for each layer, fill in its lag-resampling function for interp1: | |
128 for layer = 1:n_layers | |
129 width = layer_array(layer).width; | |
130 left = layer_array(layer).left_overlap; | |
131 right = layer_array(layer).right_overlap; | |
132 | |
133 % Still need to adjust this to make lags match at edges: | |
134 target_indices = layer_array(layer).target_indices; | |
135 samples_per = NAP_samples_per_SAI_sample(target_indices); | |
136 % Accumulate lag backwards from the zero-lag point, convert to units of | |
137 % samples in the current layer. | |
138 lag_curve = (cumsum(samples_per(end:-1:1))) / 2^(layer-1); | |
139 lag_curve = lag_curve(end:-1:1); % Turn it back to corrent order. | |
140 % Now adjust it to match the zero-lag point or a lag-point from | |
141 % previous layer, and reverse it back into place. | |
142 if layer == 1 | |
143 lag_adjust = lag_curve(end) - 0; | |
144 else | |
145 % Align right edge to previous layer's left edge, adjusting for 2X | |
146 % scaling factor difference. | |
147 lag_adjust = lag_curve(end - right) - last_left_lag / 2; | |
148 end | |
149 lag_curve = lag_curve - lag_adjust; | |
150 % lag_curve is now offsets from right end of layer's frame. | |
151 layer_array(layer).lag_curve = lag_curve; | |
152 % Specify number of point to generate in pre-warp frame. | |
153 layer_array(layer).frame_width = ceil(1 + lag_curve(1)); | |
154 if layer < n_layers % to avoid the left = 0 unused end case. | |
155 % A point to align next layer to. | |
156 last_left_lag = lag_curve(left) - layer_array(layer).future_lags; | |
157 end | |
158 | |
159 % Specify a good window width (in history buffer, for picking triggers) | |
160 % in samples for this layer, exponentially approaching minimum. | |
161 layer_array(layer).window_width = round(min_window_width + ... | |
162 first_window_width / window_exponent^(layer - 1)); | |
163 | |
164 % Say about how long the history buffer needs to be to shift any trigger | |
165 % location in the range of the window to a fixed location. Assume | |
166 % using two window placements overlapped 50%. | |
167 n_triggers = 2; | |
168 layer_array(layer).buffer_width = layer_array(layer).frame_width + ... | |
169 ceil((1 + (n_triggers - 1)/2) * layer_array(layer).window_width); | |
170 end | |
171 | |
172 return | |
173 |