Browse Source

011 - Spectrum matching

master
Michaël Lemaire 5 months ago
parent
commit
dd06df3cd3
  1. 1
      .gitignore
  2. 551
      plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/atlantis-reverb.jsfx
  3. 53
      plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/atlantis-reverb.jsfx.rpl
  4. 133
      plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/frequency-graph.jsfx-inc
  5. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/ui-lib.jsfx-inc
  6. 302
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/Cross-Polyphonic-FM.jsfx
  7. 279
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/delay-utils.jsfx-inc
  8. 51
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/smoother.jsfx-inc
  9. 87
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/synth-framework-obj-ui.jsfx-inc
  10. 477
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/synth-framework-obj.jsfx-inc
  11. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/themes/backgrounds/sebastian-pociecha-451575-unsplash.png
  12. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/themes/bitmap-simple/theme-dark-orange.png
  13. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/ui-lib.jsfx-inc
  14. 357
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/Echo-Cycles.jsfx
  15. 45
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/Echo-Cycles.jsfx.rpl
  16. 279
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/delay-utils.jsfx-inc
  17. 289
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/filter-utils.jsfx-inc
  18. 51
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/smoother.jsfx-inc
  19. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/themes/bitmap-simple/theme-dark-cyan.png
  20. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Echo-Cycles/ui-lib.jsfx-inc
  21. 792
      plugins/jsfx/Geraint's JSFX/Delay/Ripple/Ripple.jsfx
  22. 42
      plugins/jsfx/Geraint's JSFX/Delay/Ripple/Ripple.jsfx.rpl
  23. 289
      plugins/jsfx/Geraint's JSFX/Delay/Ripple/filter-utils.jsfx-inc
  24. 133
      plugins/jsfx/Geraint's JSFX/Delay/Ripple/frequency-graph.jsfx-inc
  25. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Ripple/ui-lib.jsfx-inc
  26. 369
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/Spring-Box.jsfx
  27. 92
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/Spring-Box.jsfx.rpl
  28. 279
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/delay-utils.jsfx-inc
  29. 51
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/smoother.jsfx-inc
  30. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/themes/bitmap-simple/theme-cyan.png
  31. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Spring-Box/ui-lib.jsfx-inc
  32. 673
      plugins/jsfx/Geraint's JSFX/Delay/Stereo-Alignment-Delay/Stereo Alignment Delay.jsfx
  33. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Stereo-Alignment-Delay/ui-lib.jsfx-inc
  34. 192
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/Vibrato.jsfx
  35. 14
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/Vibrato.jsfx.rpl
  36. 279
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/delay-utils.jsfx-inc
  37. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/themes/backgrounds/tom-barrett-512968-unsplash-small-denoised.png
  38. BIN
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/themes/bitmap-simple/theme-dark-blue.png
  39. 2970
      plugins/jsfx/Geraint's JSFX/Delay/Vibrato/ui-lib.jsfx-inc
  40. 227
      plugins/jsfx/Geraint's JSFX/Distortion/Bad-Connection/Bad Connection.jsfx
  41. 37
      plugins/jsfx/Geraint's JSFX/Distortion/Bad-Connection/Bad Connection.jsfx.rpl
  42. 279
      plugins/jsfx/Geraint's JSFX/Distortion/Bad-Connection/delay-utils.jsfx-inc
  43. 2970
      plugins/jsfx/Geraint's JSFX/Distortion/Bad-Connection/ui-lib.jsfx-inc
  44. 294
      plugins/jsfx/Geraint's JSFX/Distortion/Sandwich-Amp/Sandwich Amp.jsfx
  45. 17
      plugins/jsfx/Geraint's JSFX/Distortion/Sandwich-Amp/Sandwich Amp.jsfx.rpl
  46. 51
      plugins/jsfx/Geraint's JSFX/Distortion/Sandwich-Amp/smoother.jsfx-inc
  47. 2970
      plugins/jsfx/Geraint's JSFX/Distortion/Sandwich-Amp/ui-lib.jsfx-inc
  48. 560
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/Droplets.jsfx
  49. 40
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/Droplets.jsfx.rpl
  50. 211
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/piano-ui.jsfx-inc
  51. BIN
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/themes/backgrounds/isiah-gibson-xqFCy9AbHP4-unsplash.png
  52. BIN
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/themes/bitmap-simple/theme-dark-cyan.png
  53. 2970
      plugins/jsfx/Geraint's JSFX/MIDI/Droplets/ui-lib.jsfx-inc
  54. 139
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Gate/MIDI Gate.jsfx
  55. 255
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Gate/synth-framework.jsfx-inc
  56. 2970
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Gate/ui-lib.jsfx-inc
  57. 494
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Harmony/MIDI Harmony.jsfx
  58. 80
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Harmony/MIDI Harmony.jsfx.rpl
  59. 211
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Harmony/piano-ui.jsfx-inc
  60. 255
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Harmony/synth-framework.jsfx-inc
  61. 2970
      plugins/jsfx/Geraint's JSFX/MIDI/MIDI-Harmony/ui-lib.jsfx-inc
  62. 76
      plugins/jsfx/Geraint's JSFX/Misc/MIDI-Controller-Control/MIDI-Controller-Control.jsfx
  63. 2970
      plugins/jsfx/Geraint's JSFX/Misc/MIDI-Controller-Control/ui-lib.jsfx-inc
  64. 862
      plugins/jsfx/Geraint's JSFX/Pitch Correction/Warble/Warble.jsfx
  65. 279
      plugins/jsfx/Geraint's JSFX/Pitch Correction/Warble/delay-utils.jsfx-inc
  66. 282
      plugins/jsfx/Geraint's JSFX/Pitch Correction/Warble/pitch-detection.jsfx-inc
  67. 2970
      plugins/jsfx/Geraint's JSFX/Pitch Correction/Warble/ui-lib.jsfx-inc
  68. 452
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/Hammer And Chord.jsfx
  69. 98
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/Hammer And Chord.jsfx.rpl
  70. 279
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/delay-utils.jsfx-inc
  71. 289
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/filter-utils.jsfx-inc
  72. 87
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/synth-framework-obj-ui.jsfx-inc
  73. 477
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/synth-framework-obj.jsfx-inc
  74. 2970
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-Chord/ui-lib.jsfx-inc
  75. 224
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-String/Hammer And String.jsfx
  76. 279
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-String/delay-utils.jsfx-inc
  77. 255
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-String/synth-framework.jsfx-inc
  78. 2970
      plugins/jsfx/Geraint's JSFX/Synth/Hammer-And-String/ui-lib.jsfx-inc
  79. 1060
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/Humonica.jsfx
  80. 242
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/Humonica.jsfx.rpl
  81. 289
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/filter-utils.jsfx-inc
  82. 304
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/graph-utils.jsfx-inc
  83. 51
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/smoother.jsfx-inc
  84. 87
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/synth-framework-obj-ui.jsfx-inc
  85. 477
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/synth-framework-obj.jsfx-inc
  86. 2970
      plugins/jsfx/Geraint's JSFX/Synth/Humonica/ui-lib.jsfx-inc
  87. 194
      plugins/jsfx/Geraint's JSFX/Synth/Learning-Sampler/Learning Sampler.jsfx
  88. 255
      plugins/jsfx/Geraint's JSFX/Synth/Learning-Sampler/synth-framework.jsfx-inc
  89. 2970
      plugins/jsfx/Geraint's JSFX/Synth/Learning-Sampler/ui-lib.jsfx-inc
  90. 3379
      plugins/jsfx/Geraint's JSFX/Synth/PadSynth/pad-synth.jsfx
  91. 808
      plugins/jsfx/Geraint's JSFX/Synth/PadSynth/pad-synth.jsfx.rpl
  92. 72
      plugins/jsfx/Geraint's JSFX/Synth/PadSynth/ui-lib-compat.jsfx-inc
  93. 2970
      plugins/jsfx/Geraint's JSFX/Synth/PadSynth/ui-lib.jsfx-inc
  94. 400
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/Soft-Bell.jsfx
  95. 62
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/Soft-Bell.jsfx.rpl
  96. 279
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/delay-utils.jsfx-inc
  97. 289
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/filter-utils.jsfx-inc
  98. 87
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/synth-framework-obj-ui.jsfx-inc
  99. 477
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/synth-framework-obj.jsfx-inc
  100. BIN
      plugins/jsfx/Geraint's JSFX/Synth/Soft-Bell/themes/backgrounds/fancycrave-194878-unsplash.png

1
.gitignore

@ -4,4 +4,5 @@
*.reapeaks
/soundfonts/
/renders/
/tracks/references/
recover.mmp

551
plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/atlantis-reverb.jsfx

