Skip to content

Commit

Permalink
protosynth: improve voice stealing
Browse files Browse the repository at this point in the history
  • Loading branch information
JoepVanlier committed May 22, 2024
1 parent 4a86b01 commit 32491f3
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ global(CUSTOM_SLIDER)

function init_midi(freemem)
instance(note_mem,
active_note_mem, active_note_vel, active_note_state, note_history, sustaining, sustain_history, num_sustain,
active_note_mem, active_note_vel, active_note_state, note_history, sustaining, sustain_history, num_sustain, note_release_mem,
max_notes, notes_on, pitchbend, modwheel)
global()
(
max_notes = 12;
freemem = (note_mem = freemem) + 32768;
freemem = (active_note_mem = freemem) + 4096;
freemem = (active_note_mem = freemem) + 2048;
freemem = (note_release_mem = freemem) + 2048;
freemem = (active_note_vel = freemem) + 4096;
freemem = (active_note_state = freemem) + 4096;
freemem = (note_history = freemem) + 4096;
freemem = (sustain_history = freemem) + 4096;
memset(active_note_mem, 0, 128);
memset(note_release_mem, 0, 128);
memset(sustain_history, 0, 128);
memset(note_mem, 0, 32768);
notes_on = 0;
Expand Down Expand Up @@ -161,7 +163,7 @@ local(offset, msg1, msg2, msg3, note_on, note_off, mwCC, pb, lastpb, idx, found)

function midi_clear_notes()
local(i)
instance(notes_on, active_note_mem, active_note_state, active_note_vel, note_history, max_notes)
instance(notes_on, active_note_mem, active_note_state, active_note_vel, note_history, max_notes, note_release_mem, release_idx)
global()
(
// Terminate all notes
Expand All @@ -174,6 +176,8 @@ global()
active_note_state[i] = -1;
//active_note_vel[i] = 0;
note_history[i] = 0;
release_idx += 1;
note_release_mem[i] = release_idx;
);
i += 1;
);
Expand All @@ -183,7 +187,7 @@ global()
// midi.notes_remain
function midi_sample(respond_to_notes)
instance(note_next, curSample, note_ptr, notes_remain, notes_on, note, velocity,
active_note_mem, active_note_vel, active_note_state, note_history,
active_note_mem, active_note_vel, active_note_state, note_history, note_release_mem, release_idx,
cur_cc, cc_target, max_notes, modwheel, pitchbend, last_vel, last_slot)
local(i, i2, found, current_item, change, not_released)
global(MAX_STRING, voice_thievery, glide)
Expand Down Expand Up @@ -223,32 +227,49 @@ global(MAX_STRING, voice_thievery, glide)
found = 1337;

i = last_slot + 1;
(i >= MAX_STRING) ? i = 0;
(i >= MAX_NOTES) ? i = 0;

!voice_thievery ? (
// Try just cycling first.
while((found == 1337) && (i < MAX_STRING))
while((found == 1337) && (i < MAX_NOTES))
(
(active_note_mem[i] == 0) ? found = i;
i += 1;
);
);

i = 0;
while((found == 1337) && (i < max_notes))
while((found == 1337) && (i < MAX_NOTES))
(
(active_note_mem[i] == 0) ? found = i;
i += 1;
);

// Try cycling through released notes next
(found > MAX_NOTES) ? (
i = 0;
while((found == 1337) && (i < max_notes))
(
(active_note_mem[i] < 0) ? found = i;
i = 0;
i2 = 90071992547409; // 9007199254740992;

// Find the oldest one to recycle
loop(MAX_NOTES,
(active_note_mem[i] < 0) ? (
(note_release_mem[i] < i2) ? (
i2 = note_release_mem[i];
found = i;
);
);
i += 1;
);

// We are about to recycle an existing one, so we need to drop the old one from the note history
(found < MAX_NOTES) ? (
i2 = found;
loop(max_notes,
note_history[i2] = note_history[i2 + 1];
i2 += 1;
);
notes_on -= 1;
);
);

(found <= MAX_NOTES) ? (
Expand Down Expand Up @@ -295,6 +316,10 @@ global(MAX_STRING, voice_thievery, glide)
);
);
active_note_mem[i] = -abs(active_note_mem[i]); // Released

