Skip to content

Commit 54765ce

Browse files
committed
Panalysis delay analysis
1 parent c898760 commit 54765ce

File tree

5 files changed

+145
-9
lines changed

5 files changed

+145
-9
lines changed

Panalysis-delay.png

31.3 KB
Loading

Panalysis.jsfx

Lines changed: 139 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ freemem = (buffer0 = freemem) + buffer_length;
1919
freemem = (buffer1 = freemem) + buffer_length;
2020
buffer_index = 0;
2121

22+
delay_fftsize = 2048;
23+
delay_bufferlength = delay_fftsize;
24+
freemem = (delay_bufferA = freemem) + delay_bufferlength*2;
25+
freemem = (delay_bufferB = freemem) + delay_bufferlength*2;
26+
freemem = (delay_bufferSum = freemem) + delay_bufferlength;
27+
2228
DISPLAY_SCREEN = 5;
2329
ui_reserve_image(DISPLAY_SCREEN);
2430

@@ -49,6 +55,74 @@ buffer_index >= buffer_length ? (
4955
buffer_index = 0;
5056
);
5157

58+
// Delay analysis
59+
60+
function perform_delay_analysis() local(i, ratio, window, rA, iA, rB, iB, mag2, factor, max_abs, max_index, max_phase) (
61+
action_delay_analysis_reset ? (
62+
i = 0;
63+
while (i < delay_bufferlength) (
64+
delay_bufferSum[i] = 0;
65+
i += 1;
66+
);
67+
action_delay_analysis_reset = 0;
68+
);
69+
70+
ratio = (delay_bufferindex/delay_bufferlength);
71+
window = 0.5 - 0.5*cos(ratio*2*$pi);
72+
73+
delay_bufferA[delay_bufferindex*2] = spl0*window;
74+
delay_bufferA[delay_bufferindex*2 + 1] = 0;
75+
delay_bufferB[delay_bufferindex*2] = spl1*window;
76+
delay_bufferB[delay_bufferindex*2 + 1] = 0;
77+
78+
delay_bufferindex += 1;
79+
delay_bufferindex >= delay_bufferlength ? (
80+
delay_bufferindex = 0;
81+
82+
fft(delay_bufferA, delay_fftsize);
83+
fft(delay_bufferB, delay_fftsize);
84+
i = 0;
85+
while (i < delay_bufferlength) (
86+
rA = delay_bufferA[2*i];
87+
iA = delay_bufferA[2*i + 1];
88+
rB = delay_bufferB[2*i];
89+
iB = delay_bufferB[2*i + 1];
90+
mag2 = (rA*rA + iA*iA)*(rB*rB + iB*iB);
91+
factor = 1/(max(mag2, 0.000001))/delay_fftsize;
92+
delay_bufferA[2*i] = (rA*rB + iA*iB)*factor;
93+
delay_bufferA[2*i + 1] = (-rA*iB + iA*rB)*factor;
94+
i += 1;
95+
);
96+
ifft(delay_bufferA, delay_fftsize);
97+
98+
max_abs = 0;
99+
max_phase = 1;
100+
max_index = 0;
101+
102+
i = 0;
103+
while (i < delay_bufferlength) (
104+
i2 = (i + delay_bufferlength/2)%delay_bufferlength;
105+
value = delay_bufferSum[i2] += delay_bufferA[2*i];
106+
abs(value) >= max_abs ? (
107+
max_abs = abs(delay_bufferSum[i2]);
108+
max_index = i;
109+
max_phase = delay_bufferSum[i2] < 0 ? -1 : 1;
110+
);
111+
i += 1;
112+
);
113+
114+
delay_samples = max_index;
115+
delay_samples > delay_bufferlength/2 ? (
116+
delay_samples -= delay_bufferlength;
117+
);
118+
delay_phase = max_phase;
119+
);
120+
);
121+
122+
delay_analysis_active ? (
123+
perform_delay_analysis();
124+
);
125+
52126
@gfx 510 660
53127