@ -0,0 +1,551 @@
desc:Atlantis Reverb (by Geraint Luff)
in_pin:left
in_pin:right
out_pin:left
out_pin:right
slider1:wet_db=-12<-60,12>-Wet (dB)
slider2:dry_db=0<-60,12>-Dry (dB)
slider3:window_ms=200<10,500>-Grain size (ms)
slider4:decay_seconds=0.3<0.01,5>-Decay Period (s)
slider5:high_damping_hz=6000<100,18000>-High Damping (Hz)
slider6:low_damping_hz=120<20,500>-Low Damping (Hz)
slider7:damping_strength=3<1,100>-Damping (strength)
slider8:shimmer_factor=0<0,1>-Shimmer
slider9:shimmer_tone=0<0,0.3>-Shimmer tone
slider10:compressor_threshold_db=0<-60,0>-Compressor threshold
slider11:compressor_ratio=2<1,10>-Compressor ratio
slider12:release_decay_seconds=0.2<0.01,5>-Release Decay Period (s)
slider13:release_mode=0<0,1,1{sustain,release}>-Release mode
slider14:release_cc64=0<0,1,1{off,on}>-Release MIDI sustain (CC-64)
slider15:detune_cents_per_second=0<0,1200>-Detune rate (cents/second)
slider16:detune_shift_bias=0<-1,1>-Detune shift (up/down)
import ui-lib.jsfx-inc
import frequency-graph.jsfx-inc
@init
interval_index = 0;
!srate ? srate = 44100;
window_overlap_factor = 4; // Must be a multiple of 2 so that things add up nicely
MAX_FFT_SIZE = 32768;
buffer_length = MAX_FFT_SIZE*2; // Store as complex stereo buffer
freemem = 0;
freemem = (fft_buffer = freemem) + MAX_FFT_SIZE*2;
freemem = (energy_buffer = freemem) + MAX_FFT_SIZE;
freemem = (release_buffer = freemem) + MAX_FFT_SIZE;
freemem = (input_buffer = freemem) + buffer_length;
freemem = (output_buffer = freemem) + buffer_length;
freemem = (window_buffer = freemem) + MAX_FFT_SIZE;
freemem = (display_spectrum_buffer = freemem) + MAX_FFT_SIZE;
memset(energy_buffer, 0, MAX_FFT_SIZE);
memset(release_buffer, 0, MAX_FFT_SIZE);
memset(input_buffer, 0, buffer_length);
memset(output_buffer, 0, buffer_length);
freemem = ui_setup(freemem);
function window(r) local(s, c) (
s = sin(r*$pi);
c = cos(r*$pi);
s*s/sqrt(s*s*s*s + c*c*c*c);
);
midi_cc64 = 0;
release_mode = 0;
@block
window_samples = min(32760, ceil(srate*0.001*window_ms));
fft_size = pow(2, ceil(log(window_samples + 1)/log(2)));
interval_samples = ceil(window_samples/window_overlap_factor);
// Decay
decay_intervals = srate*decay_seconds/interval_samples;
decay_interval_factor = exp(-1/decay_intervals);
// Release mode
release_decay_intervals = srate*min(release_decay_seconds, decay_seconds)/interval_samples;
release_decay_interval_factor = exp(-1/release_decay_intervals);
// Damping
damped_extra_interval_factor = pow(decay_interval_factor, damping_strength - 1);
detune_cents_per_interval = detune_cents_per_second/srate*interval_samples;
// We're going to sweep twice, once up and once down
detune_factor_up = (pow(2, detune_cents_per_interval/1200) - 1)*0.5*(1 + detune_shift_bias);
detune_factor_down = (pow(2, detune_cents_per_interval/1200) - 1)*0.5*(1 - detune_shift_bias);
wet_factor = pow(10, wet_db/20);
dry_factor = pow(10, dry_db/20);
prev_fft_size != fft_size || prev_window_samples != window_samples ? (
memset(energy_buffer, 0, MAX_FFT_SIZE);
prev_fft_size = fft_size;
prev_window_samples = window_samples;
i = 0;
loop(window_samples,
window_buffer[i] = window((i + 0.5)/window_samples);
i += 1
);
);
octave_up_rate_persecond = pow(8, shimmer_factor) - 1;
octave_up_rate_perinterval = min(0.75, octave_up_rate_persecond/srate*interval_samples);
shimmer_double = octave_up_rate_perinterval*(1 - shimmer_tone/1.58);
shimmer_triple = octave_up_rate_perinterval/1.58*shimmer_tone;
shimmer_remainder = (1 - shimmer_double - shimmer_triple);
low_damping_hz = max(20, min(low_damping_hz, high_damping_hz));
high_damping_hz = min(18000, max(low_damping_hz, high_damping_hz));
high_damping_index = high_damping_hz/srate*fft_size;
high_damping_index_low = high_damping_index*0.75;
high_damping_index_high = high_damping_index*1.5;
low_damping_index = low_damping_hz/srate*fft_size;
low_damping_index_low = low_damping_index*0.5;
low_damping_index_high = low_damping_index*1.5;
compressor_threshold_power = pow(10, compressor_threshold_db/10);
compressor_ratio_inv = 1/compressor_ratio;
while (midirecv(offset, midi_msg1, midi_msg2, midi_msg3)) (
midisend(offset, midi_msg1, midi_msg2, midi_msg3);
midi_type = midi_msg1>>4;
midi_channel = midi_msg1&0x0f;
midi_type == 11 ? (
midi_msg2 == 121 ? ( // Reset controllers
midi_cc64 = 0;
) : midi_msg2 == 64 ? (
midi_cc64 = midi_msg3;
// If it's below 64, make sure we release
release_cc64 && midi_cc64 < 64 && release_decay_seconds < decay_seconds ? (
action_release = 1;
);
);
);
);
release_cc64 && midi_cc64 < 64 && release_decay_seconds < decay_seconds ? (
action_release = 1;
);
@slider
// In case the slider flips too fast, between reverb blocks
release_mode ? (
action_release = 1;
);
@sample
function reverb_block() local(i, i2, r, r2, w, real, imag, real2, imag2, mag2, mag, mult_factor, mag2sum) (
action_release || release_mode ? (
action_release = 0;
// Transfer all energy from main buffer to release buffer
release_decay_seconds < decay_seconds ? (
i = 0;
loop(fft_size/2,
release_buffer[i] += energy_buffer[i];
energy_buffer[i] = 0;
i += 1;
);
);
);
// Detune by smudging the frequency bins
detune_cents_per_second ? (
i = 1;
current_energy = energy_buffer[i];
current_release = release_buffer[i];
loop(fft_size/2 - 1,
r = 1/(detune_factor_up*i + 1);
// at this point: current_energy = energy_buffer[i]
// so this means: energy_buffer[i] *= r
energy_buffer[i] = current_energy*r;
// spread the remaining energy into the next bin, and update current_energy
// We don't actually need to assign energy_buffer[i + 1] = current_energy, because it will be done in the next step anyway
current_energy = energy_buffer[i + 1] + current_energy*(1 - r);
release_buffer[i] = current_release*r;
current_release = release_buffer[i + 1] + current_release*(1 - r);
i += 1;
);
current_energy = energy_buffer[i];
current_release = release_buffer[i];
loop(fft_size/2 - 1,
r = 1/(detune_factor_down*i + 1);
energy_buffer[i] = current_energy*r;
current_energy = energy_buffer[i - 1] + current_energy*(1 - r);
release_buffer[i] = current_release*r;
current_release = release_buffer[i - 1] + current_release*(1 - r);
i -= 1;
);
energy_buffer[i] = current_energy;
release_buffer[i] = current_release;
energy_buffer[0] = energy_buffer[fft_size/2] = 0;
release_buffer[0] = release_buffer[fft_size/2] = 0;
);
i = 0;
mag2sum = 0;
loop(window_samples,
w = window_buffer[i>>1];
i2 = buffer_pos - window_samples*2 + i;
i2 < 0 ? i2 += buffer_length;
real = input_buffer[i2]*w;
imag = input_buffer[i2 + 1]*w;
fft_buffer[i] = real;
fft_buffer[i + 1] = real;
mag2sum += real*real + imag*imag;
i += 2;
);
loop(fft_size - window_samples,
fft_buffer[i] = 0;
fft_buffer[i + 1] = 0;
i += 2;
);
fft(fft_buffer, fft_size);
fft_permute(fft_buffer, fft_size);
i = 1;
mult_factor = 0.5/window_overlap_factor*pow(window_samples/(fft_size*fft_size), 2);
mult_factor *= window_samples/(0.1*srate);
mag2 = mag2sum*sqrt(2)/window_samples;
display_power = mag2;
mag2 > compressor_threshold_power ? (
mult_factor *= pow(mag2/compressor_threshold_power, compressor_ratio_inv - 1);
display_power_compressed = display_power*pow(mag2/compressor_threshold_power, compressor_ratio_inv - 1);
) : (
display_power_compressed = display_power;
);
fft_buffer[0] = fft_buffer[1] = fft_buffer[fft_size] = fft_buffer[fft_size + 1] = 0;
loop(fft_size/2 - 1,
i2 = fft_size - i;
real = fft_buffer[2*i];
imag = fft_buffer[2*i + 1];
real2 = fft_buffer[2*i2];
imag2 = fft_buffer[2*i2 + 1];
mag2 = (real*real + imag*imag + real2*real2 + imag2*imag2)*mult_factor;
display_spectrum_buffer[i] = mag2;
decay = decay_interval_factor;
extra_decay = i <= high_damping_index_low ? (
1;
) : i >= high_damping_index_high ? (
damped_extra_interval_factor;
) : (
r = (i - high_damping_index_low)/(high_damping_index_high - high_damping_index_low);
1 + (damped_extra_interval_factor - 1)*r*r;
);
i < low_damping_index_high ? (
i > low_damping_index_low ? (
r = (i - low_damping_index_high)/(low_damping_index_low - low_damping_index_high);
extra_decay *= 1 + (damped_extra_interval_factor - 1)*r*r;
) : (
extra_decay *= damped_extra_interval_factor;
);
);
energy_buffer[i] = (energy_buffer[i]*decay + mag2)*extra_decay;
release_buffer[i] *= release_decay_interval_factor*extra_decay;
i += 1;
);
octave_up_rate_perinterval ? (
i = fft_size/2 - 1;
loop(fft_size/2 - 2,
current = energy_buffer[i];
release_current = release_buffer[i];
shimmer_tone ? (
2*i + 1 < fft_size/2 ? (
energy_buffer[2*i - 1] += 0.25*shimmer_double*current;
energy_buffer[2*i] += 0.5*shimmer_double*current;
energy_buffer[2*i + 1] += 0.25*shimmer_double*current;
release_buffer[2*i - 1] += 0.25*shimmer_double*release_current;
release_buffer[2*i] += 0.5*shimmer_double*release_current;
release_buffer[2*i + 1] += 0.25*shimmer_double*release_current;
);
3*i + 1 < fft_size/2 ? (
energy_buffer[3*i - 2] += 0.11*shimmer_triple*current;
energy_buffer[3*i - 1] += 0.22*shimmer_triple*current;
energy_buffer[3*i] += 0.34*shimmer_triple*current;
energy_buffer[3*i + 1] += 0.22*shimmer_triple*current;
energy_buffer[3*i + 2] += 0.21*shimmer_triple*current;
release_buffer[3*i - 2] += 0.11*shimmer_triple*release_current;
release_buffer[3*i - 1] += 0.22*shimmer_triple*release_current;
release_buffer[3*i] += 0.34*shimmer_triple*release_current;
release_buffer[3*i + 1] += 0.22*shimmer_triple*release_current;
release_buffer[3*i + 2] += 0.21*shimmer_triple*release_current;
);
) : (
2*i + 1 < fft_size/2 ? (
energy_buffer[2*i - 1] += 0.25*shimmer_double*current;
energy_buffer[2*i] += 0.5*shimmer_double*current;
energy_buffer[2*i + 1] += 0.25*shimmer_double*current;
release_buffer[2*i - 1] += 0.25*shimmer_double*release_current;
release_buffer[2*i] += 0.5*shimmer_double*release_current;
release_buffer[2*i + 1] += 0.25*shimmer_double*release_current;
);
);
energy_buffer[i] = shimmer_remainder*current;
release_buffer[i] = shimmer_remainder*release_current;
i -= 1;
);
);
// Assign energy spectrum to FFT buffer
i = 1;
loop(fft_size/2 - 1,
i2 = fft_size - i;
mag = sqrt(mag2 = energy_buffer[i] + release_buffer[i]);
phase1 = rand()*2*$pi;
phase2 = rand()*2*$pi;
fft_buffer[2*i] = cos(phase1)*mag;
fft_buffer[2*i + 1] = sin(phase1)*mag;
fft_buffer[2*i2] = cos(phase2)*mag;
fft_buffer[2*i2 + 1] = sin(phase2)*mag;
i += 1;
);
fft_ipermute(fft_buffer, fft_size);
ifft(fft_buffer, fft_size);
i = 0;
loop(window_samples,
w = window_buffer[i>>1];
i2 = buffer_pos + i;
i2 >= buffer_length ? i2 -= buffer_length;
output_buffer[i2] += fft_buffer[i]*w;
output_buffer[i2 + 1] += fft_buffer[i + 1]*w;
i += 2;
);
);
input_buffer[buffer_pos] = spl0;
input_buffer[buffer_pos + 1] = spl1;
interval_index += 1;
interval_index >= interval_samples ? (
interval_index = 0;
reverb_block();
);
spl0 = spl0*dry_factor + output_buffer[buffer_pos]*wet_factor;
spl1 = spl1*dry_factor + output_buffer[buffer_pos + 1]*wet_factor;
output_buffer[buffer_pos] = 0;
output_buffer[buffer_pos + 1] = 0;
buffer_pos += 2;
buffer_pos >= buffer_length ? buffer_pos = 0;
@gfx 760 300
control_start("main", "tron");
function labels(label, value, number_format) local(h) (
h = max((ui_height() - 60)/2, ui_height()*0.2);
ui_split_top(h);
ui_align(0.5, 1);
ui_text(label);
ui_pop();
ui_split_bottom(h);
ui_align(0.5, 0);
number_format >= 0 ? (
ui_textnumber(value*1.0000001, number_format);
);
ui_pop();
h;
);
function draw_compressor_display() (
ui_push();
control_background_technical();
g_db = log(display_power)/log(10)*10;
g_compressed_db = log(display_power_compressed)/log(10)*10;
g_r1 = max(0, min(1, g_db/-60));
g_r2 = max(0, min(1, g_compressed_db/-60));
g_y1 = floor(ui_top_retina() + ui_height_retina()*g_r1);
g_y2 = floor(ui_top_retina() + floor(ui_height_retina()*g_r2));
ui_color(192, 64, 64);
gfx_rect(ui_left_retina(), g_y1, ui_width_retina(), g_y2 - g_y1);
ui_color(64, 192, 64);
gfx_rect(ui_left_retina(), g_y2, ui_width_retina(), ui_bottom_retina() - g_y2);
ui_fontsize(10);
control_finish_technical();
ui_color(255, 255, 255);
ui_textnumber(g_compressed_db - g_db, "%.1f");
ui_align(0.5, 0.65);
ui_text("dB");
ui_pop();
);
srate && ui_screen() == "main" ? (
control_navbar("Atlantis Reverb");
ui_split_topratio(1/2);
ui_split_leftratio(2/10);
control_group("mix");
ui_split_leftratio(1/2);
labels("dry", dry_db, "%.1f db");
ui_automate(dry_db, control_dial(dry_db, -60, 12, -3.3, 0));
ui_split_next();
labels("wet", wet_db, "%.1f db");
ui_automate(wet_db, control_dial(wet_db, -60, 12, -3.3, -12));
ui_pop();
ui_pop();
ui_split_leftratio(3/8);
control_group("decay");
ui_split_leftratio(1/3);
labels("period", decay_seconds, "%.2f s");
ui_automate(decay_seconds, control_dial(decay_seconds, max(0.01, window_ms*0.001*0.3), 5, 5, 0.3));
ui_split_next();
labels("release", release_decay_seconds, release_decay_seconds >= decay_seconds ? "off" : "%.2f s");
ui_automate(release_decay_seconds, control_dial(release_decay_seconds, max(0.01, window_ms*0.001*0.3), 5.001, 5, 0.2));
ui_split_next();
ui_fontsize(10);
ui_split_topratio(0.45);
ui_pad(5, 5);
control_button("release");
ui_press() ? (
ui_automate(release_mode, 1);
action_release = 1;
);
ui_mouse_up() ? (
ui_automate(release_mode, 0);
midi_cc64 = 0;
);
(release_mode || (release_cc64 && midi_cc64 < 64)) && (release_decay_seconds < decay_seconds) ? (
ui_color(64, 255, 64, 0.3);
ui_fill();
);
ui_pop();
ui_split_topratio(1/3);
ui_text("MIDI sustain");
ui_pop();
release_cc64 = control_switch(release_cc64);
ui_pop();
ui_pop();
ui_split_leftratio(3/5);
control_group("damping");
ui_split_leftratio(1/3);
labels("damping", damping_strength, "x %.1f");
ui_automate(damping_strength, control_dial(damping_strength, 1, 100, 6, 3));
ui_split_next();
labels("low", low_damping_hz, "%i Hz");
ui_automate(low_damping_hz, control_dial(low_damping_hz, 20, 500, 3, 120));
ui_split_next();
labels("high", high_damping_hz, "%i Hz");
ui_automate(high_damping_hz, control_dial(high_damping_hz, 100, 18000, 3, 6000));
ui_pop();
ui_pop();
ui_split_leftratio(2/2);
control_group("shimmer");
ui_split_leftratio(1/2);
labels("rate", octave_up_rate_persecond, octave_up_rate_persecond > 0.3 ? "%.1f oct/s": "%.2f oct/s");
ui_automate(shimmer_factor, control_dial(shimmer_factor, 0, 1, 2, 0));
ui_split_next();
labels("fifths", shimmer_tone, -1);
ui_automate(shimmer_tone, control_dial(shimmer_tone, 0, 0.25, 2, 0));
ui_pop();
ui_pop();
ui_split_next();
ui_split_leftratio(2/10);
control_group("input comp");
ui_split_leftratio(1/2);
labels("threshold", compressor_threshold_db, "%idB");
ui_automate(compressor_threshold_db, control_dial(compressor_threshold_db, -60, 0, -2, 0));
ui_split_next();
labels("ratio", compressor_ratio, "x%.1f");
ui_automate(compressor_ratio, control_dial(compressor_ratio, 1, 10, 3, 2));
ui_pop();
ui_pop();
ui_split_rightratio(2/8);
control_group("detune");
ui_split_leftratio(1/2);
labels("spread", detune_cents_per_second, "%i cent/s");
ui_automate(detune_cents_per_second, control_dial(detune_cents_per_second, 0, 1200, 8, 0));
ui_split_next();
labels("shift", floor(detune_shift_bias*100 + 0.5), detune_shift_bias ? "%i%%" : "off");
ui_automate(detune_shift_bias, control_dial(detune_shift_bias, -1, 1, 0, 0));
ui_pop();
ui_pop();
ui_split_left(30);
ui_pad(0, 0, 3, 0);
draw_compressor_display();
ui_pop();
ui_split_right(70);
ui_pad(10, 20);
labels("grain", window_ms, "%i ms");
window_ms = control_dial(window_ms, 10, 500, 1.5, 150);
ui_pop();
ui_split_topratio(1/1);
graph.frequency_graph_init(50, 20000, -120, 0);
control_background_technical();
graph.frequency_graph_grid();
graph.frequency_graph_graph(energy_buffer, fft_size/2, srate/2, 0, 2);
ui_color(255, 255, 255, 0.4);
graph.frequency_graph_graph(release_buffer, fft_size/2, srate/2, 0, 2);
ui_color(255, 255, 0, 0.5);
graph.frequency_graph_graph(display_spectrum_buffer, fft_size/2, srate/2, 0, 2);
control_finish_technical();
ui_pop();
ui_pop();
) : control_system();
@serialize
preset_version = 4;
file_var(0, preset_version);
preset_version < 2 ? (
shimmer_tone = 0;
compressor_threshold_db = 0;
compressor_ratio = 2;
);
preset_version < 3 ? (
release_mode = 0;
release_cc64 = 0;
release_decay_seconds = 0.2;
);
preset_version < 4 ? (
detune_cents_per_second = 0;
detune_shift_bias = 0;
);