release_idx += 1;
note_release_mem[i] = release_idx;

//active_note_vel[i] = 0;
active_note_state[i] = -1; /* Release state */
change = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ local(
instance(res, iter, v0n, v1, v2, k, wc, g_v1, mo0, mo1, mo2, env_est)
(
v0 = v0 + v0 + v0;
env_est = max(0.5, min(1.0, max(abs(v0 + v0), 0.9998 * env_est)));
env_est = max(0.5, min(1.0, max(abs(v0 + v0), 0.9998 * env_est))); // TODO: make this samplerate independent
k_factor = 1.0 + preamp * (1.0 - k) * 0.2;
k_dim = (k - k_factor) * env_est + (1 - env_est);

Expand Down Expand Up @@ -925,6 +925,94 @@ instance(res, iter, v0n, v1, v2, k, wc, g_v1, mo0, mo1, mo2, env_est)
mo0 * v0 + mo1 * v1 + mo2 * v2
);

function c_svf3(x)
local()
global()
instance()
(
x / (1 + abs(0.1 * x))
);

function dc_svf3(x)
local(ax)
global()
instance()
(
ax = 1.0 / (0.1 * abs(x) + 1);
ax * ax
);

function f_svf3(x)
local()
global()
instance()
(
x / (1 + abs(0.1 * x))
);

function df_svf3(x)
local(ax)
global()
instance()
(
ax = 1.0 / (0.1 * abs(x) + 1);
ax * ax
);

function _eval_svf3(v0)
global(epsilon, maxiter_svf, preamp)
local(
f1, f2, fh1, fh2,
f1_const, f2_const, /* Constant part of the implicit equation */
norm,
a, b, c, d, /* Jacobian elements */
dg_v1, cterm,
k_factor, k_dim, qsq,
)
instance(res, iter, v0n, v1, v2, k, wc, g_v1, mo0, mo1, mo2, env_est, wcm, v_lp)
(
v0 = v0 + v0 + v0;
env_est = max(0.5, min(1.5, max(abs(v0 + v0), 0.9998 * env_est))); // TODO: make this samplerate independent
k_factor = 1.0 + preamp * (1.0 - k) * 0.2;
k_dim = (k - k_factor) * env_est + (1 - env_est);

v_lp = v_lp * 0.999 + 0.001 * (v2 - 0.01 * rand()); // TODO: make this samplerate independent
wcm = min(wc * (0.9 + 0.3 * abs(v_lp)), 0.98);

/* Calculate fixed stuff from previous iteration */
f1_const = wcm * c_svf3(v0n - v2 - 2 * (v1 + f_svf3(g_v1 * k_dim))) + v1;
f2_const = wcm * g_v1 + v2;

v0n = v0;

iter = 0;
while(
iter += 1;

g_v1 = v1 / (1.0 + abs(v1));
dg_v1 = dg_svf2(v1);

// Residual
cterm = v0 - v2 - 2 * (v1 + f_svf3(g_v1 * k_dim));
f1 = wcm * c_svf3(cterm) - v1 + f1_const;
f2 = wcm * g_v1 - v2 + f2_const;

a = -2 * wcm * ((k - 1) * dg_v1 * df_svf3(k_dim * g_v1) + 1.0) * dc_svf3(cterm) - 1.0;
b = - wcm;
c = wcm * dg_v1;
d = -1;

res = abs(f1) + abs(f2);

norm = 1.0 / ( a * d - b * c );
v1 = v1 - ( d*f1 - b*f2 ) * norm;
v2 = v2 - ( a*f2 - c*f1 ) * norm;

(res > epsilon) && (iter < maxiter_svf);
);

mo0 * v0 + mo1 * v1 + mo2 * v2
);

function run_shriek(x)
instance(last_x)
Expand All @@ -933,17 +1021,20 @@ global()
(
this.elliptic_2x(this._eval_svf2(last_x));
y = this.elliptic_2x(this._eval_svf2(0.5 * (last_x + x)));

/*this.elliptic_4x(this._eval_svf2(last_x)));
this.elliptic_4x(this._eval_svf2(0.75 * last_x + 0.25 * x));
this.elliptic_4x(this._eval_svf2(0.5 * (last_x + x)));
y = this.elliptic_4x(this._eval_svf2(0.25 * last_x + 0.75 * x));*/


last_x = x;
y;
);

function run_mj(x)
instance(last_x)
local(y)
global()
(
this.elliptic_2x(this._eval_svf3(last_x));
y = this.elliptic_2x(this._eval_svf3(0.5 * (last_x + x)));
last_x = x;
y;
);

function init_linear(freq, res, morph)
global(srate)
Expand Down
25 changes: 17 additions & 8 deletions protosynth/saike_protosynth.jsfx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
desc:Saike Protosynth
options:maxmem=32000000
tags: instrument, synth, generator, synthesizer
version: 0.22
version: 0.23
author: Joep Vanlier
changelog: Add a few more presets.
changelog: Add extra filter type. Improve note reassignment (reuse oldest released note first).
license: MIT
provides:
protosynth_dependencies/*
Expand Down Expand Up @@ -83,14 +83,14 @@ slider151:f2_morph=0<0,1,0.000001>-F2 Morph
slider152:f3_cutoff=1<0,1,0.000001>-F3 Filter Cutoff
slider153:f3_reso=0<0,1,0.000001>-F3 Filter Resonance
slider154:f3_morph=0<0,1,0.000001>-F3 Filter Morph
slider155:f3_type=0<0,1,5{LIN,MS,L2,L4,SHRK,PL>-F3 Type
slider155:f3_type=0<0,1,5{LIN,MS,L2,L4,SHRK,PL,SHRK2>-F3 Type
slider156:f3_drive=0<-6,48,0.00001>-F3 Drive
slider157:keyfollow=0<0,1,0.00001>-Key follow

slider158:f4_cutoff=1<0,1,0.000001>-Output Filter Cutoff
slider159:f4_reso=0<0,1,0.000001>-Output Filter Resonance
slider160:f4_morph=0<0,1,0.000001>-Output Filter Morph
slider161:f4_type=0<0,5,1{LIN,MS,L2,L4,SHRK,PL>-Output Filter Type
slider161:f4_type=0<0,5,1{LIN,MS,L2,L4,SHRK,PL,SHRK2>-Output Filter Type
slider162:f4_drive=0<-6,24,0.00001>-Output Drive
slider163:f4_keyfollow=0<0,1,0.00001>-Output Key follow

Expand Down Expand Up @@ -260,11 +260,11 @@ instance()
);
);

filter_type_slider1.init_slider_ui(-1, 0, 5, 0);
filter_type_slider1.init_slider_ui(-1, 0, 6, 0);
filter_gain_slider3.init_slider_ui(156, -24, 24, 0);
filter_keyfollow1.init_slider_ui(157, 0, 1, 0);

filter_type_slider2.init_slider_ui(-1, 0, 5, 0);
filter_type_slider2.init_slider_ui(-1, 0, 6, 0);
filter_gain_slider4.init_slider_ui(162, -24, 24, 0);
filter_keyfollow2.init_slider_ui(163, 0, 1, 0);

Expand Down Expand Up @@ -482,6 +482,7 @@ function filt_type(type)
: (type == 3) ? "L4"
: (type == 4) ? "Shrk"
: (type == 5) ? "PL"
: (type == 6) ? "Sh2"
);

// Modulator handling
Expand Down Expand Up @@ -743,6 +744,8 @@ instance(
this.filter3.shriek.init_shriek(f_cutoff, f_reso, f_morph);
) : (current_f3_type == 5) ? (
this.filter3.pl4.init_pl4(f_cutoff, f_reso, f_morph);
): (current_f3_type == 6) ? (
this.filter3.shriek.init_shriek(f_cutoff, f_reso, f_morph);
);
);
);
Expand Down Expand Up @@ -917,6 +920,8 @@ instance(
this.filter3.shriek.run_shriek(o1 * current_f3_gain) / current_f3_gain;
) : (current_f3_type == 5) ? (
this.filter3.pl4.run_pl4(o1 * current_f3_gain) / current_f3_gain;
) : (current_f3_type == 6) ? (
this.filter3.shriek.run_mj(o1 * current_f3_gain) / current_f3_gain;
)
) : o1;

Expand Down Expand Up @@ -1502,6 +1507,8 @@ voice_update ? (
this.filter4.shriek.init_shriek(f_cutoff, f_reso, f_morph);
) : (current_f4_type == 5) ? (
this.filter4.pl4.init_pl4(f_cutoff, f_reso, f_morph);
) : (current_f4_type == 6) ? (
this.filter4.shriek.init_shriek(f_cutoff, f_reso, f_morph);
);
);
);
Expand Down Expand Up @@ -1538,6 +1545,8 @@ spl0 = filter4_enabled ? (
this.filter4.shriek.run_shriek(spl0 * current_f4_gain) / current_f4_gain;
) : (current_f4_type == 5) ? (
this.filter4.pl4.run_pl4(spl0 * current_f4_gain) / current_f4_gain;
) : (current_f4_type == 6) ? (
this.filter4.shriek.run_mj(spl0 * current_f4_gain) / current_f4_gain;
)
) : spl0;

Expand Down Expand Up @@ -1896,7 +1905,7 @@ cy += 5;
printf(" );");
printf(");");

printf("mod_freq_slider%d.text_slider_ui(%d, cx, cy, 5 * toggle_size + 4, 8, sprintf(9, \"%%\dHz\", mod_freq_slider%d.display_value), \"Frequency\", \"Frequency\"); cx += 5 * toggle_size + 4 + 2;", i + 1, uuid(), i + 1);
printf("mod_freq_slider%d.text_slider_ui(%d, cx, cy, 5 * toggle_size + 4, 8, (mod_freq_slider%d.display_value >= 10) ? sprintf(9, \"%%\dHz\", mod_freq_slider%d.display_value) : sprintf(9, \"%%.1\fHz\", mod_freq_slider%d.display_value), \"Frequency\", \"Frequency\"); cx += 5 * toggle_size + 4 + 2;", i + 1, uuid(), i + 1, i + 1, i + 1);
printf("mod_freq_slider%d.modulator_field(%d, cx, cy, 8, 1, MODULATOR_STRING, \"Freq mod\", 0, N_SELECTABLE_MODULATORS, 1);\n cx += 10;", i + 1, uuid());
printf("mod_freq_slider%d.check_text_input_defaults();", i + 1);

Expand Down Expand Up @@ -2220,7 +2229,7 @@ cy = 208 - 10;
printf("(filter_type_slider%d.over && (mouse_cap == 2) && (last_cap == 0)) ? (\n", j);
printf("gfx_x = mouse_x;");
printf("gfx_y = mouse_y;");
printf("ix = gfx_showmenu(\"Linear|Nonlinear 2-pole MS-20|Nonlinear 2-pole ladder|Nonlinear 4-pole ladder|Nonlinear shriek 2-pole|Nonlinear pillow 4-pole\");");
printf("ix = gfx_showmenu(\"Linear|Nonlinear 2-pole MS-20|Nonlinear 2-pole ladder|Nonlinear 4-pole ladder|Nonlinear shriek 2-pole|Nonlinear pillow 4-pole|Shriek 2\");");
printf(" (ix > 0) ? (");
printf(" filter_type_slider%d.current_value = ix - 1;", j);
printf(" );");
Expand Down

0 comments on commit 32491f3

Please sign in to comment.