diff --git a/protosynth/protosynth_dependencies/saike_proto_synth_spreader.jsfx-inc b/protosynth/protosynth_dependencies/saike_proto_synth_spreader.jsfx-inc new file mode 100644 index 0000000..fa8c198 --- /dev/null +++ b/protosynth/protosynth_dependencies/saike_proto_synth_spreader.jsfx-inc @@ -0,0 +1,292 @@ +@init +function calc_smooth_coeff(cutoff) +instance() +global(srate) +local(g) +( + g = tan($pi * cutoff / srate); + g /(1 + g) +); + +function smooth(target) +global(smooth_coeff) +local(y, v) +instance(s) +( + v = smooth_coeff * (target - s); + y = v + s; + s = y + v; + y +); + +function init_butter2(f0) +global(srate) +local(g) +instance( + +) +( + g = tan($pi * f0 / srate); + + k_1 = 1.84775906552; // 1 / Q + k_2 = 0.76536684415; + + +); + +function eval_butter2(v0) +global() +local(v1, v2, v3) +instance( + +) +( + +); + + +function eval_butter2_2(v0) +global() +local(v1, v2, v3) +instance( + +) +( + +); + +function _init_spreader_buffer(freemem, in_buffer_size, window_in) +instance(buf, p0, p2, buffer_size, half_buffer, window) +global() +local() +( + buffer_size = in_buffer_size; + half_buffer = in_buffer_size / 2; + window = window_in; + p0 = 0; + p2 = floor(rand() * (buffer_size - 1)); + buf = freemem; + + buf + buffer_size +); + +function spreader_update_parameters(spread_slider, mix) +local(detune_fade, detuned_gain, spread) +global() +instance( + + dry_gain, total_gain, spread_gain, +) +( + // Wrangle input parameters + detune_fade = min(10.0 * spread_slider, 1.0); + detuned_gain = (mix >= 100.0) ? 1.0 : 0.01 * mix; + dry_gain = (mix <= 100.0) ? 1.0 : (detune_fade < 1.0) ? max(0.5 * (1.0 - detune_fade), (200.0 - mix) / 100.0) : (200.0 - mix) / 100.0; + spread_gain = detuned_gain * detune_fade; + total_gain = (spread_gain == 0.0) ? 1.0 : (1.41 / (1.0 + 2.4494897427831780981972840747059 * spread_gain)); + spread = 0.5 * spread_slider * spread_slider; + + // Set individual pitch shifter pitches + shifter0.out_delta = pow(0.893, spread); + shifter1.out_delta = pow(0.939, spread); + shifter2.out_delta = pow(0.98, spread); + shifter3.out_delta = pow(1.02, spread); + shifter4.out_delta = pow(1.064, spread); + shifter5.out_delta = pow(1.11, spread); + + shifter6.out_delta = pow(1.0 / 0.893, spread); + shifter7.out_delta = pow(1.0 / 0.939, spread); + shifter8.out_delta = pow(1.0 / 0.98, spread); + shifter9.out_delta = pow(1.0 / 1.02, spread); + shifter10.out_delta = pow(1.0 / 1.064, spread); + shifter11.out_delta = pow(1.0 / 1.11, spread); +); + +function _spreadshifter_tick(data) +instance(buf, p0, p2) +global() +local(p1i, p1f, p2i, x3, x3a, x3b, x4, x4a, x4b, fade34) +instance(out_delta, test, buffer_size, half_buffer, window) +( + p0 -= 1; + (p0 < 0) ? ( p0 += buffer_size; ); + buf[p0] = data; + + p2 -= out_delta; + (p2 < 0) ? ( p2 += buffer_size; ); + + p1i = floor(p2); + p1f = p2 - p1i; + x3a = buf[p1i]; + + p1i += 1; + (p1i >= buffer_size) ? ( p1i -= buffer_size; ); + + x3b = buf[p1i]; + x3 = x3a + p1f * (x3b - x3a); + + p2i = (p1i + half_buffer); + (p2i >= buffer_size) ? ( p2i -= buffer_size; ); + x4a = buf[p2i]; + + p2i += 1; + (p2i >= buffer_size) ? ( p2i -= buffer_size; ); + + x4b = buf[p2i]; + x4 = x4a + p1f * (x4b - x4a); + + p2i = (p1i - p0); + (p2i < 0) ? ( p2i += buffer_size; ); + + fade34 = window[p2i]; + + x4 + fade34 * (x3 - x4) +); + +function init_follower(atk, release) +local() +instance(at, rt, LPF) +global(srate) +( + at = ( atk > 0 ) ? exp(-1.0/(.0005 * atk * srate)) : 0; + rt = exp(-1.0/(.0005 * release * srate)); +); + +function eval_follower(x) +local() +instance(state, at, rt, x) +global() +( + x > state ? ( + state = at * state + ( 1.0 - at ) * x; + ) : ( + state = rt * state + ( 1.0 - rt ) * x; + ); + + state +); + +function spreader_tick(inL, inR, reconstruct_envelope, reset_env) +global() +local(delayed_in, total_out, avg, side) +instance( + window, + buffer_size, half_buffer, + delayL, delayR, delay_ix, read_ix, read_ix2, outL, outR, + + dry_gain, total_gain, spread_gain, + env_follower, env_follower2, env_correction, follower_state, zero_time, smooth_total_gain, smooth_total_gain.smooth, + filt, +) +( + // Delay the main signal + delayL[delay_ix] = inL; + delayR[delay_ix] = inR; + delay_ix += 1; + (delay_ix >= buffer_size) ? delay_ix -= buffer_size; + read_ix = delay_ix - half_buffer; + (read_ix < 0) ? ( read_ix += buffer_size; ); + + // Process the chorus + outL = outR = 0; + + + + (reconstruct_envelope == 1) ? ( + read_ix2 = read_ix - 1; + (read_ix2 < 0) ? ( read_ix2 += buffer_size; ); + delayed_in = max(abs(delayL[read_ix] - delayL[read_ix2]), abs(delayR[read_ix] - delayR[read_ix2])); + total_out = follower_state * max(abs(outL), abs(outR)); + follower_state = env_follower2.eval_follower(env_follower.eval_follower((total_out + delayed_in) > 0.001) > 0.2); + env_correction = follower_state; + ) : (reconstruct_envelope == 2) ? ( + reset_env ? ( + zero_time = 1024; + ); + (zero_time > 0) ? ( + env_correction = window[zero_time]; + zero_time -= 1; + ) : ( + env_correction += 0.01 * (1.0 - env_correction); + ); + ) : ( + env_correction = 1; + ); + + smooth_total_gain = smooth_total_gain.smooth(total_gain); + + outL = filt.eval_butter2(outL); + outR = filt.eval_butter2_2(outR); + + outL = (dry_gain * delayL[read_ix] + outL * spread_gain * env_correction) * smooth_total_gain; + outR = (dry_gain * delayR[read_ix] + outR * spread_gain * env_correction) * smooth_total_gain; +); + +function init_spreader(freemem, _buffer_size) +local(i) +global() +instance( + + delayL, delayR, buffer_size, half_buffer, delay_ix, window, env_follower, env_follower2, +) +( + buffer_size = _buffer_size; + half_buffer = _buffer_size / 2; + freemem = (window = freemem) + buffer_size; + i = 0; + loop(buffer_size, + window[i] = 0.5 - 0.5 * cos(2.0 * $PI * i / buffer_size); + i += 1; + ); + + freemem = (delayL = freemem) + buffer_size; + freemem = (delayR = freemem) + buffer_size; + + delay_ix = 0; + + + + env_follower.init_follower(0, 50); + env_follower2.init_follower(1, 50); + + freemem +); + diff --git a/protosynth/saike_protosynth.jsfx b/protosynth/saike_protosynth.jsfx index c50d78d..53e8d97 100644 --- a/protosynth/saike_protosynth.jsfx +++ b/protosynth/saike_protosynth.jsfx @@ -1,9 +1,9 @@ desc:Saike Protosynth options:maxmem=32000000 tags: instrument, synth, generator, synthesizer -version: 0.43 +version: 0.44 author: Joep Vanlier -changelog: Make sure hints don't leave UI. +changelog: Add superspreader. license: MIT provides: protosynth_dependencies/* @@ -101,6 +101,9 @@ slider163:f4_keyfollow=0<0,1,0.00001>-Output Key follow ); ?> +slider231:spread_slider=0.5<0, 1, 0.0001>-Spread +slider232:spread_mix=100<0, 200, 0.01>-Mix +slider233:spread_frequency=20<20,22050,0.01:log>-Filter frequency slider234:ap_freq=241<20, 22000, 0.0001:log>-AP frequency slider235:ap_feedback=0.0<-0.999, 0.999, 0.001>-AP Feedback @@ -145,6 +148,7 @@ import saike_proto_diffusion.jsfx-inc import saike_proto_stft_fx.jsfx-inc import saike_proto_synth_fx.jsfx-inc import saike_proto_synth_wt.jsfx-inc +import saike_proto_synth_spreader.jsfx-inc @init DROPPED_FILE_STR = 52; @@ -319,6 +323,10 @@ ap_freq_slider1.init_slider_ui(234, 20, 22000, 1, 241); ap_feedback_slider1.init_slider_ui(235, -0.999, 0.999, 0, 0.0); ap_sat_slider1.init_slider_ui(236, 0.01, 0.25, 0, 0.01); +spreader_detune.init_slider_ui(231, 0, 1, 0, 0.5); +spreader_mix.init_slider_ui(232, 0, 200, 0, 100.0); +spreader_freq.init_slider_ui(233, 20, 22050, 1, 20); + -CURRENT_VERSION = 17; +CURRENT_VERSION = 18; version = CURRENT_VERSION; @serialize @@ -1347,12 +1356,32 @@ has_wavetable_data ? ( ); ?> +(version > 17) ? ( + spreader_detune.serialize(version); + spreader_mix.serialize(version); + spreader_freq.serialize(version); + file_var(0, spreader_active); +) : ( + spreader_detune.defaults(); + spreader_mix.defaults(); + spreader_freq.defaults(); + spread_slider = 0.5; + spread_mix = 100; + spread_frequency = 20; + spreader_active = 0; +); + update_solo(); version = CURRENT_VERSION; @slider @block +pdc_top_ch = 2; +pdc_bot_ch = 0; +pdc_delay = spreader_active ? spreader.half_buffer : 0; +smooth_coeff = calc_smooth_coeff(1000.0 / 25.0); + must_glide = force_glide && glide; (reverb_algorithm != last_reverb_algo) ? ( @@ -1366,18 +1395,6 @@ shift_mode = 0; shift_freq = 0; fft_verb.stft_reverb_settings(1, time_ms, 0, side_cut_freq, damping_freq, high_cut_freq, low_cut_freq, shimmer, drops, shift_mode, shift_freq); -//1/time_ms - -/* - slider248:time_ms=900<5,2800,1>-Verb Time [ms] - slider249:verb_mix=0.4<0, 1, 0.01>-Verb Mix - slider250:side_cut_freq=120<20,22050,0.001:log>-Side low cut frequency - slider251:low_cut_freq=20<20,22050,0.001:log>-Low cut frequency - slider252:high_cut_freq=22050<20,22050,0.001:log>-High cut frequency - slider253:damping_freq=22050<20,22050,0.001:log>-Damping frequency -*/ - - update_solo(); any_solo ? ( @@ -1486,10 +1503,26 @@ midi.notes_remain ? ( ); ); +// Spreader +spreader_active ? ( + dspread_slider = spread_slider - current_spread_slider; + dspread_mix = spread_mix - current_spread_mix; + dspread_slider = spread_frequency - current_spread_frequency; + + spreader.filt.init_butter2(current_spread_frequency); + spreader.spreader_update_parameters(current_spread_slider, current_spread_mix); +); + pass_through = 1; midi.midi_block(pass_through); @sample +spreader_active ? ( + (d_frequency != 0) ? ( + spreader.filt.init_butter2(current_spread_frequency); + ); +); + function cheapo_block(x) local(y) global() @@ -1705,6 +1738,13 @@ audio_running ? ( spl0 = brightness_boost.eval_linearSVF_shelf(spl0); spl1 = brightness_boost.eval_linearSVF_shelf2(spl1); + spreader_active ? ( + spreader.spreader_tick(spl0, spl1, reconstruct_envelope, (last_active_notes == 0) && midi.active_notes); + last_active_notes = midi.active_notes; + spl0 = spreader.outL; + spl1 = spreader.outR; + ); + poisson_enabled ? ( poisson.poisson_noise(spl0, spl1, poisson_freq, 0); spl0 = poisson.out_left; @@ -1741,6 +1781,16 @@ audio_running ? ( m_out_last = max(abs(spl0), abs(spl1)); +spreader_active ? ( + ((dspread_slider != 0) || (dspread_mix != 0)) ? ( + spreader.spreader_update_parameters(current_spread_slider, current_spread_mix); + ); + + current_spread_slider = current_spread.smooth(spread_slider); + current_spread_mix = current_spread_mix.smooth(spread_mix); + current_spread_frequency = current_spread_frequency.smooth(spread_frequency); +); + @gfx 1050 768 (!active_text_input) ? ( char = gfx_getchar(); @@ -2498,6 +2548,30 @@ pitch_bend_slider.text_slider_ui(, cx, cy, 5 * toggle_si pitch_bend_slider.check_text_input_defaults(); cy += 10; +spreader_toggle.simple_serialize_toggle(fx_origin, cy + 1, toggle_size, spreader_active, "Enable Spreader\n\nEnable cheap spreader.", "Spread") ? ( + spreader_active = 1 - spreader_active; +); + +txt_blit("SPREADER", fx_origin + 8, cy + 2); + +cy += 8; +spreader_freq.active = spreader_active; +spreader_freq.rounding_increment = 1337; +spreader_freq.slider_ui(, fx_origin, cy, fx_width + 1, 8, sprintf(1, "%d", spread_frequency + 0.001), "Spreader Frequency", "SP Freq"); +spreader_freq.check_text_input_defaults(); + +cy += 10; +spreader_mix.active = spreader_active; +spreader_mix.rounding_increment = 0.01; +spreader_mix.slider_ui(, fx_origin, cy, hw, 8, sprintf(1, "%d", spread_mix), "Spreader Mix", "Sp Mix"); +spreader_mix.check_text_input_defaults(); + +spreader_detune.active = spreader_active; +spreader_detune.rounding_increment = 0.01; +spreader_detune.slider_ui(, fx_origin + hw + 1, cy, hw, 8, sprintf(1, "%d%%", 100 * spread_slider), "Spreader Detune", "Sp Detune"); +spreader_detune.check_text_input() ? ( slider(spreader_detune.sl) = max(spreader_detune.sl_min, min(spreader_detune.sl_max, text_box.result / 100)); ); + +cy += 10; allpass_toggle.simple_serialize_toggle(fx_origin, cy + 1, toggle_size, allpass, "Enable Smear\n\nEnable allpass stack that\nsmears the audio.", "Smear") ? ( allpass = 1 - allpass; );