53
plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/atlantis-reverb.jsfx.rpl

@ -0,0 +1,53 @@
<REAPER_PRESET_LIBRARY `JS: Atlantis Reverb (by Geraint Luff)`
<PRESET `Long and simple`
LTEyLjAwMDAwMCAwLjAwMDAwMCA1MDAuMDAwMDAwIDEuOTg0Njk2IDYwMDAuMDAwMDAwIDgwLjk5OTM4NiAzLjAwMDAwMCAwLjAwMDAwMCAwLjAwMDAwMCAwLjAwMDAw
MCAyLjAwMDAwMCAwLjUwMjM2NSAwLjAwMDAwMCAwLjAwMDAwMCAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0g
LSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gIkxvbmcgYW5kIHNpbXBsZSIAAABAQA==
>
<PRESET `Short and simple`
LTkuMDgxMzMxIDAuMDAwMDAwIDE1MC4wMDAwMDAgMC4wNzcyNjAgNjAwMC4wMDAwMDAgMTIwLjAwMDAwMCAzLjAwMDAwMCAwLjAwMDAwMCAwLjAwMDAwMCAwLjAwMDAw
MCAyLjAwMDAwMCAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAt
IC0gLSAtIC0gLSAtIC0gLSAtIC0gIlNob3J0IGFuZCBzaW1wbGUiAAAAAEA=
>
<PRESET `Mellow Piano`
LTEyLjAwMDAwMCAwLjAwMDAwMCAyODYuMzQ0MjU4IDAuOTI1NDQzIDQyMzMuNDIzNjY0IDgwLjk5OTM4NiAzLjAwMDAwMCAwLjAyMjMzNCAwLjAxODAxNiAtNDcuMzU1
MTc2IDEuMjI5ODI3IDAuNTAxNjE1IDAuMDAwMDAwIDAuMDAwMDAwIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0g
LSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAiTWVsbG93IFBpYW5vIgAAAEBA
>
<PRESET `Epic Swell`
LTEyLjAwMDAwMCAwLjAwMDAwMCAxNTAuMDAwMDAwIDEuNzI4NjM1IDQxMTUuMzM3MjgwIDEyMC4wMDAwMDAgMTAwLjAwMDAwMCAwLjI4NDM2MyAwLjAwMDAwMCAwLjAw
MDAwMCAyLjAwMDAwMCAwLjMwMDA4NyAwLjAwMDAwMCAwLjAwMDAwMCAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAt
IC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gIkVwaWMgU3dlbGwiAAAAQEA=
>
<PRESET `Glitter Reverb`
LTEyLjAwMDAwMCAwLjAwMDAwMCAxNTAuMDAwMDAwIDAuMjQ2ODc1IDQxMTUuMzM3MjgwIDEyMC4wMDAwMDAgNC4zNzk3MjYgMC4yMTU4NDcgLSAtIC0gLSAtIC0gLSAt
IC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAt
ICJHbGl0dGVyIFJldmVyYiIAAACAPw==
>
<PRESET `Super-sustain (MIDI CC-64)`
LTcuMTk1NDAwIDAuMDAwMDAwIDMwMC42MjAyNDUgMS4wMDY3MTggMjI0NS42MDIwNzMgOTkuNTI0NjY4IDMuMDAwMDAwIDAuMDAwMDAwIDAuMDE4MDE2IDAuMDAwMDAw
IDIuMDAwMDAwIDAuMjAwOTg1IDAuMDAwMDAwIDEuMDAwMDAwIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAt
IC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAiU3VwZXItc3VzdGFpbiAoTUlESSBDQy02NCkiAAAAQEA=
>
<PRESET `Ultra-Piano (MIDI CC-64)`
LTEyLjAwMDAwMCAwLjAwMDAwMCAyOTkuMDQ2MjUyIDMuNDY3NTU5IDQyMzMuNDIzNjY0IDgwLjk5OTM4NiAzLjAwMDAwMCAwLjAwMzE2MiAwLjEwMTY4NiAtNDcuMzU1
MTc2IDEuMjI5ODI3IDAuNDA1ODUyIDAuMDAwMDAwIDEuMDAwMDAwIDIuOTk0OTE0IDAuMDAwMDAwIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0g
LSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIFVsdHJhLVBpYW5vAAAAgEA=
>
<PRESET `Alien Takeoff`
LTUuNDc2OTYzIC0yLjY0NzM1NSAyNjcuMjk0MDkxIDAuODMxODgxIDc5MTkuNDIyNzc5IDEyMC4wMDAwMDAgMy4wMDAwMDAgMC4wMDAwMDAgMC4wMDAwMDAgMC4wMDAw
MDAgMi4wMDAwMDAgNS4wMDEwMDAgMC4wMDAwMDAgMC4wMDAwMDAgMzAwLjAwMDAwMCAxLjAwMDAwMCAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAt
IC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAiQWxpZW4gVGFrZW9mZiIAAACAQA==
>
<PRESET `Reduced To Noise`
LTMuMDk3MDYyIC02MC4wMDAwMDAgMTAxLjgxMjEyNSAwLjE1MDUzMyAzMDc3LjA3NDIxMSAxMjAuMDAwMDAwIDMuMDAwMDAwIDAuMDAwMDAwIDAuMDAwMDAwIDAuMDAw
MDAwIDIuMDAwMDAwIDAuMjAwMDAwIDAuMDAwMDAwIDAuMDAwMDAwIDg1MC42MjgxODEgMC4wMDAwMDAgMC4wMDAwMDAgMS4wMDAwMDAgLSAtIC0gLSAtIC0gLSAtIC0g
LSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAiUmVkdWNlZCBUbyBOb2lzZSIAAACg
QA==
>
<PRESET `Drifting Downwards`
LTUuMDUyOTIxIC0zLjAzMTQ4MyAyOTkuMDQ2MjUyIDAuNTQ0OTE5IDQyMzMuNDIzNjY0IDgwLjk5OTM4NiAzLjAwMDAwMCAwLjAwMzE2MiAwLjEwMTY4NiAwLjAwMDAw
MCAxLjIyOTgyNyA1LjAwMTAwMCAwLjAwMDAwMCAwLjAwMDAwMCA4MC4xNDY2MTEgLTAuNTAwMjI3IC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0g
LSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtICJEcmlmdGluZyBEb3dud2FyZHMiAAAAgEA=
>
>