54128
function update_display() local(dest, imgW, imgH, t, dt, i, x, y, cx, cy, cr, mag, alpha) (
@@ -63,17 +137,17 @@ function update_display() local(dest, imgW, imgH, t, dt, i, x, y, cx, cy, cr, ma
63137
gfx_rect(0, 0, imgW, imgH);
64138
);
65139
gfx_dest = DISPLAY_SCREEN;
66-
140+
67141
t = time_precise();
68142
dt = t - last_update_time;
69143
debug.dt = dt;
70144
last_update_time = t;
71-
72-
// Fade
145+
146+
// Fade
73147
alpha = 1 - exp(-dt/fade_time);
74148
gfx_gradrect(0, 0, imgW, imgH, 0, 0, 0, alpha);
75149
gfx_a = sqrt(alpha);
76-
150+
77151
mag = pow(10, reference_db/20);
78152
cx = imgW*0.5;
79153
cy = imgH*0.5;
@@ -91,7 +165,7 @@ function update_display() local(dest, imgW, imgH, t, dt, i, x, y, cx, cy, cr, ma
91165
i += 1;
92166
);
93167
buffer_index = 0;
94-
168+
95169
gfx_dest = dest;
96170
gfx_x = ui_left();
97171
gfx_y = ui_top();
@@ -154,8 +228,19 @@ function labelled_switch_vertical(value, label, valuestring) (
154228

155229
control_start("main", "tron");
156230

231+
delay_analysis_active = (ui_screen() == "delay-analysis");
232+
157233
ui_screen() == "main" ? (
158234
ui_split_top(150);
235+
ui_split_righttext("Delay");
236+
ui_split_bottomtext();
237+
control_button("Delay") ? (
238+
ui_screen_open("delay-analysis");
239+
action_delay_analysis_reset = 1;
240+
g_zoom = 1;
241+
);
242+
ui_pop();
243+
ui_pop();
159244
ui_split_rightratio(2/5);
160245
control_group("display");
161246
ui_split_leftratio(1/2);
@@ -164,23 +249,68 @@ ui_screen() == "main" ? (
164249
fade_time = labelled_dial_vertical(fade_time, 0.01, 1, 4.6, "Fade", floor(fade_time*1000 + 0.5), "%ims", 0.1, 1000000);
165250
ui_pop();
166251
ui_pop();
167-
252+
168253
control_group("stereo field");
169254
ui_split_leftratio(1/3);
170255
default_width = (rot_cos + rot_sin > 0) ? (rot_cos - rot_sin)/(rot_cos + rot_sin) : 0;
171256
default_width > 1 ? default_width = 1/default_width;
172257
slider1 = labelled_dial_vertical(slider1, 0, 2, 0, "Width", floor(slider1*100 + 0.5), "%i%%", default_width, default_width);
173258
ui_split_next();
174-
slider2 = labelled_dial_vertical(slider2, -1, 1, 0, "Pan (rotate)", abs(slider2*100), slider2 < 0 ? "%i%% L" : slider2 > 0 ? "%i%% R" : "0%%", 0, 1000000);
259+
slider2 = labelled_dial_vertical(slider2, -1, 1, 0, "Rotate", abs(slider2*100), slider2 < 0 ? "%i%% L" : slider2 > 0 ? "%i%% R" : "0%%", 0, 1000000);
175260
ui_split_next();
176261
slider3 = labelled_switch_vertical(slider3, "Invert", slider3 ? "on" : "off");
177262
ui_pop();
178263
ui_pop();
179-
264+
180265
update_display();
266+
) : ui_screen() == "delay-analysis" ? (
267+
control_dialog("Delay analysis", gfx_w*0.9, gfx_h*0.5, "done", -1);
268+
269+
/*
270+
ui_split_left(60);
271+
ui_padright();
272+
ui_split_bottom(100);
273+
g_zoom = labelled_dial_vertical(g_zoom, 1, 0.01, 0, "Zoom", 0, "", 1, 1);
274+
ui_pop();
275+
ui_pop();
276+
*/
277+
278+
control_background_technical();
279+
ui_graph_step(delay_bufferSum, delay_bufferlength, 1, 0, 0);
280+
ui_hover() ? (
281+
mouse_sample = ceil((ui_mouse_xratio() - 0.5)*delay_bufferlength);
282+
ui_align(1, 1);
283+
ui_textnumber(mouse_sample, "%i samples");
284+
ui_align(0, 1);
285+
ui_textnumber(mouse_sample/srate*1000, "%fms");
286+
287+
ui_colora(255, 255, 255, 0.5);
288+
gfx_line(mouse_x, ui_top(), mouse_x, ui_bottom());
289+
);
290+
291+
ui_push();
292+
ui_color(255, 255, 255);
293+
ui_align(delay_samples >= delay_bufferlength*0.1 ? 0 : 1, 0);
294+
ui_split_top(gfx_texth);
295+
g_ms = delay_samples/srate*1000;
296+
g_ms >= 0 ? (
297+
ui_textnumber(g_ms, "Ch1 is %fms ahead");
298+
) : (
299+
ui_textnumber(-g_ms, "Ch0 is %fms ahead");
300+
);
301+
ui_pop();
302+
ui_split_top(gfx_texth);
303+
ui_textnumber(delay_samples, "(peak at %i samples)");
304+
ui_pop();
305+
ui_split_top(gfx_texth);
306+
ui_text(delay_phase > 0 ? "Phase: aligned" : "Phase: inverted");
307+
ui_pop();
308+
ui_pop();
309+
310+
control_finish_technical();
181311
) : control_system();
182312

183313
@serialize
184314

185315
file_var(0, fade_time);
186-
file_var(0, reference_db);
316+
file_var(0, reference_db);

Panalysis.png

61.1 KB
Loading

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ It's possible to rotate the field such that hard-left or hard-right inputs will
117117

118118
![screenshot](Panalysis 2.png)
119119

120+
It also includes a delay analyser, for when one channel is ahead/behind or out of phase with the other - this can be useful when trying to get phases to agree in a multi-mic setup.
121+
122+
![screenshot](Panalysis-delay.png)
123+
120124
## Learning Sampler
121125

122126
This sampler records samples from the incoming audio when in learning mode (selected by a controller switch), and plays them back when in playback mode.

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ <h2 id="padsynth">PadSynth</h2><div class="section section2">
294294
<p><img src="Panalysis.png" alt="screenshot"></p>
295295
<p>It&#39;s possible to rotate the field such that hard-left or hard-right inputs will output &quot;inside out&quot; (opposite phase in both output channels). In this case, the Width dial will turn red - double-click the dial to reset it to the maximum &quot;safe&quot; width:</p>
296296
<p><img src="Panalysis 2.png" alt="screenshot"></p>
297+
<p>It also includes a delay analyser, for when one channel is ahead/behind or out of phase with the other - this can be useful when trying to get phases to agree in a multi-mic setup.</p>
298+
<p><img src="Panalysis-delay.png" alt="screenshot"></p>
297299
</div><h2 id="learning-sampler">Learning Sampler</h2><div class="section section2">
298300
<p>This sampler records samples from the incoming audio when in learning mode (selected by a controller switch), and plays them back when in playback mode.</p>
299301
<p>To record the samples, set the appropriate controller to a value of 64 or above. While this controller is down, when you play a MIDI note the sampler will remember the start/end positions in the buffer. When you have recorded all the samples, reset the controller to 0, and it will enter playback mode.</p>

0 commit comments

Comments
 (0)