133
plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/frequency-graph.jsfx-inc

@ -0,0 +1,133 @@
@init
function frequency_graph_init(low_freq, high_freq, low_db, high_db) (
this.low_freq = low_freq;
this.high_freq = high_freq;
this.low_db = low_db;
this.high_db = high_db;
);
function frequency_graph_x_hz(freq) local(xratio) (
xratio = log(freq/this.low_freq)/log(this.high_freq/this.low_freq);
//xratio = xratio*0.9 + 0.1*(freq - this.low_freq)/(this.high_freq - this.low_freq);
ui_left() + xratio*ui_width();
);
function frequency_graph_y_db(db) local(yratio) (
yratio = (db - this.high_db)/(this.low_db - this.high_db);
ui_top() + yratio*ui_height();
);
function frequency_graph_graph(buffer, count, high_freq, logfreq, dbAmpOrPower) local(i, hz, amp, db, step_counter, freq_range, x, prev_x, y) (
freq_range = log(this.high_freq/this.low_freq);
ui_push();
ui_retina(1);
i = 0;
prev_x = ui_left() - 1;
while (i < count) (
hz = logfreq == 0 ? i/count*high_freq : this.low_freq*exp(freq_range*i/count);
amp = buffer[i];
step_counter = 1;
while (i < count && this.frequency_graph_x_hz(hz) < prev_x + 0.5) (
i += 1;
hz = logfreq == 0 ? i/count*high_freq : this.low_freq*exp(freq_range*i/count);
amp += buffer[i];
step_counter += 1;
);
amp /= step_counter;
x = this.frequency_graph_x_hz(hz);
prev_x = x;
db = dbAmpOrPower == 0 ? amp : (
buffer[i] !== 0 ? (
dbAmpOrPower == 1 ? 20*log10(amp) : 10*log10(amp);
) : (
-360;
)
);
x = this.frequency_graph_x_hz(hz);
y = this.frequency_graph_y_db(db);
x = max(ui_left(), min(ui_right(), x));
y = max(ui_top(), min(ui_bottom(), y));
i == 0 ? (
gfx_x = x;
gfx_y = y;
) : (
gfx_lineto(x, y);
);
i += 1;
);
ui_pop();
);
function frequency_graph_hz_db(buffer, count, high_freq) (
this.frequency_graph_graph(buffer, count, high_freq, 0, 0);
);
function frequency_graph_grid() local(fi, fmag, freq, xratio, db) (
ui_push();
ui_retina(1);
fi = 1;
fmag = 10;
while (fi*fmag < this.high_freq) (
freq = fi*fmag;
freq > this.low_freq ? (
gfx_x = this.frequency_graph_x_hz(freq);
gfx_y = ui_top();
ui_color(255, 255, 255, 0.3);
gfx_lineto(gfx_x, ui_bottom());
fi == 1 ? (
ui_fontsize(10*ui_retina());
ui_color(255, 255, 255, 0.5);
gfx_x += ui_texth()*0.5;
gfx_y = ui_bottom() - ui_texth()*1.5;
fmag >= 1000 ? (
gfx_printf("%ikHz", freq/1000);
) : (
gfx_printf("%iHz", freq);
);
);
);
fi += 1;
fi >= 10 ? (
fi /= 10;
fmag *= 10;
);
);
db = 0;
while (abs(db) < max(abs(this.high_db), abs(this.low_db))) (
debug.plot += 1;
db > this.low_db && db < this.high_db ? (
gfx_y = this.frequency_graph_y_db(db);
debug.y = gfx_y;
gfx_x = ui_left();
db == 0 ? (
ui_color(255, 255, 255, 0.5);
) : (
ui_color(255, 255, 255, 0.15);
);
gfx_lineto(ui_right(), gfx_y);
ui_fontsize(10*ui_retina());
abs(gfx_y - this.frequency_graph_y_db(db*0.25)) > ui_texth() ? (
ui_color(255, 255, 255, 0.5);
gfx_x = ui_left() + ui_texth()*0.5;
gfx_y -= ui_texth()*1;
gfx_printf("%idB", dB);
);
);
db == 0 ? (
db = 3;
) : db > 0 ? (
db = -db;
) : (
db = -2*db;
);
);
ui_pop();
);

2970
plugins/jsfx/Geraint's JSFX/Delay/Atlantis-Reverb/ui-lib.jsfx-inc

File diff suppressed because it is too large

302
plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/Cross-Polyphonic-FM.jsfx

@ -0,0 +1,302 @@
desc:Cross-Polyphonic FM (by Geraint Luff)
in_pin:Left
in_pin:Right
out_pin:Left
out_pin:Right
slider1:attack_ms=3<0,1000>-attack (ms)
slider2:decay_ms=500<0, 1000>-decay (ms)
slider3:sustain_level=0.5<0, 1>-sustain
slider4:release_ms=10<3, 1000>-release (ms)
slider5:fm_depth=4<0, 10>-FM depth
slider6:fm_transpose_fine=0<-12, 12>-FM transpose (fine)
slider7:fm_transpose_octaves=0<-1,5,1>-FM transpose (octaves)
slider8:velocity_curve=0.5<0,2>-Velocity curve
import ui-lib.jsfx-inc
import delay-utils.jsfx-inc
import synth-framework-obj.jsfx-inc
import synth-framework-obj-ui.jsfx-inc
import smoother.jsfx-inc
filename:0,themes/bitmap-simple/theme-dark-orange.png
// Photo by Sebastian Pociecha on Unsplash: https://unsplash.com/photos/LfDFvUlUIDo
filename:1,themes/backgrounds/sebastian-pociecha-451575-unsplash.png
@init
MAX_DELAY = 0.1;
MAX_DELAY_SAMPLES = ceil(MAX_DELAY*srate);
gfx_ext_retina = 1;
srate = max(1000, srate);
freemem = 0;
freemem = ui_setup(freemem);
freemem = delay0.delay_setup(freemem, MAX_DELAY_SAMPLES);
freemem = delay1.delay_setup(freemem, MAX_DELAY_SAMPLES);
g_buffer_active = 0;
g_buffer_index = 0;
g_buffer_length = ceil(0.03*srate);
g_buffer_scale = max(g_buffer_scale, 0.01);
freemem = (g_buffer_dry = freemem) + g_buffer_length;
freemem = (g_buffer_wet = freemem) + g_buffer_length;
freemem = synth_setup(freemem);
synth_setup_each(synth1);
synth_setup_each(synth2);
synth_setup_each(synth3);
synth_setup_each(synth4);
synth_setup_each(synth5);
synth_setup_each(synth6);
synth_setup_each(synth7);
synth_setup_each(synth8);
synth_setup_each(synth9);
synth_setup_each(synth10);
synth_setup_each(synth11);
synth_setup_each(synth12);
synth_setup_each(synth13);
synth_setup_each(synth14);
synth_setup_each(synth15);
synth_setup_each(synth16);
smoother_init(fm_depth);
smoother_init(fm_transpose_fine);
@block
// synth framework boilerplate
synth_preblock_each(synth1)
&& synth_preblock_each(synth2)
&& synth_preblock_each(synth3)
&& synth_preblock_each(synth4)
&& synth_preblock_each(synth5)
&& synth_preblock_each(synth6)
&& synth_preblock_each(synth7)
&& synth_preblock_each(synth8)
&& synth_preblock_each(synth9)
&& synth_preblock_each(synth10)
&& synth_preblock_each(synth11)
&& synth_preblock_each(synth12)
&& synth_preblock_each(synth13)
&& synth_preblock_each(synth14)
&& synth_preblock_each(synth15)
&& synth_preblock_each(synth16);
synth_block();
synth_block_each(synth1)
&& synth_block_each(synth2)
&& synth_block_each(synth3)
&& synth_block_each(synth4)
&& synth_block_each(synth5)
&& synth_block_each(synth6)
&& synth_block_each(synth7)
&& synth_block_each(synth8)
&& synth_block_each(synth9)
&& synth_block_each(synth10)
&& synth_block_each(synth11)
&& synth_block_each(synth12)
&& synth_block_each(synth13)
&& synth_block_each(synth14)
&& synth_block_each(synth15)
&& synth_block_each(synth16);
attack_samples = attack_ms*0.001*srate;
decay_samples = decay_ms*0.001*srate;
release_samples = release_ms*0.001*srate;
transpose_factor = pow(2, fm_transpose_fine/12 + floor(fm_transpose_octaves + 0.5));
g_buffer_time < time() - 1 ? (
g_buffer_active = 0;
) : (
g_buffer_scale = max(g_buffer_scale, 0.01);
g_scale_decay = 1 - 1/srate;
);
smoothing = smoother_block(fm_depth) + smoother_block(transpose_factor);
@sample
smoother_block(fm_depth);
smoother_sample(transpose_factor);
g_buffer_active ? (
g_value = g_buffer_dry[g_buffer_index] = spl0 + spl1;
g_buffer_scale = max(g_buffer_scale*g_scale_decay, abs(g_value));
);
delay0.delay_input(spl0);
delay1.delay_input(spl1);
delay_factor = 0;
function process_note(note*) (
synth_sample_each(note) ? (
synth_needs_init(note) ? (
note.phase = 0;
note.amp = velocity_curve ? pow(synth_velocity(note)/127, velocity_curve) : 1;
);
envelope = note.amp;
synth_attack(note) < attack_samples ? (
envelope *= synth_attack(note)/attack_samples;
) : synth_attack(note) - attack_samples < decay_samples ? (
envelope *= 1 - (synth_attack(note) - attack_samples)/decay_samples*(1 - sustain_level);
) : (
envelope *= sustain_level;
);
synth_release(note) > 0 ? (
synth_release(note) >= release_samples ? (
synth_stop(note);
envelope = 0;
) : (
envelope *= 1 - synth_release(note)/release_samples;
);
);
z = smoother_value(transpose_factor)*synth_freq(note)/srate;
depth = envelope/(2*$pi*z);
delay_factor += depth*(0.5 - 0.5*cos(note.phase*2*$pi));
note.phase += z;
while (note.phase >= 1) (
note.phase -= 1;
);
);
synth_continue(note);
);
synth_sample();
process_note(synth1)
&& process_note(synth2)
&& process_note(synth3)
&& process_note(synth4)
&& process_note(synth5)
&& process_note(synth6)
&& process_note(synth7)
&& process_note(synth8)
&& process_note(synth9)
&& process_note(synth10)
&& process_note(synth11)
&& process_note(synth12)
&& process_note(synth13)
&& process_note(synth14)
&& process_note(synth15)
&& process_note(synth16);
delay_samples = delay_factor*smoother_sample(fm_depth);
delay_samples !== 0 ? (
spl0 = delay0.delay_output_linear1(delay_samples);
spl1 = delay1.delay_output_linear1(delay_samples);
);
g_buffer_active ? (
g_buffer_wet[g_buffer_index] = spl0 + spl1;
g_buffer_index += 1;
g_buffer_index >= g_buffer_length ? (
g_buffer_index = 0;
);
);
@gfx 580 320
g_buffer_active = 1;
g_buffer_time = time();
function labels(label, value, number_format, hidden_textnumber) local(h) (
h = (ui_height() - 60)/2;
ui_split_top(h);
ui_align(0.5, 1);
ui_text(label);
ui_pop();
ui_split_bottom(h);
ui_align(0.5, 0);
number_format >= 0 ? (
hidden_textnumber ? (
value = control_hidden_textnumber(value, number_format);
) : (
ui_textnumber(value, number_format);
);
);
ui_pop();
value;
);
function labels(label, value, number_format) (
labels(label, value, number_format, 0);
);
control_start("main", "bitmap-simple", 0);
gfx_a = 0.21;
g_scale = max(gfx_w/800, gfx_h/343);
gfx_x = (gfx_w - 800*g_scale)*0.8;
gfx_y = (gfx_h - 343*g_scale)*0.5;
gfx_blit(1, g_scale, 0);
gfx_a = 1;
ui_screen() == "main" ? (
control_navbar("Cross-Polyphonic FM", "config", "synth.config");
ui_split_topratio(1/2);
ui_split_leftratio(4/5);
control_group("Envelope");
ui_split_leftratio(1/4);
labels("Attack", attack_ms, "%i ms");
attack_ms = control_dial(attack_ms, 0, 1000, 5, 3);
ui_split_next();
labels("Decay", decay_ms, "%i ms");
decay_ms = control_dial(decay_ms, 0, 1000, 5, 500);
ui_split_next();
labels("Sustain", floor(sustain_level*100 + 0.5), "%i%%");
ui_automate(sustain_level, control_dial(sustain_level, 0, 1, 0, 0.5));
ui_split_next();
labels("Release", release_ms, "%i ms");
release_ms = control_dial(release_ms, 0, 1000, 5, 10);
ui_pop();
ui_pop();
control_group("Velocity");
labels("Velocity", 0, "curve");
velocity_curve = control_dial(velocity_curve, 0, 2, 2, 0.5);
ui_split_next();
ui_split_leftratio(1/4);
control_background_technical();
ui_push();
ui_split_topratio(0.5);
ui_color(160, 128, 128);
ui_graph(g_buffer_dry, g_buffer_length, g_buffer_index, -g_buffer_scale*1.5, g_buffer_scale*1.5);
ui_split_next();
ui_color(160, 64, 128);
ui_graph(g_buffer_wet, g_buffer_length, g_buffer_index, -g_buffer_scale*1.5, g_buffer_scale*1.5);
ui_pop();
control_finish_technical();
ui_pop();
ui_color(255, 255, 255, 0.1);
ui_border();
ui_pop();
control_group("FM oscillators");
ui_split_leftratio(1/3);
labels("Depth", fm_depth, "");
ui_automate(fm_depth, control_dial(fm_depth, 0, 10, 0.3, 4));
ui_split_next();
labels("Octave", 0, "");
sprintf(#fm_transpose_octaves, fm_transpose_octaves > 0 ? "+%i" : "%i", fm_transpose_octaves);
ui_automate(fm_transpose_octaves, control_selector(fm_transpose_octaves, #fm_transpose_octaves, min(fm_transpose_octaves + 1, 5), max(fm_transpose_octaves - 1, -1)));
ui_split_next();
ui_automate(fm_transpose_fine, labels("Fine-tune", fm_transpose_fine, "%.2f st", 1));
ui_automate(fm_transpose_fine, control_dial(fm_transpose_fine, -12, 12, 0, 0));
ui_pop();
ui_pop();
) : synth_ui_system() || control_system();
@serialize
preset_version = 2;
file_var(0, preset_version);
synth_serialize(preset_version < 2);

279
plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/delay-utils.jsfx-inc

@ -0,0 +1,279 @@
/* Generated by: JSFX Pre-Processor (https://github.com/geraintluff/jsfx-preprocessor) */
@init
function sinc(x) (
abs(x) < 0.000001 ? 1 : sin(x)/x;
);
function delay_config(key, value) (
key == "interpolation samples" ? (
this.config.interpolation_samples = min(32, max(4, ceil(value)));
);
);
function delay_setup(freemem, max_delay_samples) local(i, t, wr, w) (
this.interp_samples = this.config.interpolation_samples ? this.config.interpolation_samples : 4;
this.buffer_length = ceil(max_delay_samples + this.interp_samples + 1);
freemem = (this.buffer = freemem) + this.buffer_length;
i = 0;
while (i < this.buffer_length) (
this.buffer[i] = 0;
i += 1;
);
this.allpass.x1 = this.allpass.x2 = this.allpass.y1 = this.allpass.y2 = 0;
this.interp_scale = 64;
this.interp_offset = this.interp_samples/2;
freemem = (this.interp_buffer = freemem) + this.interp_samples*this.interp_scale;
i = 0;
while (i < this.interp_samples*this.interp_scale) (
wr = (i + 0.5)/(this.interp_samples*this.interp_scale);
// Blackman window
w = 0.42 - 0.5*cos(wr*$pi*2) + 0.08*cos(wr*$pi*4);
t = i/this.interp_scale - this.interp_offset;
this.interp_buffer[i] = sinc(t*$pi)*w; // Windowed sinc
i += 1;
);
freemem;
);
function delay_init(freemem, max_delay_samples) (
this.delay_setup(freemem, max_delay_samples);
);
function delay_size(max_delay_samples) local(length, interp_samples, interp_scale) (
interp_samples = this.config.interpolation_samples ? this.config.interpolation_samples : 4;
length = ceil(max_delay_samples + interp_samples + 1);
interp_scale = 64;
length + interp_scale*interp_samples;
);
function delay_input(spl) (
this.index += 1;
this.index >= this.buffer_length ? (
this.index = 0;
);
this.buffer[this.index] = spl;
);
function delay_output_linear1(samples) local(index, index1, value, value2) (
index = this.index - max(0, samples);
index1 = floor(index);
ratio = index - index1;
while (index1 < 0) (
index1 += this.buffer_length;
);
value = this.buffer[index1];
index1 += 1;
value2 = (index1 >= this.buffer_length) ? (
this.buffer[0];
) : (
this.buffer[index1];
);
value + ratio*(value2 - value);
);
function delay_output_linear(samples) local(index, index1, index2, ratio, i, interp_index, sum) (
index = this.index - max(this.interp_samples, samples + this.interp_offset - 1);
index1 = floor(index);
ratio = index - index1;
while (index1 < 0) (
index1 += this.buffer_length;
);
sum = 0;
index = index1;
interp_index = floor((1 - ratio)*this.interp_scale);
i = 0;
while (i < this.interp_samples) (
sum += this.interp_buffer[interp_index]*this.buffer[index];
interp_index += this.interp_scale;
index += 1;
index >= this.buffer_length ? (
index = 0;
);
i += 1;
);
sum;
);
function delay_output_samples(samples) local(index, buffer) (
index = this.index - samples;
while (index < 0) (
index += this.buffer_length;
);
this.buffer[index];
);
function delay_output_allpass(samples)
local(buffer, interp_buffer, index, index1, index2, Delta, ratio, x0, y0, a1, a2, b0, b1, b2) (
buffer = this.buffer;
index = this.index - max(3, samples);
index1 = ceil(index + 2 + 0.1); // This makes sure Delta is above 2
Delta = index1 - index;
Delta = 3;
while (index1 < 0) (
index1 += this.buffer_length;
);
this.allpass.prev_delay != index1 ? (
// Reset the filter
index2 = index1 - 1;
index2 < 0 ? index2 += this.buffer_length;
this.allpass.x1 = buffer[index2];
ratio = max(0, min(1, Delta));
// Linear interpolation to simulate previous output
this.allpass.y1 = buffer[index1] + ratio*(buffer[index2] - buffer[index1]);
this.allpass.prev_delay = index1;
);
// Second-order Thiran
a1 = -2*(Delta - 2)/(Delta + 1);
a2 = (Delta - 2)*(Delta - 1)/(Delta + 1)/(Delta + 2);
b0 = a2;
b1 = a1;
b2 = 1;
x0 = buffer[index1];
y0 = b0*x0 + b1*this.allpass.x1 + b2*this.allpass.x2
- a1*this.allpass.y1 - a2*this.allpass.y2;
this.allpass.x2 = this.allpass.x1;
this.allpass.x1 = x0;
this.allpass.y2 = this.allpass.y1;
this.allpass.y1 = y0;
);
/**************** Buffer versions ****************/
function delay_buffer_size(max_delay_samples) local(length, interp_samples, interp_scale) (
interp_samples = this.config.interpolation_samples ? this.config.interpolation_samples : 4;
length = ceil(max_delay_samples + interp_samples + 1);
interp_scale = 64;
12/*DELAY: interp_samples, length, index, allpass_prev_delay, buffer, interp_scale, interp_offset, interp_buffer, allpass_x1, allpass_y1, allpass_x2, allpass_y2*/ + length + interp_scale*interp_samples;
);
function delay_buffer_setup(obj, max_delay_samples) local(i, t, wr, w, obj, buffer, interp_buffer, freemem) (
i = 0;
while (i < 12/*DELAY*/) (
obj[i] = 0;
i += 1;
);
obj[0/*DELAY:interp_samples*/] = this.config.interpolation_samples ? this.config.interpolation_samples : 4;
obj[1/*DELAY:length*/] = ceil(max_delay_samples + obj[0/*DELAY:interp_samples*/] + 1);
obj[2/*DELAY:index*/] = 0;
obj[3/*DELAY:allpass_prev_delay*/] = 1;
freemem = obj + 12/*DELAY*/;
buffer = obj[4/*DELAY:buffer*/] = freemem;
freemem = obj[4/*DELAY:buffer*/] + obj[1/*DELAY:length*/];
i = 0;
while (i < obj[1/*DELAY:length*/]) (
buffer[i] = 0;
i += 1;
);
obj[5/*DELAY:interp_scale*/] = 64;
obj[6/*DELAY:interp_offset*/] = obj[0/*DELAY:interp_samples*/]/2;
interp_buffer = obj[7/*DELAY:interp_buffer*/] = freemem;
freemem = interp_buffer + obj[0/*DELAY:interp_samples*/]*obj[5/*DELAY:interp_scale*/];
i = 0;
while (i < obj[0/*DELAY:interp_samples*/]*obj[5/*DELAY:interp_scale*/]) (
wr = (i + 0.5)/(obj[0/*DELAY:interp_samples*/]*obj[5/*DELAY:interp_scale*/]);
// Blackman window
w = 0.42 - 0.5*cos(wr*$pi*2) + 0.08*cos(wr*$pi*4);
t = i/obj[5/*DELAY:interp_scale*/] - obj[6/*DELAY:interp_offset*/];
interp_buffer[i] = sinc(t*$pi)*w; // Windowed sinc
i += 1;
);
freemem;
);
function delay_buffer_init(freemem, max_delay_samples) (
delay_buffer_setup(freemem, max_delay_samples);
);
function delay_buffer_input(obj, spl) local(buffer, index) (
index = (obj[2/*DELAY:index*/] += 1);
obj[2/*DELAY:index*/] >= obj[1/*DELAY:length*/] ? (
index = obj[2/*DELAY:index*/] = 0;
);
buffer = obj[4/*DELAY:buffer*/];
buffer[index] = spl;
spl;
);
function delay_buffer_output_samples(obj, samples) local(index, buffer) (
index = obj[2/*DELAY:index*/] - samples;
while (index < 0) (
index += obj[1/*DELAY:length*/];
);
buffer = obj[4/*DELAY:buffer*/];
buffer[index];
);
function delay_buffer_output_linear(obj, samples) local(index, index1, index2, ratio, i, interp_buffer, buffer, interp_index, sum) (
index = obj[2/*DELAY:index*/] - max(obj[0/*DELAY:interp_samples*/], samples + obj[6/*DELAY:interp_offset*/] - 1);
index1 = floor(index);
ratio = index - index1;
while (index1 < 0) (
index1 += obj[1/*DELAY:length*/];
);
sum = 0;
index = index1;
buffer = obj[4/*DELAY:buffer*/];
interp_buffer = obj[7/*DELAY:interp_buffer*/];
interp_index = floor((1 - ratio)*obj[5/*DELAY:interp_scale*/]);
i = 0;
while (i < obj[0/*DELAY:interp_samples*/]) (
sum += interp_buffer[interp_index]*buffer[index];
interp_index += obj[5/*DELAY:interp_scale*/];
index += 1;
index >= obj[1/*DELAY:length*/] ? (
index = 0;
);
i += 1;
);
sum;
);
// SHOULD NOT USE
function __deprecated__delay_buffer_output_allpass(obj, samples)
local(buffer, interp_buffer, index, index1, index2, delta, ratio, x0, y0, a1, a2, b0, b1, b2) (
buffer = obj[4/*DELAY:buffer*/];
index = obj[2/*DELAY:index*/] - max(3, samples);
index1 = ceil(index + 2 + 0.1); // This makes sure Delta is above 2
Delta = index1 - index;
Delta = 3;
while (index1 < 0) (
index1 += obj[1/*DELAY:length*/];
);
obj[3/*DELAY:allpass_prev_delay*/] != index1 ? (
// Reset the filter
index2 = index1 - 1;
index2 < 0 ? index2 += obj[1/*DELAY:length*/];
obj[8/*DELAY:allpass_x1*/] = buffer[index2];
ratio = max(0, min(1, Delta));
// Linear interpolation to simulate previous output
obj[9/*DELAY:allpass_y1*/] = buffer[index1] + ratio*(buffer[index2] - buffer[index1]);
obj[3/*DELAY:allpass_prev_delay*/] = index1;
);
// Second-order Thiran
a1 = -2*(Delta - 2)/(Delta + 1);
a2 = (Delta - 2)*(Delta - 1)/(Delta + 1)/(Delta + 2);
b0 = a2;
b1 = a1;
b2 = 1;
x0 = buffer[index1];
y0 = b0*x0 + b1*obj[8/*DELAY:allpass_x1*/] + b2*obj[10/*DELAY:allpass_x2*/]
- a1*obj[9/*DELAY:allpass_y1*/] - a2*obj[11/*DELAY:allpass_y2*/];
obj[10/*DELAY:allpass_x2*/] = obj[8/*DELAY:allpass_x1*/];
obj[8/*DELAY:allpass_x1*/] = x0;
obj[11/*DELAY:allpass_y2*/] = obj[9/*DELAY:allpass_y1*/];
obj[9/*DELAY:allpass_y1*/] = y0;
y0;
);

51
plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/smoother.jsfx-inc

@ -0,0 +1,51 @@
/*
// init
smoother_init(var1);
smoother_init(var2);
// block
smoothing = smoother_block(var1) + smoother_block(var2);
// sample
smoothing ? (
smoother_block(var1);
smoother_sample(var2);
);
*/
@init
function smoother_init(slidervar*) (
slidervar.value = slidervar;
slidervar.step = 0;
1;
);
function smoother_block(slidervar*, modulo) local(half) (
slidervar != slidervar.value ? (
half = modulo*0.5;
while (slidervar - slidervar.value > half) (
slidervar.value += modulo;
);
while (slidervar - slidervar.value < -half) (
slidervar.value -= modulo;
);
slidervar.step = (slidervar - slidervar.value)/samplesblock;
1;
) : (
slidervar.step = 0;
);
);
function smoother_block(slidervar*) (
slidervar != slidervar.value ? (
slidervar.step = (slidervar - slidervar.value)/samplesblock;
1;
) : (
slidervar.step = 0;
);
);
function smoother_sample(slidervar*) (
slidervar.value += slidervar.step;
);
function smoother_value(slidervar*) (
slidervar.value;
);

87
plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/synth-framework-obj-ui.jsfx-inc

@ -0,0 +1,87 @@
@init
function synthx_ui_system_labels(label, value, number_format) local(h) (
h = max((ui_height() - 60)/2, ui_height()*0.2);
ui_split_top(h);
ui_align(0.5, 1);
ui_text(label);
ui_pop();
ui_split_bottom(h);
ui_align(0.5, 0);
number_format >= 0 ? (
ui_textnumber(value, number_format);
);
ui_pop();
);
function synth_ui_system() local(allow_latency) (
ui_screen() == "synth.config" ? (
allow_latency = synthx_supports_latency;
control_dialog("Synth config");
ui_split_topratio(1/3);
control_group("Legato");
ui_split_leftratio(0.5);
synthx_ui_system_labels("Monophonic", 0, "");
ui_pad(-1, 0);
#synthx_legato_mode = (synthx_legato_mode == 0) ? "off" : (synthx_legato_mode == 1) ? "retrigger (mono)" : "slide (mono)";
synthx_legato_mode = control_selector(synthx_legato_mode, #synthx_legato_mode, min(2, synthx_legato_mode + 1), max(0, synthx_legato_mode - 1));
ui_split_next();
synthx_ui_system_labels("Portamento", 0, "");
ui_pad(-1, 0);
#synthx_legato_mode = (synthx_legato_portamento_mode == 0) ? "legato only" : (synthx_legato_portamento_mode == 1) ? "all notes" : "notes + release";
synthx_legato_portamento_mode = control_selector(synthx_legato_portamento_mode, #synthx_legato_mode, min(2, synthx_legato_portamento_mode + 1), max(0, synthx_legato_portamento_mode - 1));
ui_pop();
synth_legato(synthx_legato_mode, synthx_legato_portamento_mode);
ui_split_next();
control_group("Portamento");
ui_split_leftratio(allow_latency ? 1/4 : 1/3);
synthx_ui_system_labels("Enabled", 0, "");
ui_pad(-1, 0);
synthx_portamento_mode = control_selector(synthx_portamento_mode, (synthx_portamento_mode == 0) ? "off" : (synthx_portamento_mode == 1) ? "on" : (synthx_portamento_mode == 2) ? "MIDI (CC 5)" : "MIDI (CC 5 + 65)", min(3, synthx_portamento_mode + 1), max(0, synthx_portamento_mode - 1));
ui_split_next();
synthx_portamento_mode ? (
synthx_ui_system_labels("Time", floor(synthx_portamento_seconds*1000 + 0.5), "%i ms");
ui_pad(-1, 0);
synthx_portamento_seconds = max(0.001, control_dial(synthx_portamento_seconds, 0.001, 1, 5, 0.03));
);
ui_split_next();
synthx_portamento_mode ? (
synthx_ui_system_labels("Source note", 0, synthx_portamento_from_nearest ? "nearest" : "latest");
ui_pad(-1, 0);
synthx_portamento_from_nearest = control_switch(synthx_portamento_from_nearest);
);
allow_latency && synthx_portamento_mode ? (
ui_split_next();
synthx_ui_system_labels("Latency", floor(synthx_delay_ratio*100 + 0.5), "%i%%");
ui_pad(-1, 0);
synthx_delay_ratio = control_dial(synthx_delay_ratio, 0, 1, 0, 0);
);
ui_pop();
synth_portamento(synthx_portamento_mode, synthx_portamento_seconds);
ui_split_next();
control_group("");
ui_split_leftratio(0.5);
synthx_ui_system_labels("MIDI passthrough", 0, "");
ui_pad(-1, 0);
synthx_midi_sink = !control_switch(!synthx_midi_sink);
ui_split_next();
synthx_ui_system_labels("Pitch-bend", 0, "");
ui_pad(-1, 0);
sprintf(#synthx_pitchbend_range, "%i semitones", synthx_pitchbend_range);
synthx_pitchbend_range = control_selector(synthx_pitchbend_range, #synthx_pitchbend_range, min(24, synthx_pitchbend_range + 1), max(1, synthx_pitchbend_range - 1));
ui_pop();
ui_pop();
/*
file_var(0, synthx_midi_sink);
file_var(0, synthx_legato_mode);
file_var(0, synthx_legato_portamento_mode);
file_var(0, synthx_portamento_mode);
file_var(0, synthx_portamento_seconds);
file_var(0, synthx_pitchbend_range);
*/
1;
) : 0;
);

477
plugins/jsfx/Geraint's JSFX/Delay/Cross-Polyphonic-FM/synth-framework-obj.jsfx-inc

@ -0,0 +1,477 @@
/* Generated by: JSFX Pre-Processor (https://github.com/geraintluff/jsfx-preprocessor) */
@init
function synth_reset_controllers() local(i) (
synthx_pitchbend = 0;
synthx_pitchbend_ratio = 1;
i = 0;
while (i < 128) (
(i == 7 || i == 10 || (i >= 91 && i <= 95) || (i >= 70 && i <= 79) || i >= 120) ? (
0; // These don't get reset
) : (i >= 98 && i <= 101) ? (
synthx_controllers[i] = 127;
) : (i != 0 && i != 32) ? (
synthx_controllers[i] = 0;
);
i += 1;
);
synthx_controllers[11] = 127;
);
function synth_option_midi_sink(value) (
synthx_midi_sink = value;
);
function synth_serialize(reset) local(version) (
version = 3;
file_var(0, version);
!reset && version >= 1 ? (
file_var(0, synthx_midi_sink);
file_var(0, synthx_legato_mode);
file_var(0, synthx_legato_portamento_mode);
file_var(0, synthx_portamento_mode);
file_var(0, synthx_portamento_seconds);
file_var(0, synthx_pitchbend_range);
) : (
synthx_midi_sink = 0;
synthx_legato_mode = 0;
synthx_legato_portamento_mode = 0;
synthx_portamento_mode = 0;
synthx_portamento_seconds = 0.03;
synthx_pitchbend_range = 2;
);
!reset && version >= 2 ? (
file_var(0, synthx_portamento_from_nearest);
) : (
synthx_portamento_from_nearest = 0;
);
!reset && version >= 3 ? (
file_var(0, synthx_delay_ratio);
) : (
synthx_delay_ratio = 0;
);
);
function synth_setup(freemem, custom_slots, max_polyphony) local(i) (
synthx_polyphony = 0;
synthx_custom_offset = 12/*NOTE: BASE_FREQ, PORTAMENTO_END_SAMPLE, BASE_FREQ_SLOPE, SAMPLES_FROM_ATTACK, ACTIVE, CHANNEL, SAMPLES_FROM_RELEASE, SAMPLES_FROM_SUSTAIN_RELEASE, NOTE, PHRASE_STILL_ACTIVE, VEL, CUSTOM_INIT*/;
synthx_timestep = 1/srate;
synthx_step = synthx_custom_offset + custom_slots;
synthx_maxduration = 60*60*24*365*srate; // one year
synthx_pitchbend = 0;
synthx_pitchbend_ratio = 1;
!synthx_pitchbend_range ? synthx_pitchbend_range = 2;
!synthx_portamento_seconds ? synthx_portamento_seconds = 0.03; // Not enabled, though :)
synthx_max_active_note = 0;
freemem = (synthx_controllers = freemem) + 128;
freemem = (synthx_notestack = freemem) + max_polyphony*synthx_step;
synth_reset_controllers();
i = 0;
while (i < max_polyphony*synthx_step) (
synthx_notestack[i] = 0;
i += 1;
);
synthx_latest_note = synthx_notestack;
synthx_latest_note[0/*NOTE:BASE_FREQ*/] = 440*pow(2, (63 - 69)/12); // Start on note 63
synthx_latest_note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = 0;
synthx_latest_note[2/*NOTE:BASE_FREQ_SLOPE*/] = 0;
/*NOTE: ACTIVE, SAMPLES_FROM_RELEASE, SAMPLES_FROM_ATTACK, SAMPLES_FROM_SUSTAIN_RELEASE, NOTE, BASE_FREQ, VEL, CHANNEL, CUSTOM_INIT*/
// synth_preblock_each() and synth_block_each() need to have considered all of these
synthx_custom_offset !== 9 ? (
synthx.ERROR = uix_error = "synth framework error";
);
freemem;
);
function synth_pdc_delay() (
synthx_supports_latency = 1;
floor(synthx_delay_ratio*synthx_portamento_seconds*srate);
);
function synth_setup(freemem, custom_slots) (
synth_setup(freemem, custom_slots, 16);
);
function synth_setup(freemem) (
synth_setup(freemem, 0);
);
function synth_setup_each(note*) (
note._synth.index = synthx_polyphony;
note._synth.mem = synthx_notestack + synthx_step*note._synth.index;
note = note._synth.mem + synthx_custom_offset;
synthx_polyphony += 1;
);
function synth_legato(mode/*0: off, 1: legato but still trigger, 2: suppress new notes */, portamento_mode/*0: legato notes only, 1: legato and first notes, 2: releases as well*/) (
synthx_legato_mode = mode;
synthx_legato_portamento_mode = portamento_mode;
);
function synth_legato(enabled) (
synth_legato(enabled, 0);
);
function synth_portamento(mode, seconds) (
synthx_portamento_mode = mode;
synthx_portamento_seconds = seconds;
);
function synth_portamento_future_freq_buffer(note) (
note[3/*NOTE:SAMPLES_FROM_ATTACK*/] + synthx_delay_samples < note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] ? (
(note[0/*NOTE:BASE_FREQ*/]
+ min(0, note[3/*NOTE:SAMPLES_FROM_ATTACK*/] + synthx_delay_samples - note[1/*NOTE:PORTAMENTO_END_SAMPLE*/])*note[2/*NOTE:BASE_FREQ_SLOPE*/]
);
) : (
note[0/*NOTE:BASE_FREQ*/];
);
);
// Same logic as before - perhaps this should be a mod on top of synth-framework, that just caches things for the @sample block?
function synth_block() local(midi_offset, midi_offset_delayed, midi_msg1, midi_msg23, midi_msg2, midi_msg3, midi_type, midi_channel, i, note, selected_note, base_freq, current_freq, portamento_samples, first_note_of_phrase, previous_note, distance, nearest_distance) (
synthx_portamento_samples = synthx_portamento_seconds*srate;
synthx_delay_samples = floor(synthx_delay_ratio*synthx_portamento_samples);
while (midirecv(midi_offset, midi_msg1, midi_msg23)) (
!synthx_midi_sink ? (
midisend(midi_offset, midi_msg1, midi_msg23); // passthrough
);
midi_offset_delayed = midi_offset + synthx_delay_samples;
midi_type = midi_msg1>>4;
midi_channel = midi_msg1&0x0f;
midi_msg2 = midi_msg23&$xff; // note / controller
midi_msg3 = midi_msg23>>8; // velocity / value
(midi_type == $x9 && midi_msg3 != 0) ? (
// Note on
base_freq = 440*pow(2, (midi_msg2 - 69)/12);
// Is this the first note of a phrase
first_note_of_phrase = 1;
first_note_of_phrase_sustained = 1;
previous_note = synthx_latest_note; // TODO: In case nothing is active, use previous note - ideally we'd have one of these per-channel, but we can't, because there's no guarantee some other channel wouldn't've used it in the meantime. Perhaps we should cache the latest *frequency* per-channel, because that's what we actually need.
nearest_distance = 128;
i = 0;
// TODO: use loop() instead, and increment by synth_step?
while (i < synthx_polyphony) (
note = synthx_notestack + synthx_step*i;
// This isn't quite accurate - if the sustain pedal is down then it might not register as released, even if the sustain is released later in this block and it would cross the line. That's probably enough of an edge-case to fudge, though.
note[4/*NOTE:ACTIVE*/] && note[5/*NOTE:CHANNEL*/] == midi_channel && (note[6/*NOTE:SAMPLES_FROM_RELEASE*/] <= -midi_offset_delayed
|| (note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] <= 0 && synthx_controllers[64] >= 64)
) ? (
first_note_of_phrase_sustained = 0;
note[6/*NOTE:SAMPLES_FROM_RELEASE*/] <= -midi_offset_delayed ? (
first_note_of_phrase = 0;
);
distance = synthx_portamento_from_nearest ? (
distance = abs(note[8/*NOTE:NOTE*/] - midi_msg2);
) : 0;
distance < nearest_distance || (distance == nearest_distance && note[3/*NOTE:SAMPLES_FROM_ATTACK*/] < previous_note[3/*NOTE:SAMPLES_FROM_ATTACK*/]) ? (
previous_note = note;
nearest_distance = distance;
);
);
i += 1;
);
synthx_legato_mode ? (
// Move all unreleased notes to new frequency
i = 0;
while (i < synthx_polyphony) (
note = synthx_notestack + synthx_step*i;
note[4/*NOTE:ACTIVE*/] && note[5/*NOTE:CHANNEL*/] == midi_channel ? (
note[9/*NOTE:PHRASE_STILL_ACTIVE*/] ? (
first_note_of_phrase && synthx_legato_portamento_mode < 2 ? (
// Any previous phrases are closed
note[9/*NOTE:PHRASE_STILL_ACTIVE*/] = 0;
) : (
synthx_portamento_mode ? (
current_freq = synth_portamento_future_freq_buffer(note);
portamento_samples = synthx_portamento_samples;
synthx_portamento_mode >= 2 ? portamento_samples *= synthx_controllers[5]/127;
synthx_portamento_mode == 3 && synthx_controllers[65] < 64 ? portamento_samples = 0;
portamento_samples += midi_offset;
note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = note[3/*NOTE:SAMPLES_FROM_ATTACK*/] + portamento_samples;
note[2/*NOTE:BASE_FREQ_SLOPE*/] = portamento_samples ? (base_freq - current_freq)/portamento_samples : 0;
) : (
note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = 0;
note[2/*NOTE:BASE_FREQ_SLOPE*/] = 0;
);
note[0/*NOTE:BASE_FREQ*/] = base_freq;
debug.transition += 1;
debug.note_from = note[8/*NOTE:NOTE*/];
debug.note_to = midi_msg2;
note[8/*NOTE:NOTE*/] = midi_msg2;
synthx_legato_mode < 2 ? ( // We're going to re-trigger a new note, so we close this one
debug.retrigger += 1;
note[6/*NOTE:SAMPLES_FROM_RELEASE*/] = max(note[6/*NOTE:SAMPLES_FROM_RELEASE*/], -midi_offset_delayed);
note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = max(note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/], -midi_offset_delayed);
) : note[6/*NOTE:SAMPLES_FROM_RELEASE*/] == -midi_offset_delayed ? (
debug.force_continue += 1;
// This note was just released, but it should continue transition to the new pitch instead
selected_note[6/*NOTE:SAMPLES_FROM_RELEASE*/] = -synthx_maxduration;
selected_note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = -synthx_maxduration;
);
);
)
);
i += 1;
);
);
(first_note_of_phrase || synthx_legato_mode < 2) ? (
selected_note = -1;
// Choose inactive note
i = 0;
while (i < synthx_polyphony && selected_note < 0) (
note = synthx_notestack + synthx_step*i;
!note[4/*NOTE:ACTIVE*/] ? (
selected_note = note;
);
i += 1;
);
// If we didn't find one, stop a release
selected_note < 0 ? (
// Start by selecting random index
i = floor(rand()*synthx_polyphony);
selected_note = synthx_notestack + synthx_step*i;
// Choose note with longest release phase
i = 0;
while (i < synthx_polyphony && selected_note < 0) (
note = synthx_notestack + synthx_step*i;
note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] > selected[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] ? (
selected_note = note;
);
i += 1;
);
);
// Reset the custom note parameters to 0
i = synthx_custom_offset;
while (i < synthx_step) (
selected_note[i] = 0;
i += 1;
);
// Set up the note parameters
selected_note[4/*NOTE:ACTIVE*/] = 1;
synthx_portamento_mode && (!first_note_of_phrase_sustained || synthx_legato_portamento_mode > 0) ? (
// This calculation goes first, in case this is the same note we're sweeping from
portamento_samples = synthx_portamento_samples;
synthx_portamento_mode >= 2 ? portamento_samples *= synthx_controllers[5]/127;
synthx_portamento_mode == 3 && synthx_controllers[65] < 64 ? portamento_samples = 0;
selected_note[2/*NOTE:BASE_FREQ_SLOPE*/] = portamento_samples ? (base_freq - synth_portamento_future_freq_buffer(previous_note))/portamento_samples : 0;
selected_note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = portamento_samples;
) : (
selected_note[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = 0;
selected_note[2/*NOTE:BASE_FREQ_SLOPE*/] = 0;
);
selected_note[0/*NOTE:BASE_FREQ*/] = base_freq;
selected_note[3/*NOTE:SAMPLES_FROM_ATTACK*/] = -midi_offset_delayed;
selected_note[6/*NOTE:SAMPLES_FROM_RELEASE*/] = -synthx_maxduration;
selected_note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = -synthx_maxduration;
selected_note[8/*NOTE:NOTE*/] = midi_msg2;
selected_note[10/*NOTE:VEL*/] = midi_msg3;
selected_note[5/*NOTE:CHANNEL*/] = midi_channel;
selected_note[11/*NOTE:CUSTOM_INIT*/] = 0;
selected_note[9/*NOTE:PHRASE_STILL_ACTIVE*/] = 1;
synthx_latest_note = selected_note;
);
) : (midi_type == $x8 || (midi_type == $x9 && midi_msg3 == 0)) ? (
// Close open note if there is one
i = 0;
while (i < synthx_polyphony) (
note = synthx_notestack + synthx_step*i;
note[8/*NOTE:NOTE*/] == midi_msg2 && note[5/*NOTE:CHANNEL*/] == midi_channel ? (
note[6/*NOTE:SAMPLES_FROM_RELEASE*/] = max(note[6/*NOTE:SAMPLES_FROM_RELEASE*/], -midi_offset_delayed);
note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = max(note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/], -midi_offset_delayed);
);
i += 1;
);
) : (midi_type == 11) ? (
// Controller
midi_msg2 == 121 ? (
synth_reset_controllers();
) : midi_msg2 == 123 ? (
// stop all notes (with release)
i = 0;
while (i < synthx_polyphony) (
note = synthx_notestack + synthx_step*i;
note[6/*NOTE:SAMPLES_FROM_RELEASE*/] = max(note[6/*NOTE:SAMPLES_FROM_RELEASE*/], 0);
note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = max(note[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/], 0);
i += 1;
);
) : midi_msg2 == 120 ? (
// stop all notes (no release)
i = 0;
while (i < synthx_polyphony) (
note = synthx_notestack + synthx_step*i;
note[4/*NOTE:ACTIVE*/] = 0;
i += 1;
);
) : (
synthx_controllers[midi_msg2] = midi_msg3;
);
) : (midi_type == 14) ? (
synthx_pitchbend = (midi_msg3*128 + midi_msg2) - 8192;
synthx_pitchbend_ratio = pow(2, synthx_pitchbend_range/12*synthx_pitchbend/8192);
);
);
synthx_max_active_note = -1;
i = 0;
note = synthx_notestack;
while (i < synthx_polyphony) (
note[4/*NOTE:ACTIVE*/] ? (
synthx_max_active_note = i;
);
i += 1;
note += synthx_step;
);
synthx_max_active_note + 1;
);
function synth_preblock_each(note*) local() (
// We don't need to set _mem[4/*NOTE:ACTIVE*/], because synth_stop() does that
note._synth.ACTIVE ? (
note._synth.mem[6/*NOTE:SAMPLES_FROM_RELEASE*/] = note._synth.SAMPLES_FROM_RELEASE;
note._synth.mem[3/*NOTE:SAMPLES_FROM_ATTACK*/] = note._synth.SAMPLES_FROM_ATTACK;
note._synth.mem[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/] = note._synth.SAMPLES_FROM_SUSTAIN_RELEASE;
note._synth.mem[11/*NOTE:CUSTOM_INIT*/] = note._synth.CUSTOM_INIT;
);
/* Not ever changed in @sample */
//note._synth.mem[8/*NOTE:NOTE*/] = note._synth.NOTE;
//note._synth.mem[0/*NOTE:BASE_FREQ*/] = note._synth.BASE_FREQ;
//note._synth.mem[2/*NOTE:BASE_FREQ_SLOPE*/] = note._synth.BASE_FREQ_SLOPE;
//note._synth.mem[1/*NOTE:PORTAMENTO_END_SAMPLE*/] = note._synth.PORTAMENTO_END_SAMPLE;
//note._synth.mem[10/*NOTE:VEL*/] = note._synth.VEL;
//note._synth.mem[5/*NOTE:CHANNEL*/] = note._synth.CHANNEL;
// TODO: does synth_continue(note) make this faster or slower here?
//synth_continue(note);
1;
);
function synth_block_each(note*) local() (
(note._synth.ACTIVE = note._synth.mem[4/*NOTE:ACTIVE*/]) ? (
note._synth.SAMPLES_FROM_RELEASE = note._synth.mem[6/*NOTE:SAMPLES_FROM_RELEASE*/];
note._synth.SAMPLES_FROM_ATTACK = note._synth.mem[3/*NOTE:SAMPLES_FROM_ATTACK*/];
note._synth.SAMPLES_FROM_SUSTAIN_RELEASE = note._synth.mem[7/*NOTE:SAMPLES_FROM_SUSTAIN_RELEASE*/];
note._synth.NOTE = note._synth.mem[8/*NOTE:NOTE*/];
note._synth.BASE_FREQ = note._synth.mem[0/*NOTE:BASE_FREQ*/];
note._synth.BASE_FREQ_SLOPE = note._synth.mem[2/*NOTE:BASE_FREQ_SLOPE*/];
note._synth.PORTAMENTO_END_SAMPLE = note._synth.mem[1/*NOTE:PORTAMENTO_END_SAMPLE*/];
note._synth.VEL = note._synth.mem[10/*NOTE:VEL*/];
note._synth.CHANNEL = note._synth.mem[5/*NOTE:CHANNEL*/];
note._synth.CUSTOM_INIT = note._synth.mem[11/*NOTE:CUSTOM_INIT*/];
);
//synth_continue(note);
1;
);
function synth_on(note*) (
note._synth.ACTIVE && note._synth.SAMPLES_FROM_ATTACK >= 0;
);
function synth_sample() (
synthx_max_active_note >= 0;
);
function synth_sample_each(note*) (
note._synth.ACTIVE ? (
note._synth.SAMPLES_FROM_ATTACK += 1;
note._synth.SAMPLES_FROM_RELEASE += 1;
note._synth.SAMPLES_FROM_RELEASE < 0 || synthx_controllers[64] < 64 ? (
note._synth.SAMPLES_FROM_SUSTAIN_RELEASE += 1;
);
note._synth.SAMPLES_FROM_ATTACK >= 0
) : 0;
);
function synth_continue(note*) (
note._synth.index < synthx_max_active_note;
);