From 67e8fca3888b39644962a7e1f4f2824d1911df49 Mon Sep 17 00:00:00 2001 From: TiagoLr Date: Tue, 12 Dec 2023 17:22:07 +0000 Subject: [PATCH] Release JSAdditiv v1.1 --- Synth/tilr_JSAdditiv.jsfx | 580 +++++++++++++++++ Synth/tilr_JSAdditiv/Complex 1.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Complex 2.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Complex 3.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Complex 4.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Complex 5.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 1.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 2.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 3.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 4.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 5.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 6.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Organ 7.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Saw 1.wav | Bin 0 -> 2092 bytes Synth/tilr_JSAdditiv/Saw 2.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Saw 3.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Sine 1.wav | Bin 0 -> 2092 bytes Synth/tilr_JSAdditiv/Sine 2.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Sine 3.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Sine 4.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Sine 5.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Spectral.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Square 1.wav | Bin 0 -> 2092 bytes Synth/tilr_JSAdditiv/Square 2.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Stairs.wav | Bin 0 -> 1344 bytes Synth/tilr_JSAdditiv/Triangle.wav | Bin 0 -> 2092 bytes Synth/tilr_JSAdditiv/add.adsr.jsfx-inc | 129 ++++ Synth/tilr_JSAdditiv/add.array.jsfx-inc | 269 ++++++++ .../add.fft_real_synth.jsfx-inc | 214 +++++++ Synth/tilr_JSAdditiv/add.gfxlib.jsfx-inc | 214 +++++++ Synth/tilr_JSAdditiv/add.mouselib.jsfx-inc | 34 + Synth/tilr_JSAdditiv/add.wavetable.jsfx-inc | 595 ++++++++++++++++++ Synth/tilr_JSAdditiv/add.zdf_filter.jsfx-inc | 403 ++++++++++++ 33 files changed, 2438 insertions(+) create mode 100644 Synth/tilr_JSAdditiv.jsfx create mode 100644 Synth/tilr_JSAdditiv/Complex 1.wav create mode 100644 Synth/tilr_JSAdditiv/Complex 2.wav create mode 100644 Synth/tilr_JSAdditiv/Complex 3.wav create mode 100644 Synth/tilr_JSAdditiv/Complex 4.wav create mode 100644 Synth/tilr_JSAdditiv/Complex 5.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 1.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 2.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 3.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 4.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 5.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 6.wav create mode 100644 Synth/tilr_JSAdditiv/Organ 7.wav create mode 100644 Synth/tilr_JSAdditiv/Saw 1.wav create mode 100644 Synth/tilr_JSAdditiv/Saw 2.wav create mode 100644 Synth/tilr_JSAdditiv/Saw 3.wav create mode 100644 Synth/tilr_JSAdditiv/Sine 1.wav create mode 100644 Synth/tilr_JSAdditiv/Sine 2.wav create mode 100644 Synth/tilr_JSAdditiv/Sine 3.wav create mode 100644 Synth/tilr_JSAdditiv/Sine 4.wav create mode 100644 Synth/tilr_JSAdditiv/Sine 5.wav create mode 100644 Synth/tilr_JSAdditiv/Spectral.wav create mode 100644 Synth/tilr_JSAdditiv/Square 1.wav create mode 100644 Synth/tilr_JSAdditiv/Square 2.wav create mode 100644 Synth/tilr_JSAdditiv/Stairs.wav create mode 100644 Synth/tilr_JSAdditiv/Triangle.wav create mode 100644 Synth/tilr_JSAdditiv/add.adsr.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.array.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.fft_real_synth.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.gfxlib.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.mouselib.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.wavetable.jsfx-inc create mode 100644 Synth/tilr_JSAdditiv/add.zdf_filter.jsfx-inc diff --git a/Synth/tilr_JSAdditiv.jsfx b/Synth/tilr_JSAdditiv.jsfx new file mode 100644 index 0000000..c8d2570 --- /dev/null +++ b/Synth/tilr_JSAdditiv.jsfx @@ -0,0 +1,580 @@ +desc: JSAdditiv +author: tilr +version: 1.1 +provides: + tilr_JSAdditiv/add.adsr.jsfx-inc + tilr_JSAdditiv/add.array.jsfx-inc + tilr_JSAdditiv/add.fft_real_synth.jsfx-inc + tilr_JSAdditiv/add.gfxlib.jsfx-inc + tilr_JSAdditiv/add.mouselib.jsfx-inc + tilr_JSAdditiv/add.wavetable.jsfx-inc + tilr_JSAdditiv/add.zdf_filter.jsfx-inc + [data] tilr_JSAdditiv/Complex 1.wav + [data] tilr_JSAdditiv/Complex 2.wav + [data] tilr_JSAdditiv/Complex 3.wav + [data] tilr_JSAdditiv/Complex 4.wav + [data] tilr_JSAdditiv/Complex 5.wav + [data] tilr_JSAdditiv/Organ 1.wav + [data] tilr_JSAdditiv/Organ 2.wav + [data] tilr_JSAdditiv/Organ 3.wav + [data] tilr_JSAdditiv/Organ 4.wav + [data] tilr_JSAdditiv/Organ 5.wav + [data] tilr_JSAdditiv/Organ 6.wav + [data] tilr_JSAdditiv/Organ 7.wav + [data] tilr_JSAdditiv/Saw 1.wav + [data] tilr_JSAdditiv/Saw 2.wav + [data] tilr_JSAdditiv/Saw 3.wav + [data] tilr_JSAdditiv/Sine 1.wav + [data] tilr_JSAdditiv/Sine 2.wav + [data] tilr_JSAdditiv/Sine 3.wav + [data] tilr_JSAdditiv/Sine 4.wav + [data] tilr_JSAdditiv/Sine 5.wav + [data] tilr_JSAdditiv/Spectral.wav + [data] tilr_JSAdditiv/Square 1.wav + [data] tilr_JSAdditiv/Square 2.wav + [data] tilr_JSAdditiv/Stairs.wav + [data] tilr_JSAdditiv/Triangle.wav +screenshot: https://raw.githubusercontent.com/tiagolr/JSAdditiv/master/doc/ss.png +about: + # JSAdditiv + + Polyphonic additive synthesizer + + Features: + * Wavetable oscillator that reads single cycle waveforms from disk + * Additive synthesis engine that allows to modify the wave + * Wave editors for harmonics amplitude and phase + * 8 voices unison engine + * Pitch, amplitude and filter adsr + +desc: JSAdditiv +tags: synth, instrument + +slider1:/tilr_JSAdditiv:Sine 1.wav:Wave +slider2:vol=80<0, 100, .01>-Volume +slider4:_uni_voices=1<1, 8, 1>-Unison voices +slider5:uni_detune=15<0, 100, 0.1>-Unison detune +slider6:_uni_pan=50<0, 100, 0.1>-Unison panning + +slider7:osc_vel=100<0,100,0.1>-Velocity +slider8:osc_att=1<1, 10000, 1:log>-Attack +slider9:osc_dec=1<1, 10000, 1:log>-Decay +slider10:_osc_sus=100<0, 100, .1>-Sustain Db +slider11:osc_rel=500<1, 10000, 1:log>-Release + +slider13:flt_shape=0<0,3,1{Off,Low Pass,Band Pass,High Pass}>-Filter shape +slider14:flt_freq=22000<20, 22000, 1:log>-Filter frequency +slider15:flt_q=0.70<0.01, 40, 0.01:log>-Filter Q +slider16:flt_amt=0<-100, 100, 0.01>-Filter ASDR amt. +slider17:flt_att=1<1, 5000, 1:log>-Filter Attack +slider18:flt_dec=500<1, 5000, 1:log>-Filter Decay +slider19:_flt_sus=0<0, 100, 0.1>-Filter Sustain +slider20:flt_rel=500<1, 5000, 1:log>-Filter Release + +slider22:_pitch_amt=0<-24, 24, 1>-Pitch ASDR amt. +slider23:pitch_att=1<1, 5000, 1:log>-Pitch Attack +slider24:pitch_dec=500<1, 5000, 1:log>-Pitch Decay +slider25:_pitch_sus=0<0, 100, 0.1>-Pitch Sustain +slider26:pitch_rel=500<1, 5000, 1:log>-Pitch Release + +import add.wavetable.jsfx-inc +import add.array.jsfx-inc +import add.adsr.jsfx-inc +import add.zdf_filter.jsfx-inc +import add.gfxlib.jsfx-inc +import add.mouselib.jsfx-inc +import add.fft_real_synth.jsfx-inc + +options:gfx_hz=60 no_meter + +@init +wavebuf = 100000; +wavechn = 0; +wavelen = 0; +wavesrate = 0; +osc_free = 1; +env_panel = 0; +is_mono = 0; +edit_mode = 0; +phase_mode = 0; +harm_index = 0; // index of harmonics displayed, 0 displays first 34 harmonics + +poly.array_init(0, 128, 4+8); // [0note, 1velocity, 2freq, 3phase, 4..12 phase_unison] +envelope = 2000; // 129 * 7 buffer (where 129 buffer is the mono envelope) +pitch_env = 3000; // 128 * 7 buffer +filter_arr_l = 4000; // 128 * 11 buffer for filters one per key left channel +filter_arr_r = 6000; // 128 * 11 buffer for filters one per key right channel +filter_env = 8000; // 128 * 7 buffer for filter adsr +last_phase = 10000; // 9 buffer for copy pasting note osc phases + +function db2gain (db) local (val) ( + val = 10^(db / 20); + val <= 0.001 ? 0 : val; +); +function normalize_vol_slider(val) ( val * 60 / 100 - 60 ); +function note2freq(n) ( 440 * pow(2, (n - 69) / 12); ); + +function normalize_wave(buf, len) +local (_min, _max, i) +( + _min = 1; + _max = -1; + loop(i = 0; len, + _min = min(_min, buf[i]); + _max = max(_max, buf[i]); + i += 1; + ); + loop(i = 0; len, + buf[i] = 2 * ((buf[i] - _min) / (_max - _min)) -1; + i += 1; + ); +); + +/* + * Normalizes wave only if it passes max amplitude + */ +function semi_normalize_wave(buf, len) +local(_min, _max, i) +( + _min = 1; + _max = -1; + loop(i = 0; len, + _min = min(_min, buf[i]); + _max = max(_max, buf[i]); + i += 1; + ); + _min < -1 || _max > 1 ? ( + loop(i = 0; len, + buf[i] = 2 * ((buf[i] - _min) / (_max - _min)) -1; + i += 1; + ); + ) +); + +function stereo_to_mono_wave(buf, len) ( + loop(i = 0; (len / 2) | 0, + buf[i] = (buf[i*2] + buf[i*2+1]) * 0.5; + i += 1; + ); +); + +/* + * Convert wave into harmonics using FFT + */ +function synthesize(buf, len) +local (window) +( + window = 512 >= len ? 512 + : 1024 >= len ? 1024 + : 2048 >= len ? 2048 + : 4096; + four.four_init(buf, window); + four.four_fft(); +); + +/* + * Generate wavetable from FFT coeficients + */ +function resynthesize() ( + four.four_setf(1); + four.four_update(); + four.four_ifft(); + semi_normalize_wave(wavebuf, wavelen); // correct signal strenght after summing frequencies +); + +function on_file_change () ( + filehandle=file_open(slider1); + filehandle > 0 ? ( + file_riff(filehandle, wavechn, wavesrate); + wavechn ? ( + wavelen = file_avail(filehandle); + file_mem(filehandle,wavebuf,wavelen); + ); + file_close(filehandle); + wavechn == 2 ? ( + stereo_to_mono_wave(wavebuf, wavelen); + wavelen = (wavelen / 2) | 0; + ); + normalize_wave(wavebuf, wavelen); + osc.wave_init(wavebuf, wavelen); + synthesize(wavebuf, wavelen); + resynthesize(); + harm_index = 0; + ); +); + +// copy filter coeficients from buffer1 to buffer2 +function filter_copy_coefs(buf1, buf2) ( + buf2[2] = buf1[2]; + buf2[3] = buf1[3]; + buf2[4] = buf1[4]; +); + +// wraps filter function using buffers +function filter_setf(buf, freq, q) ( + filter.zdf_setf(freq, q); + buf[2] = filter.g; + buf[3] = filter.r2; + buf[4] = filter.h; +); + +// wraps filter function using buffers +function filter_lp(buf, sample) local(lp) ( + filter.s1 = buf[0]; + filter.s2 = buf[1]; + filter.g = buf[2]; + filter.r2 = buf[3]; + filter.h = buf[4]; + lp = filter.zdf_svf_lp(sample); + buf[0] = filter.s1; + buf[1] = filter.s2; + lp; +); + +// wraps filter function using buffers +function filter_bp(buf, sample) local(bp) ( + filter.s1 = buf[0]; + filter.s2 = buf[1]; + filter.g = buf[2]; + filter.r2 = buf[3]; + filter.h = buf[4]; + bp = filter.zdf_svf_bp(sample); + buf[0] = filter.s1; + buf[1] = filter.s2; + bp; +); + +// wraps filter function using buffers +function filter_hp(buf, sample) local(hp) ( + filter.s1 = buf[0]; + filter.s2 = buf[1]; + filter.g = buf[2]; + filter.r2 = buf[3]; + filter.h = buf[4]; + hp = filter.zdf_svf_hp(sample); + buf[0] = filter.s1; + buf[1] = filter.s2; + hp; +); + +function on_slider() ( + gain = db2gain(normalize_vol_slider(vol)); + lfile != slider1 ? on_file_change(); + uni_pan = _uni_pan / 100; + osc_sus = normalize_vol_slider(_osc_sus); + flt_sus = _flt_sus / 100; + uni_voices = _uni_voices | 0; + odd_voices = uni_voices & 1; + pitch_amt = _pitch_amt | 0; + pitch_sus = _pitch_sus / 100; + detune_amt = uni_detune / 100 * 3 / srate; // 3 hz max detune +); + +@serialize +file_var(0, osc_free); +file_mem(0, wavebuf, wavelen + four.size * 4); + +@slider + +on_slider(); + +@block + +// remove notes that finished adsr +ptr = poly.array_first(); +while (ptr >= 0) ( + env = envelope + ptr[0] * 7; + env[5] == 0 ? ( + poly.array_remove(ptr); + ptr = poly.array_first(); + ) : ( + ptr = poly.array_next(ptr); + ); +); + +while (midirecv(offset, msg1, note, vel)) ( + event = msg1 & 0xF0; + channel = msg1 & 0x0F; + + // Note on + event == 0x90 && vel ? ( + + // remove note if it is already playing + // copy its phase into new note to avoid clicking + note_repeat = 0; + ptr = poly.array_find(note); + ptr >= 0 ? ( + note_repeat = 1; + last_phase[0] = ptr[3]; + loop(i = 1; 8, + last_phase[i] = ptr[3+i]; + i += 1; + ); + poly.array_remove(ptr); + ); + + // if polyphony is full + poly.size == 127 ? ( + poly.array_remove(poly.array_first()); + ); + + ptr = poly.array_add(); + ptr[0] = note; + ptr[1] = vel / 127; + ptr[2] = note2freq(note) / srate; + ptr[3] = note_repeat ? last_phase[0] : osc_free ? rand(2) - 1 : 0; // osc start phase + loop(i = 4; 8, + ptr[i] = note_repeat ? last_phase[i-3] : osc_free ? rand(2) - 1 : 0; // unison start phase + i += 1; + ); + + env = envelope + note * 7; + adsr_seta(osc_att * 0.001, env); + adsr_setd(osc_dec * 0.001, env); + adsr_sets(exp(log(10)/20 * osc_sus), env); + adsr_setr(osc_rel * 0.001, env); + adsr_a(min(vel / 127 + (1 - osc_vel / 100), 1), env); + + env = pitch_env + note * 7; + adsr_seta(pitch_att * 0.001, env); + adsr_setd(pitch_dec * 0.001, env); + adsr_sets(pitch_sus, env); + adsr_setr(pitch_rel * 0.001, env); + adsr_a(1, env); + + env = filter_env + note * 7; + adsr_seta(flt_att * 0.001, env); + adsr_setd(flt_dec * 0.001, env); + adsr_sets(flt_sus, env); + adsr_setr(flt_rel * 0.001, env); + adsr_a(1, env); + ); + + // Note off + event == 0x80 || (event == 0x90 && !vel) ? ( + ptr = poly.array_find(note); + ptr >= 0 ? ( + adsr_r(envelope + note * 7); + adsr_r(pitch_env + note * 7); + adsr_r(filter_env + note * 7); + ); + ); + + // All notes off + event == 0xB0 && note == 123 ? ( + poly.array_clear(); + ); + + midisend(offset, msg1, note, vel); +); + +@sample + +ptr = poly.array_first(); +while(ptr >= 0) ( // for each note/voice + outl = 0; + outr = 0; + envbuf = envelope + ptr[0] * 7; + env_state = adsr_process(envbuf); + osc_pitch_det = 1; + pitch_amt ? ( + pitchbuf = pitch_env + ptr[0] * 7; + adsr_process(pitchbuf); + osc_pitch_det = pow(2, pitchbuf[0] * pitch_amt / 12); + ); + loop(i = 0; uni_voices, // for each unison voice + imod2 = i & 1; + pos = i == 0 && odd_voices ? 0 : imod2 ? -1 : 1; // center, left or right position + detune = !odd_voices + ? pos * detune_amt * (i + 1 - imod2) / uni_voices + : pos * detune_amt * (i + imod2) / uni_voices; + osc.wave_setdt(ptr[2] * osc_pitch_det + detune); + osc.wave_sync(ptr[i + 3]); + wave = osc.wave_spline3(); + pan = !odd_voices + ? pos * uni_pan * (i + 1 - imod2) / uni_voices + : pos * uni_pan * (i + imod2) / uni_voices; + outl += wave * (1 + pan) * envbuf[]; + outr += wave * (1 - pan) * envbuf[]; + ptr[i + 3] = osc.t; // update osc phase + i += 1; + ); + + // Apply filter + flt_shape != 0 ? ( + filterbuf = filter_env + ptr[0] * 7; // envelope buffer + adsr_process(filterbuf); + filterbuf_l = filter_arr_l + ptr[0] * 11; // filter buffer left + filterbuf_r = filter_arr_r + ptr[0] * 11; // filter buffer right + multiplier = pow(20000/flt_freq, filterbuf[0] * flt_amt / 100); + + filter_setf(filterbuf_l, flt_freq * multiplier, flt_q); + filter_copy_coefs(filterbuf_l, filterbuf_r); + + flt_shape == 1 ? ( + outl = filter_lp(filterbuf_l, outl); + outr = filter_lp(filterbuf_r, outr); + ) : flt_shape == 2 ? ( + outl = filter_bp(filterbuf_l, outl); + outr = filter_bp(filterbuf_r, outr); + ) : ( + outl = filter_hp(filterbuf_l, outl); + outr = filter_hp(filterbuf_r, outr); + ); + ); + + spl0 += outl * gain; + spl1 += outr * gain; + + ptr = poly.array_next(ptr); +); + +spl0 += outl * gain; +spl1 += outr * gain; + +@gfx 520 270 + +gfx_clear = COLOR_BG; +mouse.update_mouse_state(); + +draw_wave(20, 20, 120, 60, wavebuf, wavelen); + +//draw_grey_button(520 - 60, 2, 40, is_mono ? "Mono" : "Poly"); +//mouse.left_click && mouse_in_rect(520 - 60, 0, 40, 10 + 2) ? ( +// is_mono = !is_mono; +// on_slider(); +//); + +draw_button(160-2, 10-2, 50, "Harm", !phase_mode); +draw_button(160-2+50, 10-2, 50, "Phase", phase_mode); +mouse.left_click && mouse_in_rect(160-2, 10-2, 50, 12) ? phase_mode = 0; +mouse.left_click && mouse_in_rect(160-2+50, 10-2, 50, 12) ? phase_mode = 1; + +set_color(0xFFFFFFF); +harm_index > 0 ? ( + gfx_triangle(275, 10, 285, 5, 285, 15); + mouse.left_click && mouse_in_rect(275, 5, 10, 10) ? ( + harm_index -= 1; + ); +); +harm_index + 1< ceil(four.size / 34) ? ( + gfx_triangle(305, 10, 295, 5, 295, 15); + mouse.left_click && mouse_in_rect(295, 5, 10, 10) ? ( + harm_index += 1; + ); +); +gfx_x = 320; gfx_y = 10-2; +gfx_drawstr(sprintf(#, "%i / %i", harm_index + 1, + ceil(four.size / 34), 1, gfx_x+10, gfx_y + 10)); + +mouse.left_click && mouse_in_rect(160, 20, 340, 60) ? ( + edit_mode = 1; +); +edit_mode && !mouse.left ? ( + edit_mode = 0; + resynthesize(); +); +max_harm = harm_index + 1 >= ceil(four.size / 34) ? (harm_index + 1) * 34 - four.size - 1 : 34; +draw_harmonics(160, 20, 340, 60, four.coef+2 +harm_index*34*2, max_harm); + +draw_knob(20, 110, 2, "Vol", 80, 0, 100, 0, 0, sprintf(#, "%i%%", slider(2))); + +set_color(0x666666); +gfx_x = 160; gfx_y = 20+70; +gfx_drawstr("UNISON"); + +draw_button(220, 20+70, 40, "Free", osc_free); +mouse.left_click && mouse_in_rect(220, 20 - 2+70, 40, 10+2) ? ( + osc_free = !osc_free; +); + +draw_knob(160, 40+70, 4, "Voices", 1, 1, 8, 0, 0, sprintf(#, "%i", slider(4))); +draw_knob(220, 40+70, 5, "Det", 15, 0, 100, 0, 0, sprintf(#, "%i%%", slider(5))); +draw_knob(280, 40+70, 6, "Pan", 50, 0, 100, 0, 0, sprintf(#, "%i%%", slider(6))); + +set_color(0x666666); +gfx_x = 160; gfx_y = 110 + 70; +gfx_drawstr("ADSR"); + +draw_button(200, 110+70, 60, "Env", env_panel == 0); +mouse.left_click && mouse_in_rect(200, 110-2+70, 60, 14) ? env_panel = 0; +draw_button(260, 110+70, 60, "Pitch", env_panel == 1); +mouse.left_click && mouse_in_rect(260, 110-2+70, 60, 14) ? env_panel = 1; +draw_button(320, 110+70, 60, "Filter", env_panel == 2); +mouse.left_click && mouse_in_rect(320, 110-2+70, 60, 14) ? env_panel = 2; + +env_panel == 0 ? ( + draw_knob(160, 130+70, 7, "Vel", 0, 0, 100, 0, 0, sprintf(#, "%i%%", slider(7))); + draw_knob(220, 130+70, 8, "Att", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(8))); + draw_knob(280, 130+70, 9, "Dec", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(9))); + draw_knob(340, 130+70, 10, "Sus", 100, 0, 100, 0, 0, sprintf(#, "%i", slider(10))); + draw_knob(400, 130+70, 11, "Rel", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(11))); +); + +env_panel == 1 ? ( + draw_knob(160, 130+70, 22, "Amt", 0, -24, 24, 0, 1, sprintf(#, "%i", slider(22))); + draw_knob(220, 130+70, 23, "Att", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(23))); + draw_knob(280, 130+70, 24, "Dec", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(24))); + draw_knob(340, 130+70, 25, "Sus", 100, 0, 100, 0, 0, sprintf(#, "%i", slider(25))); + draw_knob(400, 130+70, 26, "Rel", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(26))); +); + +env_panel == 2 ? ( + draw_knob(160, 130+70, 16, "Amt", 0, -100, 100, 0, 1, sprintf(#, "%i%%", slider(16))); + draw_knob(220, 130+70, 17, "Att", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(17))); + draw_knob(280, 130+70, 18, "Dec", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(18))); + draw_knob(340, 130+70, 19, "Sus", 100, 0, 100, 0, 0, sprintf(#, "%i", slider(19))); + draw_knob(400, 130+70, 20, "Rel", 1, 1, 10000, 1, 0, sprintf(#, "%i ms", slider(20))); +); + +set_color(0x666666); +gfx_x = 400; gfx_y = 20+70; +gfx_drawstr("FILTER"); + +shape_name = flt_shape == 0 ? "Off" : flt_shape == 1 ? "LP" : flt_shape == 2 ? "BP" : "HP"; +draw_button(520 - 60, 20+70, 40, shape_name, 0); +mouse.left_click && mouse_in_rect(520-60, 20 - 2+70, 40, 10 + 2) ? ( + gfx_x = 520 - 60; gfx_y = 30+70; + choice = gfx_showmenu("Off|LP|BP|HP"); + choice > 0 ? slider(13) = choice - 1; + on_slider(); +); + +draw_knob(400, 40+70, 14, "Freq", 22000, 20, 22000, 1, 0, sprintf(#, "%i Hz", slider(14))); +draw_knob(460, 40+70, 15, "Q", 0.70, 0.01, 40, 1, 0, sprintf(#, "%.2f", slider(15))); + +function on_knob_move (nslider, slider_min, slider_max, slider_is_log, factor) ( + factor *= mouse.control ? 0.05 : 1; + slider_is_log ? ( + slider(nslider) = slider(nslider) * pow(100, -factor * 0.01); + ) : ( + inc = (slider_max - slider_min) / 100 * -factor; + slider(nslider) += inc; + ); + + slider(nslider) > slider_max ? slider(nslider) = slider_max; + slider(nslider) < slider_min ? slider(nslider) = slider_min; + on_slider(); + sliderchange(slider(nslider)); + slider_automate(slider(nslider)); +); + +wheelknob_nslider ? ( + on_knob_move(wheelknob_nslider, wheelknob_min, wheelknob_max, wheelknob_is_log, mouse.wheel * -7); +); + +selknob_nslider && mouse.left && mouse.dy != 0 ? ( + on_knob_move(selknob_nslider, selknob_min, selknob_max, selknob_is_log, mouse.dy); +); + +doubleclk_nslider ? ( + on_slider(); + sliderchange(slider(doubleclk_nslider)); + slider_automate(slider(doubleclk_nslider)); + doubleclk_nslider = 0; +); + +gfx_setcursor(selknob_nslider ? 32511 : 0); +!mouse.wheel ? wheelknob_nslider = 0; +!mouse.left ? selknob_nslider = 0; + diff --git a/Synth/tilr_JSAdditiv/Complex 1.wav b/Synth/tilr_JSAdditiv/Complex 1.wav new file mode 100644 index 0000000000000000000000000000000000000000..a69e045145f9cf8ba289ca6362f8dddbfe3f210d GIT binary patch literal 1344 zcmY*ZX;4!K5Pk_DL=mGPhiD;ypa^m!NVxLyUgQ))Ac4{rrf2{`tVd}rs9e@_+8MOq zQK^=f0|E&N@Dd<_q=15`2o#B+77$QWu!-dmFn#Szr{nJI&VKvN&U`<1K6)4o`xyWL zGc*C(pMBJp2mk;MOR4|>&aiL*9v}i~sYg>q`T#bQfeOF|hvL%k4tnqO2J}@1@>T7I zgGL&HE4qu>9J9CkGKttSw?p}l5mQd%<@aLOwuK3K3zz5{d^_Gb|0I4ft|!Y z$X2pF(go=*8G;PwOim{0K-mF8R&LffR>b`SEGqpJL!02m60q7?HLQFVnKi?_%=Bco zCqyRn#Dnodh8g3lxZCuN^yJvg4hWC8Dj8E_6< z0++)aSPVD83Rn)KumJuUJ`W$pI1iWr4?{fY5abEXQW0uArmqqN!C}fl%2H@yC_XeV zWHvZCxFg6Zs4(ym*^!(RAld!a-^ripSG0@o+vW4zd(q3t%hYp|hqb%8o5@bRt7gZD zbNhDwwsTJDj^I{n2aR1P>8fp(4fzw}EhE-~O?g&PmNpjJjd#p$n&xjvULQ!bTesF^ z#aO#$X7wy#!N|ynWJp<+ZE#C}RL@1P7(b2s0yhDi0Nk*Dd#3wI=c8-=uy&j=wqN?~ zdsV(Nx9w=_*NQxONy`t-6-^RZxAZxRL+u)A4LS8!B<*5N9jT5XIxAGvYHOTpJ`-H5 zMynq4HM|SFn^ogN`-TeCT)Kp+(Ju53+J}yzi>R5@Tgs9aNNc5|QVUs}?2=3^BQ<3= z$(xAHY0XW|W-U1_cUzp~#qv>kprTx%RYbJjYMpD{(^l3t+UBIpP$J4)_vT8|Lt~64*x7|>jla+~?qUpLI$pguhq(f37@s_B?--u1cygI+SPEm|V zEld$k)*h&RTXU>tSx_V}7L-*xR7m= zI;uMfce1($x$m#-`XFhDr*<1gM}nVrK8qaf9iu;gI1x3~@xp(a z`-=GH$jpOT`#*CuV(sg9o99C36BqU`?t34<^Go?zYp literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Complex 2.wav b/Synth/tilr_JSAdditiv/Complex 2.wav new file mode 100644 index 0000000000000000000000000000000000000000..5cf77018acf315f1b99c5a075e4b7a0261bec104 GIT binary patch literal 1344 zcmYjRXH-)M7=1}_7R#_wl-;@q=y8A|D9X?%ITVpmLc+B`>Wn$;)CUB*cIa zz%Ud-32Fgl6hRG6hC-1-K|w*L$S9OT8-IA}chCL4bH97;-|KzS-Q8gs0QkB1x`k1w z)`kE8pmcCm1Ax#U1)u>#03SjP(dYrd?Pb8%?d#TC-2Gm??SpOFUbVfiW0~_4mnv6= z$4czQ(-NH8tK0j`*&Y7Df!gz%LOm`m66_-eN#9Vklr@nbDL+w4$yCx^BAFn-+lABy zX?{%d&+!}cI~ZUa_;b)yaCm4l-kg9Xo+UX_79!=*U&Wq_OQcHUX*4_fZ}cr-1egR~ z0nGhc74%HGE&Y_;cf`)g@i$Ab;k|CS#t7OQ^Gey=E&Et3o< z;}SQDLIq2_QN#ifajm(XoO}+GbC$DbEZ=ERny=9ERTnY3SK zBd?R!%iZJ==~&rh8bNY1wKo}^^cUdy%{&F~E^iyZlV2lvDnuptB;HRtoidav zk+@4S>Gg8D!ZcHQ^<`FbHb*(DJe(7mQi^St!0$bCJaB62FX}(8sM6)d`cI9Im@b)NEH-ZFG0Mj5oa*bB zbl{$J+riBZ3n76Bi#+VHrHf~!dZ5{WocZ83Ixiy_sds_am z!)M5s5Ks}=71R}69&+L0IGzwTLXZ(@q*RJF>hBmm>f`t$^g!@ChJvAGR4_=40nij& zqKV>b;>u$(qUtE2FAZ3?K}&7IK7QpfczkWXRgYvSiId8VCzLX6|Q782#XOkU&qTmBinm zCd8eIHHjIF>WJ*7JRygWoQQ*$9$r`;0D2xj%-uX;kUtN-E9{7Vgsbbh-Krih(9503 z6w6!FuBFZ;JxoB0RDw)?FE5m5!ka+e=rD|YL}qjxL|!6w$aN$gNko#7Y@`;ML>zcm zcm{kG-$!62)Dta=^b(Dd%#*ECY*MYnHjrd*!17n~BNtQ*!e(1wF-$ znwARJ8e)BSgR=2M!}fa_burc2%Glz2Jd1hn0>dP7}@vNBdl6jtw3ka^2!y=G1GebY0`|ZO z+OZUewyh59<^XuywKuN~-85UbqcTDxEDkT2$Ti5mlQAGql~tylld#2B;zMGsxL#r; zEtes3u0ongR9Y5*rPaTxYxXnB^NZ_;{$GS#B;rkVQq%8=(PiOJj;;!I)a@3L`+vt~dfos4 literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Complex 3.wav b/Synth/tilr_JSAdditiv/Complex 3.wav new file mode 100644 index 0000000000000000000000000000000000000000..6faa068968783b39e80d380cb9c9b61f2554e2cf GIT binary patch literal 1344 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 A1ONa4 literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Organ 1.wav b/Synth/tilr_JSAdditiv/Organ 1.wav new file mode 100644 index 0000000000000000000000000000000000000000..557809fb9f9b9d62589a682ba42479b457d29a1b GIT binary patch literal 1344 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 literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Organ 4.wav b/Synth/tilr_JSAdditiv/Organ 4.wav new file mode 100644 index 0000000000000000000000000000000000000000..1599b12426c615595dff6d5fc471d8ef6228a172 GIT binary patch literal 1344 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 wYyIp? literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Organ 6.wav b/Synth/tilr_JSAdditiv/Organ 6.wav new file mode 100644 index 0000000000000000000000000000000000000000..b3c0794f92e77de4f6f343c5042936c39a2fcdba GIT binary patch literal 1344 zcmYk+e^AnA90%|(h$Sfq1dGlHGg5PF&0*>UZYL?`-894>sYM>>P$&NAmYyI^+O)HF zIh}6V$}+C2bHiUW%6^u49c9a{wtj5lwxzW#LXZFPj=SJ zx7PD6SuO?3tgYDo7c;w|<0NwMC($ptctMR|D&_OoLZf=g$H?u!%xJ%z*dxd}Gw}o*pvhZkQRB_ly zK!T?`GRmBx%~s8JDB5=3<#%%WNh8;F_smIaFsOAS;I-?VWGt%ZUh}rP!ipDV!qU6v z^J;&PpXjZh(y#rF*ZS54zNTNNk*S@O!#JZ2J2y0jAAowjdO}sH8q3t0iM7#=tia@B zU%Hy?)yAZ8c6a}8xZ3b@CMi;S=WI-k^j1ulU?kqq0kxo?5~PGll+hkwcsA4@! z>Rppqxwm@h@;41uMUdJSi37*_sBFq7guqstyk=gdA;YS$-vda z5Fd`0!xcWWi_41E0asGsis-I!xLOGKlyxJ)JMACgt=uQTRb7mV0RyfU?OTDXAmA$9 zK7o4-Tr~n$Jdqu^x>?Ydj*cs4h`qzHD|XD%d85>nw4w#BJ|=enSAWs|4H=`l`ScO= zz?G-=(Ez6htt?O-85Pg75w)AHQu2Q)B8qcP4f4tCGTKQW%{y1==n@YXKXuSuruChx zZ#P{D+aot!Wo){d^cxDYGJc^iQw`jNOSDg~zYHxGTrYp2L-rQ!#hiku^-!3<3vktBG)!34T;+C$s|Uc94&Uc+B|0j1xH{(P23%!nzf$Qs+S`c0 zRb2nBksG=;bMhJ-YT2N^M$L(VD@C(Qow#D6EE~AWt*w$L_sXVpYYP0RZ++kj{Tj`S zdLOtl*-&$1W0(QB!xdk%G_?v`bs#UibCo!L6u8=6%RIOEo~z7}SVI@ojDAKaCQPH| z?S~9cMqRt#3qdpLI-az1R_WkFxDSa4FkOvFdDLy|yuK2kjgJ$yZf zKI1=aLFPgoMCn8*MZZL{L-#?fKj%EqIgK`BGh{H{E#xbgDjh0iDuydoE!Ho`G7L6& zIm|reKbbG1 zQvOq*R6tb6QZi7FO!-HVLYzEDHsdd}DflD%9`73d7ef}$6`>ZF7@-?@ANC{1D6lPQ zGORX6Is-kzK5ak$K7>6EJHR)_GfXdvDzzo^AaNX17^M_@5zP*d47Lgy38)7)2C4+< z0^$IJ{?q%{__6hB@;&cT?9S=+=iBDI=AP%L>A>vx@EG%QP^0n?H>%8Zv8|WZ?}G6>^IP=K z^r7_1^KkO1@TTsE?2GEi=!oaZ=Dg-*=ep>>>PYQ3@LThN_G$U$`?>wV{>%Q>{m=Wq z_{8)A@TKbe+|qt_4@dW`}_P* z{eJwT`r-Fi^f~av>lWv8;`!am*#_3E(#p?F%_hq{$#BP=#k0eL!2rK0yz97JwT7{i zt)HpXrFEi}pMjl7oEDt*ob8_dp#rAZs#dT!wmZBV!jZ>W%s0^g(uvf<)UeY}(bUaY z$sELBzG%3DvgoYfr=6omp!%J>oQ#}YodBO!p(~_Br){cmtsk&dvhuU_v{khlwQsay zvoEs!u>7xOuUfAIuye7%v*@RswB>vinz?RM|Q@UrnD^5F6F@K^5!?KA4^=49i{-&x!>*_GCz z)MnET(}mO4)Zf;P*{s|f;Hcvo=S%7w?O^W|@hbA6@|yA@@@Mgm@Wt;v?;-Dq@0{>x z^5^sz_~QE?0IUQ=3BnEJ5qcF!7(yB>8zdVC8UPqA6}A!D4ciI-1%?8;|IhvT{8jz= z{_O#CZE$Qn00000000000001!SO5S#00000000000000000001000000000000000 z00000000120ssI2000000001CV`*d<0000`ghB*7004jh000040000400aO4(1}Ln CynopM literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Saw 1.wav b/Synth/tilr_JSAdditiv/Saw 1.wav new file mode 100644 index 0000000000000000000000000000000000000000..3ad7e36bdb67678f639b3151f836f214c54c8367 GIT binary patch literal 2092 zcmaLVS2P=p0>JU;-lfAHF%uDci%_l7sjU`4gcwPT7!f1pckaVI_nvbf?#oB3h!7bf zL&J3;+-j5PlnjCjzwr0D#snfsFvb zzYto0V*qVHBASQ>{A#rSdi+<3)*k@CKMqf5{<;6}o%7rO+5GRij!H*WrYKYBFB{9M z7R?qYa~2ZnjE$HsvYlcHp%YDGEk9gGc;7sS`9u66!6*12d@$hsz}vt#?*@YVgZn@B z{c*PMe(z_&A0gc#-NT*3ouT|t{z&_8VZ5-a)caYGqPF47O`U+XIOEp*#>lj zgc;AAr~hU}$IztI-@w%5x@8K!24BM_E35xM-O8}azbi*8b*sXw{;v92rCS|dT~<9> ztxFCkmyt)wdNtuSWi_KUx|9e?Ic1chTN_bZUOQH+TNhqeUN=^!M~$GCQ^%-!^%3>Y z>&NQ#Xc4q>+Bi+09zidskJI%T2u3+$oT1M|FrPEWnfeWghUX3A4F)U(>p5$jWzZPe z_@Z&VQJ)>je!-q#>o-LdsKTxyRhA;Bf8^dhp@wlAI*QspX3{LMt8pK zoa{8}LUz6En(Q*_Ms`iq&*<0B=)oa{`?5pY%^%=iH zzN&mBdS%j&?62$>^_vXD3{(wF4Vb)+d0q8d^xEW2%$uq=qBo{*QE#i>ir$*ML%pkh zCwgb{9`(NZ{q%d&52z2-AErN;enfq&{y6>7bPzR29uyCnenNdBe-eK(`;7Wr{aO6k z>`Uwy@)z+Jv!U1_^3cqX+1J>wW-TT0k~+zp#BwfSjyg9tXE~oRPo1Biw_HeAs9Ts{uv$!9tXrI4w2~(N zT5(=#B};8N1S5&>n7&u_xOD?c??t`?7uT z0q%ftAUgnSa2lpYrU4(~4w;AYL)#b z~c6Sx!f6Vel)lem+Nld_YbQ@B%%Q?gUw)40=&)3VdxGq^L%GqN+_v$(U& zv+}dxbGUQNbMkYx=abJf&dbl+UP!*cydb||dolSU^WxG)$fe{<%uDi1kjp8TnV034 zA=)Y04cbfE5Svp=S zx-8vgT|2#0y+*xdJv;qWeU`pLA8L?lz%o!6Kn>FjS%%AoP@^=XMk9q0)Huz!(O6+@ zZ<1!xXreHIn&M5_rV3N28QzR-wqgba;(=`73eeshZ_YMfF}JtCTQpg$SlC=H%p@ z>CACfIXk&zx^P@nF3zr*t{hjDtFv2{TZ`M8o3neCdyBiu-Pt3{qs2qz;o?c~Z1Gfi zI(rekxLztRXK#Wx*L&UD#V5;$>$C3T0wchmO`?vb5{atTl-)Oy|zTp~>9ncz}4sg3kyxDqFeben0@mAX{^)1&xVqja~Mxfhm z;_bHE8@Jtpa)R1|HiF!OiNU5SQp@j{VMov2-u zcPuHkGj=!DJB}3B6}KDbjV7VH(0gd_cv5^<{9e3ILP0`j!d`+;VnJe8;$EUpQbAHz z(tZ*QQ-JBh>|tQoLTndy4-3N;;<|DBI9PIFa(D86GAyMqr8{Lm1(sTrDoEW=g{2jx z3DOSIVE9730Dpk@O)p9pq#vaFW)x-gWE^DpW)@`%GBug-tfDMImL>~MC?*I98Uj4K zIJ+lXlMN>p6MKjnqF+vNPEU>|$1k@ew>MXl3(qUb>&-jNgXfpz_vRnw`;kgXy`)2u zUqMMhU%_F4UtvjMU*S=qe^E(MZ_!bae{pGXZ}CyFe@SUcUkRYpzqGWpuk@(&e*pSI Ad;kCd literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Saw 2.wav b/Synth/tilr_JSAdditiv/Saw 2.wav new file mode 100644 index 0000000000000000000000000000000000000000..883e6376ced183b020675f15c8e4b623d344f41b GIT binary patch literal 1344 zcmYk6c~DbV6viJKRG{pl$c`Y$z6HX*W@8JGyuA0cT8mm|q=1Y#)LI-vNCHY^3n454 zWDx`cf+(A!B5rJkSj1XXB4DIqrCOn&!1QA4jQ2NZ&b{|L_sn)0# zDB+ly76d^`0HOqfGBG7+9i#;vj6D|Hf`cF$Bvxu!*S5YAcSxDC!F=Pw#@bCODt@ZU zYE5cKzHwBSs`E9dnsUuDExh)uwphnaSD|}yv#nmQ9=v5`OTktj{XzZjw&4tf24;qX zh76-=qa@=kCesn^9l3R?Herk7J8OEOZ6SOI}EHUtSqf1*3LUic3Rog z*f`i0*c#fE+o{^~?R6Yd9h4js9N#$Za(deQ|yXexrWY{%8H={?vfd zfY$-hf#(C~1NQ_q1g!?qf;)m$LUx5nLOzAW5X*`0i2kHp(hHIsIi7rjtWAlfv{B|L z0n`)JVXAQ`7TOp(6-o#@8rBd7g%iS$hu;fd4G)Ys5pgBrZG=nYiO8pC{|Q`)O3_dG&PzX-4p#P+AJnCh8xoyGZLdt$J6)GMf5)UG+mQHU?ejN7`==c zh6d~b?}0O65!?$aU=5}Rlgd2G6f&EbBg`45Dq@Kck$p%SB0?Gw8KOWI5LMI#C8E30 z1oRXtL@%IMQ7QTYT|m_^Rm=o)#3&dE+l9peP67mgTC4`^1hiwr*a$X+NkNtaCjO7L z-2^rXV)(Nr1(j>{H2@)&2fCz!Za-oehJx-+0AoxI`+!aYWN0T^4}6?L zod-T;NDuJLMiLP+f=4WnMKH~EFjYF3jtZvKWKP46;Ch$`C&Dz?6khKmT2c&ZwW(L# z7t_TC;*=7rqF!N&z%Z{d`+Gj|Oj718UN~2qqn_@>=A_}$3Rz*S9W15K=&-z4yIF;- zAr>wzDvg&WO*3L2U^lZD*}>^K=`-mB&MD3_jy0FZ9pXCil6hl1*NoJR@eKD&Vdi9} z@9EOhA5SA^E}hw&m6$b@WzXmHU-C(3JI?B4|CBwH9h6g%vyyWpSDG7;SC*%hezW$wgB|jN-v!-x5iQMX8`vS^SH5R-9a>D8tGh zlm}JxS9qUmJ?Bx`Q0ZJ%SGBeJT(w?}xJLJU$$9NsQLSd3u+H>C{srUu>U#Z#+6ITl zszy6WyCkrwvnjZFuo=B5zZlmt*%IHn*vh`7-B#9Sak=%f-<5$YNc&WKV#i8HR;PYf zZI@GbUw7oyr&r^yY4w!$*j?|wPPs9Dp=;24Wv9dAR zW5r|6Z>CRVPm+FD8Lu1P`*itf@iVXI@1EyPxXB;OkH5fAj!hn)GM~OZ&3b90kSS7M zSAXr2Io(G+A0) z8vl6rQ}=T5O4n*~LQ=dh_|Lv90R}-|pV#h{zKCCCblrc4H7)kQ;e+}hM{|O4;4N!S N*#rUyfJRg({{jFpOBMhC literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Saw 3.wav b/Synth/tilr_JSAdditiv/Saw 3.wav new file mode 100644 index 0000000000000000000000000000000000000000..c54735d29d6c0c1a96c645284f2d7f57178116bb GIT binary patch literal 1344 zcmV-G1;6@INk&FE1pok7K~_a(ZFC?I000010096*tN;LrSOEY601yCVVRT`z1ONc3 z0dWI51q27q2$%|F3@{G=56=;x6Lb|x7ajP&ZgFo;a3FEwahh^ObK-M$bp3RCb?bFgcCmH_ zcTjhdcg%PGcPMyCcw=~Xc!YS0c#3$1czbwhcvE;eco=x|cglC2cWQSvcldU{c64?; zcHecfbvSh#b%Jz&bh&fibKr6~bMqz9(Kr-P|V zs~N5DuFJ5jvXZoWwraRtx>LMPzD&SO!cW9j#$Cv0%5=k91$?*j4p^6>QL_S*Q&`o8?G{+s}c0(k^x22%(?3M>p64h9hM z5#1BW6|)zd8HO8e9a0}YAr&JGCCDb*C}%3CEbJ|OFg`JJG!it5Ia?;78VewjAEF{R zBaS7FCVwciDPt?JEHN&JFX=EkGKDkBGzK<1H)lAGIjK6tJIg%YJ<>kjKfFN1L5o6@ zLqbGPMc+mEMrcQpNZCjsN;XQVOMXi;O{q-KPM=M|W*LeD}+L#snLM3+Q9MUzEbMxsWQN5DtrNbE>X zN+U|wOIk~OO^HlMPoYgkQGrgKQ_@cARvk^XUwhpvrp>tgt_`;~vO%|IwD7krw#&G) zx3IYjxs|#;x^=ryyGguByb!%Ay~@4$y?MU0z8=3~zr4Q%z*E4Sz~#U$!F9pB!3Dxh z!j;0_!YIRa!^6WA#Aw98#2LkS#oNU}#;wL4$CSqt$d$+*$*##o%H_(5%QwvH%&E<7 z&Nj~o(BII#(V^0b({$8j)l}9^*F4xe*(BO1+X&nf-Q(T%-m%}$;B(=h;w0l|yPTq?JDcb@ma;Hv{}2xxEj1DyOO<4y-B}mzbL_v!6?JU!$`#u z#)QXo$l%HE%392Z&GF7R(5KM^(|gqG)oj=8*mv3n+@9Sz-`wDX;wGJC6 z>|XAT@W$~^^b_?>`1bdJ{b}mT-dW@bmKf)@1yZd^Dp)7_P+Rr`cV8C z{^9>~ZE$Qn00000000000001!SO5S#00000000000000000001000000000000000 z00000000120ssI2000000001CV`*d<0001R*Qf?O004jh000040000400aO4(1}K` C?`|;w literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Sine 1.wav b/Synth/tilr_JSAdditiv/Sine 1.wav new file mode 100644 index 0000000000000000000000000000000000000000..184608633c3729888d002ed290f494e00fe1c0dc GIT binary patch literal 2092 zcmV+{2-EjcNk&E_2mkZK_v7{&_EYtW^uqJ{@-gyi@uTqD?-TD$?uPBY?D*?4 z>uc(z>EGxY=v3#A=F8;< zq=cjTqHv<;pYTh2e!BhFpf5hTDc5hhT@J zhvS;t%$B9ufVTeu=}u@u{pBYvU#%{w7s-pwFI`QwoSM5x0<*? zx#_u*x;ne$yN|p&z2?1>zCXY1znj2H!TQ0f!dt@(#JQd_%?BDFB?QQNe@B8n=@Qd+N z@*4Bv^Q-i8^*r_i_ssW|_+I%Z`tSO<`+@vQ{S*G({-ytH05t*r0muT816c$j1?vU0 z27L!b2n`9=37-mQ3oZ=z48IMB4o?pj5Zw@^5o{7N6ZsRs6owT`77`cO7o8Yl87CU- z8n7F595@~O9lsudA4DJrA;}?&B1|IGKn)lH1{;FHDop#H_tbOI5;`$Ii5OEI|4kkJY+o;KEytAKOsQR zKzu|BZPq0r;Q0Y*F zQ6N&eQc_duQ-V|&Rj*YbBlA~b2fB}blr3=b$)fu zbsKhRcC~i@c1d@Uci49xcw=~{c zd~$rCe9nCNd?kHOeRh4GeZ+m}eGPsueo%gJevW>ue#m~~e*JzHe=L7Pe^`HOe|~?E zf1iJ@f4hIkf75^6f98MffAfF%fBS#_fB%2~fBt{_fB1j%f9-$ff8Br6f5?Bkf3AO@ ze~*8De`|kOe?xyPe-?lJe&c?~eye_tes6wHelC6te&~I}eV%=GeNKHOeffONe4u=C zd_sH?eBXPldv|+5dkTBfdYXD*dL(-2d8v77c`AA7c&2z_cpZ4ycaV2Tcm8&?c4&4Q zcFlEubuD$=bcu8}bm4Q0b2M|?a)NRwa?EjXaT0N}a8q#eZ;@{;Z^~|DZvJhSZ7yxZ zY+G#aYl3SPYo=;8YQ4s3VlrZ}VJ~5; zU?^asUmahSUJYJ_UHe>cT;*F>TgzHKTCG_fS&mrzS7}$)Rzg;+RTou&ROwSuQ@Bzd zQiDY}ai$Z-a2d zasP57bUAfPc1L$Ncp-W9dAoXZdnJ6qd{KSZeM^3`eh+_Hf0cj5f98MsfB%2@f8>9^ ze~o`ne*=G~enEcEeMx=0d?9>&d&7DIdN+AhcyV`sc6oJibX{{Xas_eEZ;fs>ZQpBs zY6EGFXA5R?WZz;#VT)heT_IdyT7_7%R@qeLQu$B^Px(yuO7BR|M!7_vLVZ9!J`gVM|f?0xb zf|r8Jf(?UVgTI3-gr$Tth0BF+hAD^Zhr5WMiHVAXiK5(P?~?Lv^)UGK`nLU&09FGU2KETJ3wRDt5eyW@ z7L*xA9P}QbAz&l|C!i@#EZ{C|F$gqh>3^Eh7*Qwh1rBlgwKOv zg9C%Ff^C8~f)9fCf$f3sfdhggf>VN%g5QEUgQtTZgr$T*h2@2nhFgazhy{u7iSdg0 ziw%uAj%Sahko%ESlev^RmbaHfncJF=oI#%bpVy(Qql=}9r+=w`tDUX0uhX&nvqQFj zxWl?9y?wvl!Cb`3#z4u`%T~_b&~DQl*0$JI+Z5is;A-PE=IZF8>vrxg@#6EW_Q3h1 z{O101ZE$Qn00000000000001!SO5S#00000000000000000001000000000000000 z00000000120ssI2000000001CV`*d<0000WE2aiK004jh000040000400aO4(1}K^ CO=?&G literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Sine 3.wav b/Synth/tilr_JSAdditiv/Sine 3.wav new file mode 100644 index 0000000000000000000000000000000000000000..1dc23adc24e1df1b7e5c8cb2a6ca4f996f99a537 GIT binary patch literal 1344 zcmbtUdr*{B6#sUY$HF2l0Tb$2kc7Mh6_gB-$G|2zfnWs0BVkb_;wA5e-Fv@#zg-r2 zKf*;J1c{f}kP(&*S`g(#ATm6Z7#{7giQDC{NArzKP9L}~xuXMj4mx;;_@?{u16+c}L(q|laF?U} zNI_I^^sbnp*o64eg#C$$Nj1rhDNU(3Eht@A&>`^4c#sjAc`I|bP$rZMcZwoK4Wd4g zIxqnTK?r~#2Q+{Spc8a~n_v)(0Xdif6JQbi4isP(tblp2`WF^x{y$LVd6t#`k@MgD zD!YoG?@eW2&#LpKs(nB0mG9BC-?-D+#?K0y*Sscw zm1}%-OUot`qXI)S{VQBb*GI=&JCI$WF{?(g?3uQVAVQ76%J$W&ClQYyD1w(yEp;#S z%=bR5o=N}BV(RWh%GgWy%jMR0&W&1)R1TXC%KAiF0*FwY*Cfz$0HS4X97+em$PN#Ws@J>EnO^8 zE7?)(TU1ooRbW&Qn%|LUl$V?PFy}m6t2RJbiP5nfHf#9%8BAr0;-m- zI7MlT_ln)cJH)?G<&+~ejiq=u?m=ltA9awCq$xQJ>tGx_3~it()P;Ib9daP^AFv=7 zZh*#c8|1+dmKNNG_l>OQTV!m)IJ%cupV zA}cgUo~L6RNDeswFG2}SfL_oQn$z_RXwjrAu%PyH*Z{SlAuZ<67J9-!NW!yl7;2I} zWEuGf>5ux6H=e{&iYL~POi2c^?nxEd4mqOS$vl36dQod}PHAG9L{@itzQU_&s5-XR zww}?n(#&eH`#iR-=PUd6ufOs5Zt92pZjb8={VhX5BRaQh~H!f>^Jw*%>P5I`(HiRl@jD5k|CXTzX!Oj)iL;`YWVC J?|s?4KLO})l=1)o literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Sine 4.wav b/Synth/tilr_JSAdditiv/Sine 4.wav new file mode 100644 index 0000000000000000000000000000000000000000..840c6e98e6e25ed850d7b6f817121980a47cc753 GIT binary patch literal 1344 zcmYjRX;4!K5PrFjcYqK9IRwQJ5eVdJ(Xn;3lHpPiT0w1RK+ulVC@5;t76ffQhssbn zO2d(vdF9#tI!jTXN1O(oDxf_2tb$7nq`SzRLUprq`id>$^0Dzq3 zYm+uBN+Wy$0HAntWjg>I&ZPiUz!xaYFU`M32Y`#7tL{Ht51r2)TD#e1Cn0hkk%(u_ zD5j51U4Hvwa%v)ELT=qTes)Y{fhAJ)(`!-tEfmvT0g1Nty|Dt2#qXQ{PqXQb|

=4$Dy}|4lU%AG6@v73Gmx(|xV$(D8b<_B`$h@7>$0>XqnE=&kzIeOLN?3?&BC z@KJwbzq^0qK<9vP@aI9t;KreUh9t%&Bi*#kq&6)dzC0XYt~6WBIU|3K2uB-78I}@@ z&XPQKV=Q9)_&70MV0~#_GI3#I*5r4SrpYyL@4bzhIx%IRDwuvgEt_ebVdC3xCBBq6 zOK`~TBt|Z_wb&^3Lix9C`p%L+hbP$PE4l zZUbY%VeV;eE*Ik7=O{QKoG0vJb|AZhmCvHF&M`k@8W5CcNj0$EH>jkTf&F7$;16&z647NboFo--tDtwms627-)?edfPc5&{O@aPCr#QAx7^MfPN$Ri6r;amA_QE38P z(6Vqv^gwjwqR^NdG0Cx-*upqVT#0Z}sE8km&rNumAQIJy2vN59lGrIuk<>^qiJvr8 zS}tvuVp38XAd8o+kY&o&$-b1Wmu1W3GN~+7=8@{8H>HQ9Ur0lxdP%b+Tf&fB7q1gL zMD-$}=s`ky0vi8${A*#d@J^f{t~z!yCL`wJB3JaP=!S)Q!F)kp)G7Y!1+)d?$gk%A zFz?rhXW{0#^toYSl2Ca_PVnYAJA%Fs{4t<PCG}thXx>CNupw+!PctO>o5ca$u~omLp$?9N5b@JGRvPOa z3r73{i^no%aew;#f;>?*aXMajJ~j>;%l%P));F4ax{Wy&IUYV8HqH>z%YrUY_Xes` zUY_9l?(>lyd*DeU8+!~M;vV!mu*v!L{z?b7J>AaE*31UK3umowWl6HNq?%`&wU}bI z`Vne3tK%;k0mH;in)&W zHU^D}Q(i@tu$G{ZW9@dAK6wegb6yXYM#jN~}oG#atx2#fErLs(! zk*rv1B+Xett`}A1SWz@~??d!99+dt?(Ix&$n?LI%Vlp|vRHRVdxXbrShhOV{2HN3=R z|F)~dx*ZMQcJ9gE=j1f!LUX(Q{UTX*^u2eU?*acB%1WRGjSxIX|CK=qi#>Ta{C)&K z@^I8h)DdPLvzaMm7BK^uD^UqiFC&#Bog+fSLr!{y5kp5p_;g}$1#L4mEpUuN2nab* zaa%Gbc89kvWjvpnuLC)6t1MH)Q*ZlS)pd3S||zfgtkCZ&<`5GRd5kRB2>8p zdte1jfdSC@!W9O&#(&A0f=A&2xCiclYZ0ce=@|?tpo{lp< zp~vz^w1=~YFoS{bAHKy69PelMHM|<`o$pb3sq;eRIi^d|IndGGUfRlPaek=VD5@{J zf2wBhZ=dh9Rq-kh-z1d7Wh2)g{BoA`8vmmO1& zrf@Eeu>;sE$xX@ElW!-FBs;R7vlA})q=cmAU0zJ%aSn2cJg+PPAIdGvPbiEiPQ5;T zBjD!Ttv~M8*9hxHO_bKo4sw@4cYkl?o7Q&^KX!h?O_Ao9OVu)S_-xeaNPFa;{bzhX ofNb<@gT9tulrQT4A}#nNT@R6Xi*gJyoAgfyd4)(IxhKu_Z;khC;s5{u literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Spectral.wav b/Synth/tilr_JSAdditiv/Spectral.wav new file mode 100644 index 0000000000000000000000000000000000000000..d4d3b6a9e5d1f1475dc92a68164d2e386475f87f GIT binary patch literal 1344 zcmV-G1;6@INk&FE1pok7K~_a(ZFC?I000010096*tN;LrSOEY601yCVVRT`z1ONcL z1Njdy9GxX!D+n*%GK4pyJ19O33zB>=h^7E>xk{|?b_{-?NIE6=?3P$k;-9X+t z-I&>X)W^@R%rncY%BIR(%T~>P(8bfy)j`%o)?U|V*_zrX+O*l$+hO5#==1GV@Eh^X z^1Jow_gMJo_-Fhw1BD9X4h0XO47>_E3quZc6QmkmBN8k%GNd!9Fsm#GEPgPZH_klS zJu*E?Kc+=cO%+m)Qj<`gPRLEYP0dXePBBmIP$E(ZQsq!5PmfHENKr#0KC3#TISD$m zIdC^YH2EfbVD@7^JCt@acB_<`^BWfQ-7t;;025JNL0sQ`S`SJ6X z@c{Ce@h9$9=?&(K z2q6kP3f%}l2igXU2UrKa1iJvN{h<7Z{Q3J>`>Ol`0bK^O2)zXz{|Wi~_Coob{nG(F z1>FZ+2~`RD2k`|51Q`Q<19=1%1(gLZ2agIH4`34$7Q_}Z7UmT;7dRT}9YrC=BQ+%f zCVD4>DjqI;F!V4MFl;UXEDb5XCd4M7DOWA%E=4UfDzPW6B_AU=AZs3I9+e)Q9Bvq- z6pa&C6u}d~584S{1-Anz0}KIX{r>vW`$+z${22r~=$Ymb5`P$( z9BUrHAFv@3BhMj{9q<@g7p55)9V{Lc9K#p{7HSiP4|fVH1{(yE1Azjo{~!Do`W^ZT z`c(JN@ty0p=7{5R;pN{e--Y1JdWdZ>OJZb>@Dxz z@ecDa^H1{!^g8t<_U-k8^w;tT@{aN%^yKxS_I>s1^7!x{^5^!M{8j&${Ve$b^(*tS z^fdZ&ZE$Qn00000000000001!SO5S#00000000000000000001000000000000000 z00000000120ssI2000000001CV`*d<00000RT2a|004jh000040000400aO4(1}JB CabWxa literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Square 1.wav b/Synth/tilr_JSAdditiv/Square 1.wav new file mode 100644 index 0000000000000000000000000000000000000000..3e3a25d26f4ca93ca2aa3d088d6f21fba0c571de GIT binary patch literal 2092 zcmWIYbaPYTU|^`S3``6H3@M2vi3~tB%d1D>Xb6mk Uz{m}O$d*w!8UmvsFfu~`0QnUL1^@s6 literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Square 2.wav b/Synth/tilr_JSAdditiv/Square 2.wav new file mode 100644 index 0000000000000000000000000000000000000000..3df2d4b667023e8b39c9ace24704c952f1281895 GIT binary patch literal 1344 zcmZuxO=wd=5Z=V}(2G`3)PuBo@z9e8p@*W4+E5MTCDk@hwAv&uBicp;>#blcB8oRF z;^s9?iG^yZO{y)5h`*p96g>1KDAtSEi+brPh%=pRcVEmKW_NzR`DSJhulq5{Li$^}0d_+>SdfGa48sWAf>F2&E)<{$lQ4xc zjWUDs2&J^9C76XW+N33zTeHgOk^dNTb5)ye&J%`}Q3}Cmog<5xyeIciER^F2-C#kT8x{llEzRX?Cg>%ihiu@wq%aSa}+w!^`mY3xPd0sj)Df>`PNL$8b z56UrlTz2CbMU7P@iXPb(D%s?*Z6P*-YOUueZdFl?RTYdgV_sV^GIy=VJ=N*0tmmmI z6k9!v8RJc8Ki+`r*i$ja$Eq8vYBg!?Y>H9k^(1OGV$j{FKC=G8T*W=S464TLZ@#z} zO&PPFs*1`={_VrA`HmN<51*Irf8X}|Z{c_o<|D$Nkz#q|Q0p V(T!d^7{J;rW$;&sisz@Bp?`0wXVL%w literal 0 HcmV?d00001 diff --git a/Synth/tilr_JSAdditiv/Stairs.wav b/Synth/tilr_JSAdditiv/Stairs.wav new file mode 100644 index 0000000000000000000000000000000000000000..01996df067c0acdb73761a504108622b2d62fbfb GIT binary patch literal 1344 zcmZuxu}%U(6nu9;Pibk*7g*S6l*G>9?SUncn3zanA|G&th1UKAIS%8e*xc{9?%eoz zyMUYBdv9jmN@flwH{WAb_C7Gs zkmn2-t#nQm+GoKV?{TJ2VBUA|!9gZI;V&ojKASh4X^f?Nb?2-(Zce9JPz}r!hA}5~ zp0_piHmptkwcAadV(ft(+nkLVMTnJAE|w8oMyKvGKX@MJlO`H>rb7m)gXZ-wrODIrn(OQQ*_&<0o=T_d^QQL)8MJ!e}nZM zKlb*+ZS_?%VG2amXLPX=-!$bkG4!~D|6e}^z8snlYWS{y#EnOm(>2rUVy2_y=i3L6WT3la>D z36U`Gy6vPxi6}uHT7P1yD7pfN}7@-&) z8JHOq8jl(a8;2VL9DW@79CaP>9c>=y9%LWiA6p>TAW|XDAxa{}B0(d)BRC|qBrhea zB_}4LCLSl6Clx4>C=DryDFiBiD*GyTEAlIDEb1&}E#WO)F4!(qFV8PaFvl=LF}^W6 zGPN=>Gp#cyG^8{iHJddTHj*|DH;Oj}IDt6*ICweqId3}aI%hlLJ6=56JXJl>JxxBy zK14siKRZCTKr%tDK`KI}LLoz)Ll{JqL=Q!aMF&QLM*c>6NA*W>NbN{yN#sdiO4~|T zOVUeDOv+3}O~Fk)PPk4qPp?laP^VBLQJzs6Qk7B>Q;kyyRD@LjRC`tTRdQDDR%%z} zS7BJ)SXf!qSx;KaT1Q*LTRvR6Ts2*>T`gXyUL;?iUmIYTU=m@DVG3e}VgX})WB6lq zWbb5aW#?sLX5MC6XVqs>Xw7IyX~bziYP)JTYqD!DY^rP}ZJ}))ZkTQqZ;x*aaEEXL zaei_7admR>a&2?yb7XYibX#@Tby9ZDc1m}}cR_f)csO~qc`tgadMA6LdoX*ndO3Q% zc|v){cuIKBcT;!Qc3gJgb!2tvbZvCYX7FWoW%*=%WCCP{V+v!BViRJQVH{zg zU?pIwUoBs3JzqWJJZC)YJ8(Po zI(a(%If6L`If^(BIFmOQH=H&gHl;NwHLWx*xg3ndGw3M~q<2{sA3 z2tNqJ2S*3Y22cjo1z82%1Y!i{18M{B0&@cQ0ek@f0fhhw0FM6?|Cjz7{-FIN{iysc z{IUBs`?~r*`osB0`OElF_|*4U_uckk_U83!_3rd@^!M|7^8oXN@(A*c@e%Qs@EY)* z?<4Q0?kn!F?KJJU>^qhI!>Q3s@=~wC7=wRsN=V<5c=5gls<$C4*7$biTM$cV=c$C1Vs#+t<*#iPV0#H+(F!?eOU!o9&k!N$N!z|Ox@zt_H7zTmxN zz39Acyz#qsyZX9*x&yk0xeK|FxD>dUw;i{kwkEc!wJx=@v^TW7vp}=NvPrVdu~D(s zuv)O*uVb(0u4}IFt#qyVtbMEjtcI%!tB$G@s+Xx7si3GNsHvwdr?I9srn;p*rNg90 zq|2jFqtv2VqTQikq2{1!pzfb@pZA`7o&cVNoe7`_@inob7iNJ_Nh{=adhtY;shT4T*h2w;0gvNwIg}#M3hP8$HI#eT*4#dXH<#%#yv$7IOg z$Xm(Q$x_PB%1O(`%R$V%%s9=o%`eWY&L+>I&mPd3&=t{<(G1dv(gf3g)BDqQ)biAB z)#=q`*5TG%*Vxxo*w5HY*~ZyI+P>O3+qK&<+^pOv-J{(f-kaVQ-;&=B;ECV`;ep}& z;dtWn;&0>Y<7VXIM84`>mlr%>=^Bn?GNsY z?gsCI@BZ(3@b&O<@$B(v^5gPf^V{=Q^wRWB^~v=`_QCc%_qg{n_^ +// This work is free. You can redistribute it and/or modify it under the +// terms of the Do What The Fuck You Want To Public License, Version 2, +// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + +// Modified asrd lib to work with buffers instead + +// buf[0] = env +// buf[1] = a +// buf[2] = d +// buf[3] = s +// buf[4] = r +// buf[5] = state +// buf[6] = scale + +@init + +function _adsr_set(time) +( + 1 / (0.2 * time * srate + 1); +); + +function adsr_seta(time, buf) + //instance(a) +( + buf[1] = _adsr_set(time); +); + +function adsr_setd(time, buf) + //instance(d) +( + buf[2] = _adsr_set(time); +); + +function adsr_sets(gain, buf) + //instance(s) +( + buf[3] = gain; +); + +function adsr_setr(time, buf) + //instance(r) +( + buf[4] = _adsr_set(time); +); + +function adsr_reset(buf) + //instance(state, env) +( + buf[0] = 0; + buf[5] = 0; +); + +function adsr_r(buf) + // instance(state, r) +( + buf[4] < 1 ? ( // r < 1 ? state = 8 : adsr_reset() + buf[5] = 8; + ) : ( + adsr_reset(buf); + ); +); + +function adsr_s(buf) + // instance(state, s, env, scale) +( + buf[0] = buf[6] * buf[3]; // env = scale * s + buf[5] = 4; // state = 4 +); + +function adsr_d(buf) +// instance(state, d) +( + buf[2] < 1 ? ( // d < 1 + buf[5] = 2; // state = 2 + ) : ( + adsr_s(buf); + ); +); + +function adsr_a(scale, buf) + // instance(state, a, env) +( + buf[6] = scale; // this.scale = scale + + buf[1] < 1 ? ( // a < 1 + buf[5] = 1; // state = 1 + ) : ( + buf[0] = scale; // env = scale + adsr_d(buf); + ); +); + +function adsr_process(buf) +local (env, a, d, s, r, state, scale) + //instance(state, a, d, s, r, env, scale) +( + env = buf[0]; + a = buf[1]; + d = buf[2]; + s = buf[3]; + r = buf[4]; + state = buf[5]; + scale = buf[6]; + buf[5] ? ( + + // Decay + buf[5] == 2 ? ( + buf[0] += d * (scale * s - env); + abs(buf[0] - scale * s) <= scale * 0.000001 ? adsr_s(buf); + ) : + + // Release + buf[5] == 8 ? ( + buf[0] += r * (0 - env); + abs(buf[0]) <= scale * 0.000001 ? adsr_reset(buf); + ) : + + // Attack + buf[5] == 1 ? ( + buf[0] += a * (scale - env); + abs(buf[0] - scale) <= scale * exp(-5) ? adsr_d(buf); + ); + ); + + buf[5]; +); diff --git a/Synth/tilr_JSAdditiv/add.array.jsfx-inc b/Synth/tilr_JSAdditiv/add.array.jsfx-inc new file mode 100644 index 0000000..50876db --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.array.jsfx-inc @@ -0,0 +1,269 @@ +desc:Simple two-dimensional array interface + +// Copyright (C) 2015-2019 Theo Niessink +// This work is free. You can redistribute it and/or modify it under the +// terms of the Do What The Fuck You Want To Public License, Version 2, +// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + +/* Example + + desc:Last-note priority mono synth + + import Tale/array.jsfx-inc + import Tale/midi_queue.jsfx-inc + import Tale/poly_blep.jsfx-inc + + @init + + voice.array_init(0, 128, 2); + + @sample + + while(midi.midiq_recv()) ( + midi.msg1 &= 0xF0; + + // Note On + midi.msg1 == 0x90 && midi.msg3 ? ( + + // Remove note if somehow it is already playing. + ptr = voice.array_find(midi.msg2); + ptr >= 0 ? voice.array_remove(ptr); + + // Add note, and set pointer to it. + ptr = voice.array_add(); + ptr[0] = midi.msg2; + + // Set oscillator frequency. + ptr[1] = osc.poly_setf(midi.msg2, 440); + osc.a *= 0.5; + ) : + + // Note Off + midi.msg1 == 0x80 || midi.msg1 == 0x90 ? ( + + // Remove note. + ptr = voice.array_find(midi.msg2); + ptr >= 0 ? ( + voice.array_remove(ptr); + !voice.size ? osc.a = 0 : ( + + // Update pointer to new last note. + ptr = voice.array_get(voice.size - 1); + osc.poly_setdt(ptr[1]); + osc.a *= 0.5; + ); + ); + ) : + + // All Notes Off + midi.msg1 == 0xB0 && midi.msg2 == 123 ? ( + voice.array_clear(); + ); + ); + + spl0 = spl1 = osc.poly_saw(); + + Initialization Functions + + * array_init(index, max_rows[, cols]) + Example: array.array_init(0, 64, 2); + Sets the offset and size of the local memory buffer to store the array + in, and returns the next available memory index (i.e. + index+rows*cols). If cols is omitted, then it defaults to 1. + + * array_alloc(max_rows[, cols]) + * array_free() + Example: array.array_alloc(64, 2); + Allocates/deallocates a block of local memory to store the array in, + and returns its index. + + Note: Requires Tale/malloc.jsfx-inc. + + Array Functions + + * array_get(row) + Example: ptr = array.array_get(0); + Returns a pointer to the local memory index of the specified row. + + * array_add() + Example: ptr = array.array_add(); + Adds a row to the end of the array and returns its local memory index. + Note that the row is added but not initialized (i.e. it does not + contain any data yet, nor is it zeroed.). + + * array_insert(ptr) + Example: array.array_insert(array.array_get(0)); + Inserts a row into the array. Note that the row is inserted but not + initialized. + + * array_remove(ptr) + Example: array.array_remove(array.array_get(0)); + Removes a row from the array. + + * array_clear() + Example: array.array_clear(); + Removes all rows from the array. + + Miscellaneous Functions + + * array_first() + Example: ptr = array.array_first(); + Returns a pointer to the local memory index of the first row, or -1 if + there are no rows. + + * array_next(ptr) + Example: ptr = array.array_next(ptr); + Returns a pointer to the local memory index of the next row, or -1 if + there is no next row. + + * array_last() + Example: ptr = array.array_last(); + Returns a pointer to the local memory index of the last row, or -1 if + there are no rows. + + * array_find(value[, col[, ptr]]) + Example: ptr = array_find(123); + Finds a value in the array at the specified column (0 by default), + starting at the specified row pointer (first row by default), and + returns the local memory index of the entire row, or -1 if the value + was not found. + + Instance Variables + + * buf + Example: ptr = array.buf; + The local memory address of the buffer in which the array is stored. + + * size + Example: num_rows = array.size; + The current size of the array in rows. + + * num + Example: num_cols = array.num; + The number of columns in each row. + +*/ + +@init + +function array_init(index, max_rows, cols) + instance(buf, size, num) +( + buf = index; + size = 0; + num = cols; + + buf + max_rows * num; +); + +function array_init(index, max_rows) +( + this.array_init(index, max_rows, 1); +); + +function array_get(row) + instance(buf, num) +( + buf + row * num; +); + +function array_add() + instance(buf, size, num) +( + buf + ((size += 1) - 1) * num; +); + +function array_insert(ptr) + instance(buf, size, num) + local(end) +( + end = buf + size * num; + size += 1; + ptr < end ? memcpy(ptr + num, ptr, end - ptr); + + // Returning the pointer here might not be very useful, but it is + // consistent with array_add(). + ptr; +); + +function array_remove(ptr) + instance(buf, size, num) + local(end) +( + end = buf + (size -= 1) * num; + ptr < end ? memcpy(ptr, ptr + num, end - ptr); + + // Again, returning the pointer here is not very useful; meh. + ptr; +); + +function array_first() + instance(buf, size) +( + size ? buf : -1; +); + +function array_next(ptr) + instance(buf, size, num) +( + ptr += num; + ptr < buf + size * num ? ptr : -1; +); + +function array_last() + instance(buf, size, num) +( + size ? buf + (size - 1) * num : -1; +); + +function array_find(value, col, ptr) + instance(buf, size, num) + local(ret, end) +( + ret = -1; + end = buf + size * num; + while( + ptr < end ? ( + ptr[col] == value ? ( + ret = ptr; + 0; // break + ) : ( + ptr += num; + 1; // continue + ); + ); + ); + ret; +); + +function array_find(value, col) + instance(buf) +( + this.array_find(value, col, buf); +); + +function array_find(value) + instance(buf, size, num) + local(ret, ptr, end) +( + ret = -1; + end = (ptr = buf) + size * num; + while( + ptr < end ? ( + ptr[] == value ? ( + ret = ptr; + 0; // break + ) : ( + ptr += num; + 1; // continue + ); + ); + ); + ret; +); + +function array_clear() + instance(size) +( + size = 0; +); diff --git a/Synth/tilr_JSAdditiv/add.fft_real_synth.jsfx-inc b/Synth/tilr_JSAdditiv/add.fft_real_synth.jsfx-inc new file mode 100644 index 0000000..31b9975 --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.fft_real_synth.jsfx-inc @@ -0,0 +1,214 @@ +desc:Real FFT bandlimited synthesis + +// Copyright (C) 2015-2021 Theo Niessink +// This work is free. You can redistribute it and/or modify it under the +// terms of the Do What The Fuck You Want To Public License, Version 2, +// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + +/* Uses the real instead of the complex FFT, which is almost 2x as fast, but + requires REAPER v5.25+. See Tale/fft_synth.jsfx-inc for more + information. */ + +@init + +function four_real_align(index, size) +( + (index & 65535) + size > 65536 ? index = ((index >> 16) + 1) << 16 : index; +); + +function four_init(index, size) + instance(buf, coef, m) +( + m = 0; + + buf = four_real_align(index, size); + coef = four_real_align(buf + size * 2, size); + this.size = size; + + // Actually uses only up to coef+size, meh. + coef + size * 2; +); + +function four_setf(freq) + // global(srate) + instance(dt) +( + dt = freq / srate; +); + +function four_setf(note, tuning) + // global(srate) + instance(dt) +( + dt = exp(/* log(2)/12 */ 0.057762265046662109 * (note - 69)) * tuning / srate; +); + +function four_setdt(time) + instance(dt) +( + dt = time; +); + +function four_update() + instance(dt, size, m) + local(n) +( + dt > 0 ? n = min(ceil(0.5 / dt), size / 2) : n = 1; + n != m ? m = n; +); + +function four_update_odd() + instance(dt, size, m) + local(n) +( + dt > 0 ? n = min(ceil(0.5 / dt), size / 2) : n = 1; + (n|1) != (m|1) ? m = n : ( m = n; 0; ); +); + +function four_update_one() + instance(dt, size, m) + local(n) +( + dt > 0 ? n = min(ceil(0.5 / dt), size / 2) : n = 1; + (!n) != (!m) ? m = n : ( m = n; 0; ); +); + +function four_reset() + instance(m) +( + m = 0; +); + +function four_setdc(dc) + instance(coef) +( + coef[0] = dc; +); + +function four_setdc(dc, phase) + instance(coef) + local(t) +( + coef[0] = dc; + + t = phase + 0.5; + coef[1] = t - (t|0); + + coef + 2; +); + +function four_getdc() + instance(coef) +( + coef[0]; +); + +function four_getrms() + instance(coef, m) + local(sum, cos, sin) +( + sum = sqr(coef[0]) * 2; + + cos = coef + 2; + sin = cos + 1; + + loop(m - 1, + sum += sqr(cos[]) + sqr(sin[]); + cos += 2; sin += 2; + ); + + sqrt(sum * 0.5); +); + +function four_sum(phase) + instance(coef, m) + local(sum, k, cos, sin, t) +( + sum = coef[0]; + + t = coef[1] + phase; + t = 2*$pi * (t - (t|0)); + + cos = coef + 2; + sin = cos + 1; + + k = 1; + loop(m - 1, + sum += cos[] * cos(t * k) - sin[] * sin(t * k); + k += 1; cos += 2; sin += 2; + ); + + sum; +); + +function four_sigma(index, size) + local(ptr, x, dx, y) +( + // Skip DC. + ptr = index + 2; + + x = dx = size > 0 ? $pi / size; + loop(size - 1, + y = sin(x)/x; + x += dx; + ptr[0] *= y; + ptr[1] *= y; + ptr += 2; + ); +); + +function four_fft() + instance(buf, coef, size) + local(ptr, scale) +( + // Scale. + memcpy(ptr = coef, buf, size); + scale = 1 / size; + loop(size, + ptr[] *= scale; + ptr += 1; + ); + + fft_real(coef, size); + fft_permute(coef, size / 2); + + // Scale DC. + coef[0] *= 0.5; + // Zero phase offset. + coef[1] = 0; +); + +function four_ifft(sigma) + instance(buf, coef, size, m) + local(phase, ptr) +( + // Copy precalculated Fourier coefficients up to Nyquist. + memcpy(buf, coef, m * 2); + + // Decode phase offset. + phase = (buf[1] * size)|0; + buf[1] = 0; + + // Scale. + ptr = buf + 2; + loop((m - 1) * 2, + ptr[] *= 0.5; + ptr += 1; + ); + + sigma ? four_sigma(buf, m); + + // Zero bins beyond Nyquist frequency. + memset(buf + m * 2, 0, size - m * 2); + + fft_ipermute(buf, size / 2); + ifft_real(buf, size); + + phase > 0 ? ( + // Apply phase offset. + ptr = buf + size; + memcpy(ptr, buf, size); + memcpy(buf, ptr + phase, size - phase); + memcpy(ptr - phase, ptr, phase); + ); +); diff --git a/Synth/tilr_JSAdditiv/add.gfxlib.jsfx-inc b/Synth/tilr_JSAdditiv/add.gfxlib.jsfx-inc new file mode 100644 index 0000000..ee89f28 --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.gfxlib.jsfx-inc @@ -0,0 +1,214 @@ +desc:add.gfxlib.jsfx-inc + +@init + +COLOR_ACTIVE = 0x5de4b1; +COLOR_BG = 0x141618; + +selknob_nslider = 0; +selknob_min = 0; +selknob_max = 0; +selknob_is_log = 0; + +wheelknob_nslider = 0; +wheelknob_min = 0; +wheelknob_max = 0; +wheelknob_is_log = 0; + +doubleclk_nslider = 0; + +function deg2rad (deg) (deg * $pi / 180;); +RAD130 = deg2rad(130); + + +function set_color(color) ( + gfx_r = (color & 0xFF0000) / 0xFF0000; + gfx_g = (color & 0x00FF00) / 0x00FF00; + gfx_b = (color & 0x0000FF) / 0x0000FF; +); + +function draw_wave(x, y, w, h, buf, len) +local(i, _x, _y) +( + set_color(COLOR_ACTIVE); + loop(i = 0; len, + _x = i * w / len + x; + _y = buf[i] * h / 2 + h / 2 + y; + i == 0 ? ( + gfx_x = _x; + gfx_y = _y; + ); + gfx_lineto(_x, _y); + i += 1; + ); +); + +function log_scale (value, max, min) +local (minP, maxP, scale) ( + minP = min; + maxP = max; + + minV = log(min); + maxV = log(max); + + scale = (maxV - minV) / (maxP - minP); + exp(minV + scale * (value - minP)); +); + +function inverse_log_scale (lg, max, min) +local (minP, maxP, scale) ( + minP = min; + maxP = max; + + minV = log(min); + maxV = log(max); + + scale = (maxV - minV) / (maxP - minP); + (log(lg) - minV) / scale + minP; +); + +function mouse_in_rect (x, y, w ,h) ( + mouse.x >= x && mouse.x <= x + w && mouse.y >= y && mouse.y <= y + h; +); + +function draw_knob(x, y, nslider, label, default, _min, _max, is_log, is_sym, val_label) +local (scale) +( + set_color(0x282D32); + gfx_arc(x+20, y+20, 20, -RAD130, RAD130, 1); + gfx_arc(x+20, y+20, 19.5, -RAD130, RAD130, 1); + gfx_arc(x+20, y+20, 19, -RAD130, RAD130, 1); + gfx_arc(x+20, y+20, 18.5, -RAD130, RAD130, 1); + + slider_val = slider(nslider); + nslider == 15 && lslider != slider_val ? ( + lsliderrrr += 1; + lsliderr = slider_val; + lsliderrr = lslider; + lslider = slider_val; + ); + is_log ? ( + slider_val = inverse_log_scale(slider_val, _max, _min); + ); + + // linear map value from min/max to -130deg +130deg + scale = (130 - -130) / (_max-_min); + _offset = (-_min * (130 - -130)) / (_max - _min) + -130; + slider_deg = slider_val * scale + _offset; + slider_rad = deg2rad(slider_deg); + + gfx_circle(x+20, y+20, 15, 1, 1); + set_color(COLOR_ACTIVE); + gfx_circle(x+20-sin(-slider_rad)*10, y+20-cos(-slider_rad)*10, 3, 1); + + gfx_arc(x+20, y+20, 20, is_sym ? 0 : -RAD130, slider_rad, 1); + gfx_arc(x+20, y+20, 19.5, is_sym ? 0 : -RAD130, slider_rad, 1); + gfx_arc(x+20, y+20, 19, is_sym ? 0 : -RAD130, slider_rad, 1); + gfx_arc(x+20, y+20, 18.5, is_sym ? 0 : -RAD130, slider_rad, 1); + + set_color(0xFFFFFF); + gfx_x = x - 20; + gfx_y = y + 20 * 2 + 5; + selknob_nslider == nslider ? ( + gfx_drawstr(val_label, 1, x+20+20*2, y+100); + ) : ( + gfx_drawstr(label, 1, x+20+20*2, y+100); + ); + + mouse_in_rect(x, y, 40, 40) ? ( + mouse.double_click ? ( + slider(nslider) = default; + doubleclk_nslider = nslider; + ); + mouse.left_click ? ( + selknob_nslider = nslider; + selknob_min = _min; + selknob_max = _max; + selknob_is_log = is_log; + ); + mouse.wheel ? ( + wheelknob_nslider = nslider; + wheelknob_min = _min; + wheelknob_max = _max; + wheelknob_is_log = is_log; + ); + ); +); + +function draw_button (x, y, w, label, toggled) ( + set_color(COLOR_ACTIVE); + gfx_rect(x, y - 2, w, 10 + 2); + gfx_x = x; gfx_y = y; + !toggled ? ( + set_color(COLOR_BG); + gfx_rect(x+1, y+1-2, w-2, 10); + ); + set_color(toggled ? 0xFFFFFF : COLOR_ACTIVE); + gfx_drawstr(label, 1, x+w, y+10); +); + +function draw_grey_button (x, y, w, label) ( + set_color(0x666666); + gfx_rect(x, y - 2, w, 10 + 2); + gfx_x = x; gfx_y = y; + set_color(0x141618); + gfx_drawstr(label, 1, x+w, y+10); +); + +function harmonic_phase(h1, h2) ( + atan2(h1, h2); +); + +function harmonic_amplitude(h1, h2) ( + sqrt(pow(h1,2) + pow(h2,2)); +); + +function draw_harmonics(x, y, w, h, buf, nharm) +local (harm_w, amp, i, mouse_active, hx, ph, yamp, phase, real, img, yphase) +( + set_color(0x666666); + gfx_rect(x - 2,y - 2,w + 4,h + 4); + set_color(COLOR_BG); + gfx_rect(x - 1, y - 1, w + 2, h + 2); + set_color(COLOR_ACTIVE); + harm_w = (w / nharm) | 0; + mouse_active = edit_mode && mouse.left && mouse_in_rect(x,y,w,h); + + phase_mode ? gfx_a = 0.3; + loop(i=0; nharm, + amp = harmonic_amplitude(buf[i], buf[i+1]); + amp > 1 ? amp = 1; + hx = x + harm_w * i / 2; // harmonic x + gfx_rect(hx, y + h - floor(amp * h), harm_w - 1, amp * h); + !phase_mode && mouse_active && mouse_in_rect(hx, y, harm_w, h) ? ( + yamp = mouse.control ? 0 : 1 - (mouse.y - y) / h; + phase = harmonic_phase(buf[i], buf[i+1]); + buf[i+1] = yamp * cos(phase); // real = r * cos(p) + buf[i] = yamp * sin(phase); // img = r * sin(p) + ); + i += 2; + ); + gfx_a = 0.75; + set_color(0xFFFFFF); + phase_mode ? ( + loop(i=0; nharm, + phase = atan2(buf[i], buf[i+1]); + hx = x + harm_w * i / 2; + ph = phase/$pi*h/2; // phase height + phase >= 0 ? ( + gfx_rect(hx, y + h/2 - ph, harm_w-1, max(ph, 1)|0); + ) : ( + gfx_rect(hx, y + h/2, harm_w-1, max(-ph, 1)|0); + ); + phase_mode && mouse_active && mouse_in_rect(hx, y, harm_w, h) ? ( + yphase = mouse.control ? 0 : (mouse.y - y) / h * -2 - 1; + yphasee = yphase; + amp = harmonic_amplitude(buf[i], buf[i+1]); + buf[i+1] = amp * cos(yphase*$pi); // real + buf[i] = amp * sin(yphase*$pi); // img + ); + i += 2; + ); + ); + gfx_a = 1; +); diff --git a/Synth/tilr_JSAdditiv/add.mouselib.jsfx-inc b/Synth/tilr_JSAdditiv/add.mouselib.jsfx-inc new file mode 100644 index 0000000..1c846fb --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.mouselib.jsfx-inc @@ -0,0 +1,34 @@ +desc:add.mouselib.jsfx-inc + +@init + +function update_mouse_state() +instance(cap, x, y, lx, ly, dx, dy, right_click, left_click, lleft, lright, left, right, click_time, double_click, control, lwheel, wheel) +global(mouse_cap, mouse_x, mouse_y, mouse_wheel) +( + lleft = left; + lright = right; + lx = x; + ly = y; + cap = mouse_cap; + control = mouse_cap & 4; + x = mouse_x; + y = mouse_y; + + left = cap & 1 > 0; + right = cap & 2 > 0; + left_click = left && lleft == 0; + right_click = right && lright == 0; + dx = x - lx; + dy = y - ly; + + wheel = mouse_wheel > lwheel ? 1 : mouse_wheel < lwheel ? -1 : 0; + lwheel = mouse_wheel; + + left_click ? ( + time_precise() - click_time < .5 ? double_click = 1; + click_time = time_precise(); + ) : ( + double_click = 0; + ); +); diff --git a/Synth/tilr_JSAdditiv/add.wavetable.jsfx-inc b/Synth/tilr_JSAdditiv/add.wavetable.jsfx-inc new file mode 100644 index 0000000..2b38036 --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.wavetable.jsfx-inc @@ -0,0 +1,595 @@ +desc:Wavetable oscillator + +// Copyright (C) 2015-2017 Theo Niessink +// This work is free. You can redistribute it and/or modify it under the +// terms of the Do What The Fuck You Want To Public License, Version 2, +// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + +/* Example + + desc:Sine wave wavetable oscillator + + slider1:440<20,20000,1>Freq (Hz) + slider2:1<0,2,1{Truncate,Linear,Spline}>Mode + + import Tale/wavetable.jsfx-inc + + @init + + function render_sin(buf, size, gain) + local(x, dx) + ( + x = 0; + dx = 2*$pi / size; + loop(size, + buf[] = gain * sin(x); + buf += 1; + x += dx; + ); + ); + + osc.wave_init(0, 16); + render_sin(osc.buf, osc.size, 0.25); + + @slider + + osc.wave_setf(slider1); + mode = slider2|0; + + @sample + + spl0 = spl1 = + mode == 0 ? osc.wave_trunc() : + mode == 1 ? osc.wave_lerp() : + mode == 2 ? osc.wave_spline3(); + + Initialisation Functions + + * wave_init(index, size) + Example: osc.wave_init(0, 1024); + Sets the offset and size of the local memory buffer that will hold the + waveform, and returns the next available memory index (i.e. + index+size). + + * wave_alloc(size) + * wave_free() + Example: osc.wave_alloc(1024); + Allocates/deallocates a block of local memory that will hold the + waveform, and returns its index. + + Note: Requires malloc.jsfx-inc. + + Setting Functions + + * wave_setf(freq) + Example: osc.wave_setf(440); + Sets the oscillator frequency (specified in Hz), and returns the + frequency in seconds/sample. + + (To convert from Hz to seconds/sample, divide by srate. To convert + from seconds/sample to Hz, multiply with srate.) + + * wave_setf(note, tuning) + Example: osc.wave_setf(60, 440); + Sets the oscillator frequency to the specified MIDI note and tuning + (in Hz), and returns the frequency in seconds/sample. + + * wave_setdt(time) + Example: osc2.wave_setdt(osc1.dt); + Sets the oscillator frequency (specified in seconds/sample), and + returns this value. + + Interpolation Functions + + * wave_trunc() -- Truncate + * wave_round() -- Round to nearest + * wave_lerp() -- Linear + + * wave_hermite3() -- Catmull-Rom spline (4-point, 3rd-order) + * wave_hermite5() -- Hermite (6-point, 5th-order) + + * wave_quint() -- Quintic spline (6-point, 5th-order) + + * wave_spline3() -- B-spline (4-point, 3rd-order) + * wave_spline5() -- B-spline (6-point, 5th-order) + + * wave_osculate3() -- 2nd-order-osculating (4-point, 5th-order) + * wave_osculate5() -- 2nd-order-osculating (6-point, 5th-order) + + * wave_lagrange2() -- Lagrange (3-point, 2nd-order) + * wave_lagrange3() -- Lagrange (4-point, 3rd-order) + * wave_lagrange4() -- Lagrange (5-point, 4th-order) + * wave_lagrange5() -- Lagrange (6-point, 5th-order) + * wave_lagrange(n) -- Lagrange (N+1-point, Nth-order) + Example: sample = osc.wave_lerp(); + Returns a sample from the wavetable, and increments the oscillator + phase. + + Miscellaneous Functions + + * wave_sync(phase) + Example: osc2.wave_sync(osc1.t + 0.5); + Synchronizes the oscillator with the specified phase, and returns the + normalized phase. + + Note: You can safely specify out or range (and even negative) values + here. + + * wave_inc() + Example: osc.wave_inc(); + Increments the oscillator phase, and returns it. + + Note: The interpolation functions automatically increment the phase. + + * wave_getdc() + * wave_getrms() + Example: dc = osc.wave_getdc(); + Calculates the DC/RMS value of the waveform. + + Instance Variables + + * buf + Example: wavetbl_index = osc.buf; + The local memory index of the wavetable. + + * size + Example: wavetbl_size = osc.size; + The size of the wavetable, in samples. + + * t + Example: phase = osc.t; + The current phase [0.0..1.0) of the oscillator. + + * dt + Example: freq = osc.dt * srate; + The oscillator frequency, in seconds/sample. + +*/ + +@init + +function wave_init(index, size) + instance(buf) +( + buf = index; + this.size = size; + + buf + size; +); + +function wave_setf(freq) + // global(srate) + instance(dt) +( + dt = freq / srate; +); + +function wave_setf(note, tuning) + // global(srate) + instance(dt) +( + dt = exp(/* log(2)/12 */ 0.057762265046662109 * (note - 69)) * tuning / srate; +); + +function wave_setdt(time) + instance(dt) +( + dt = time; +); + +function wave_sync(phase) + instance(t) +( + t = phase; + t >= 0 ? t -= t|0 : t += 1 - (t|0); +); + +function wave_inc() + instance(t, dt) +( + t += dt; + t -= t|0; +); + +function wave_getdc() + instance(buf, size) + local(sum, ptr) +( + sum = 0; + ptr = buf; + loop(size, + sum += ptr[]; + ptr += 1; + ); + sum / size; +); + +function wave_getrms() + instance(buf, size) + local(sum, ptr) +( + sum = 0; + ptr = buf; + loop(size, + sum += sqr(ptr[]); + ptr += 1; + ); + sqrt(sum / size); +); + +// Truncate + +function wave_trunc() + instance(buf, size, t) + local(i) +( + i = (t * size)|0; + this.wave_inc(); + + buf[i]; +); + +// Round to nearest + +function wave_round() + instance(buf, size, t) + local(i) +( + i = (t * size + 0.5)|0; + this.wave_inc(); + + i >= size ? i = 0; + buf[i]; +); + +// Linear + +function wave_lerp() + instance(buf, size, t) + local(x, i, j) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i + 1; + j >= size ? j = 0; + + (1 - x) * buf[i] + x * buf[j]; +); + +// Catmull-Rom spline (4-point, 3rd-order) + +function wave_hermite3() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, a0, a1, a2, a3) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 1; j < 0 ? j += size; y0 = buf[j]; + y1 = buf[i]; + i += 1; i >= size ? i = 0; y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + + a0 = 0.5*(y3 - y0) + 1.5*(y1 - y2); + a1 = y0 - 2.5*y1 + 2*y2 - 0.5*y3; + a2 = 0.5*(y2 - y0); + a3 = y1; + + // x^3*a0 + x^2*a1 + x*a2 + a3 + x*(x*(x*a0 + a1) + a2) + a3; +); + +// Hermite (6-point, 5th-order) x-form adapted from +// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + +function wave_hermite5() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, y4, y5, a0, a1, a2, a3, a4, a5) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 2; j < 0 ? j += size; y0 = buf[j]; + j += 1; j >= size ? j = 0; y1 = buf[j]; + y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + i += 1; i >= size ? i = 0; y4 = buf[i]; + i += 1; i >= size ? i = 0; y5 = buf[i]; + + a0 = 1/24*(y5 - y0) + 5/24*(y1 - y4) + 5/12*(y3 - y2); + a1 = 0.125*y0 - 7/12*y1 + 13/12*y2 - y3 + 11/24*y4 - 1/12*y5; + a2 = 5/12*y2 - 7/12*y3 + 7/24*y4 - 1/24*(y0 + y1 + y5); + a3 = 13/12*y1 - 25/12*y2 + 1.5*y3 - 11/24*y4 + 1/12*y5 - 0.125*y0; + a4 = 1/12*(y0 - y4) + 2/3*(y3 - y1); + a5 = y2; + + x*(x*(x*(x*(x*a0 + a1) + a2) + a3) + a4) + a5; +); + +// Quintic spline (6-point, 5th-order) from +// http://musicdsp.org/archive.php?classid=5#60 + +function wave_quint() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, y4, y5) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 2; j < 0 ? j += size; y0 = buf[j]; + j += 1; j >= size ? j = 0; y1 = buf[j]; + y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + i += 1; i >= size ? i = 0; y4 = buf[i]; + i += 1; i >= size ? i = 0; y5 = buf[i]; + + y2 + 1/24*x*((y3-y1)*16+(y0-y4)*2 + + x *((y3+y1)*16-y0-y2*30- y4 + + x *(y3*66-y2*70-y4*33+y1*39+ y5*7- y0*9 + + x *( y2*126-y3*124+y4*61-y1*64- y5*12+y0*13 + + x *((y3-y2)*50+(y1-y4)*25+(y5-y0)*5))))); +); + +// B-spline (4-point, 3rd-order) x-form adapted from +// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + +function wave_spline3() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, a0, a1, a2, a3) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 1; j < 0 ? j += size; y0 = buf[j]; + y1 = buf[i]; + i += 1; i >= size ? i = 0; y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + + a0 = 0.5*(y1 - y2) + 1/6*(y3 - y0); + a1 = 0.5*(y0 + y2) - y1; + a2 = 0.5*(y2 - y0); + a3 = 1/6*(y0 + y2) + 2/3*y1; + + x*(x*(x*a0 + a1) + a2) + a3; +); + +// B-spline (6-point, 5th-order) x-form adapted from +// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + +function wave_spline5() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, y4, y5, a0, a1, a2, a3, a4, a5) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 2; j < 0 ? j += size; y0 = buf[j]; + j += 1; j >= size ? j = 0; y1 = buf[j]; + y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + i += 1; i >= size ? i = 0; y4 = buf[i]; + i += 1; i >= size ? i = 0; y5 = buf[i]; + + a0 = 1/120*(y5 - y0) + 1/24*(y1 - y4) + 1/12*(y3 - y2); + a1 = 1/24*(y0 + y4) - 1/6*(y1 + y3) + 0.25*y2; + a2 = 1/12*(y4 - y0) - 1/6*(y3 - y1); + a3 = 1/12*(y0 + y4) + 1/6*(y1 + y3) - 0.5*y2; + a4 = 1/24*(y4 - y0) + 5/12*(y3 - y1); + a5 = 1/120*(y0 + y4) + 13/60*(y1 + y3) + 11/20*y2; + + x*(x*(x*(x*(x*a0 + a1) + a2) + a3) + a4) + a5; +); + +// 2nd-order-osculating (4-point, 5th-order) x-form adapted from +// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + +function wave_osculate3() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, a0, a1, a2, a3, a4, a5) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 1; j < 0 ? j += size; y0 = buf[j]; + y1 = buf[i]; + i += 1; i >= size ? i = 0; y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + + a0 = y0 + 3*(y2 - y1) - y3; + a1 = 2.5*(y3 - y0) - 7.5*(y2 - y1); + a2 = 4.5*(y2 - y1) - 1.5*(y3 - y0); + a3 = 0.5*(y0 + y2) - y1; + a4 = 0.5*(y2 - y0); + a5 = y1; + + x*(x*(x*(x*(x*a0 + a1) + a2) + a3) + a4) + a5; +); + +// 2nd-order-osculating (6-point, 5th-order) x-form adapted from +// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf + +function wave_osculate5() + instance(buf, size, t) + local(x, i, j, y0, y1, y2, y3, y4, y5, a0, a1, a2, a3, a4, a5) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + j = i - 2; j < 0 ? j += size; y0 = buf[j]; + j += 1; j >= size ? j = 0; y1 = buf[j]; + y2 = buf[i]; + i += 1; i >= size ? i = 0; y3 = buf[i]; + i += 1; i >= size ? i = 0; y4 = buf[i]; + i += 1; i >= size ? i = 0; y5 = buf[i]; + + a0 = 5/24*(y5 - y0) + 25/24*(y1 - y4) + 25/12*(y3 - y2); + a1 = 13/24*y0 - 8/3*y1 + 5.25*y2 - 31/6*y3 + 61/24*y4 - 0.5*y5; + a2 = 1.625*y1 - 35/12*y2 + 2.75*y3 - 1.375*y4 + 7/24*y5 - 0.375*y0; + a3 = 2/3*(y1 + y3) - 1/24*(y0 + y4) - 1.25*y2; + a4 = 1/12*(y0 - y4) + 2/3*(y3 - y1); + a5 = y2; + + x*(x*(x*(x*(x*a0 + a1) + a2) + a3) + a4) + a5; +); + +// Lagrange (3-point, 2nd-order) + +function wave_lagrange2() + instance(buf, size, t) + local(x, h, i, j) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + h = i - 1; h < 0 ? h += size; + j = i + 1; j >= size ? j = 0; + + 0.5 * x * (x - 1) * buf[h] - + (x + 1) * (x - 1) * buf[i] + + 0.5 * (x + 1) * x * buf[j]; +); + +// Lagrange (4-point, 3rd-order) + +function wave_lagrange3() + instance(buf, size, t) + local(x, h, i, j, k) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + h = i - 1; h < 0 ? h += size; + j = i + 1; j >= size ? j = 0; + k = j + 1; k >= size ? k = 0; + + -1/6 * x * (x - 1) * (x - 2) * buf[h] + + 0.5 * (x + 1) * (x - 1) * (x - 2) * buf[i] - + 0.5 * (x + 1) * x * (x - 2) * buf[j] - + -1/6 * (x + 1) * x * (x - 1) * buf[k]; +); + +// Lagrange (5-point, 4th-order) + +function wave_lagrange4() + instance(buf, size, t) + local(x, g, h, i, j, k) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + g = i - 2; g < 0 ? g += size; + h = g + 1; h >= size ? h = 0; + j = i + 1; j >= size ? j = 0; + k = j + 1; k >= size ? k = 0; + + 1/24 * (x + 1) * x * (x - 1) * (x - 2) * buf[g] - + 1/6 * (x + 2) * x * (x - 1) * (x - 2) * buf[h] + + 0.25 * (x + 2) * (x + 1) * (x - 1) * (x - 2) * buf[i] - + 1/6 * (x + 2) * (x + 1) * x * (x - 2) * buf[j] + + 1/24 * (x + 2) * (x + 1) * x * (x - 1) * buf[k]; +); + +// Lagrange (6-point, 5th-order) + +function wave_lagrange5() + instance(buf, size, t) + local(x, g, h, i, j, k, l) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + g = i - 2; g < 0 ? g += size; + h = g + 1; h >= size ? h = 0; + j = i + 1; j >= size ? j = 0; + k = j + 1; k >= size ? k = 0; + l = k + 1; l >= size ? l = 0; + + -1/120 * (x + 1) * x * (x - 1) * (x - 2) * (x - 3) * buf[g] + + 1/24 * (x + 2) * x * (x - 1) * (x - 2) * (x - 3) * buf[h] - + 1/12 * (x + 2) * (x + 1) * (x - 1) * (x - 2) * (x - 3) * buf[i] + + 1/12 * (x + 2) * (x + 1) * x * (x - 2) * (x - 3) * buf[j] - + 1/24 * (x + 2) * (x + 1) * x * (x - 1) * (x - 3) * buf[k] - + -1/120 * (x + 2) * (x + 1) * x * (x - 1) * (x - 2) * buf[l]; +); + +// Lagrange (N+1-point, Nth-order) + +function wave_lagrange(n) + instance(buf, size, t) + local(x, i, j, m, ofs, sum, mul) +( + x = t * size; + this.wave_inc(); + + i = x|0; + x -= i; + + ofs = (n / 2)|0; + i -= ofs; i < 0 ? i += size; + x += ofs; + + sum = j = 0; + loop(n + 1, + mul = buf[i]; + i += 1; i >= size ? i = 0; + + m = 0; + loop(n, + m == j ? m += 1; + mul *= (x - m) / (j - m); + m += 1; + ); + sum += mul; + j += 1; + ); + sum; +); + +// Legacy + +function wave_cube() ( this.wave_hermite3() ); + +// Deprecated + +function wave_cube6() ( this.wave_hermite5() ); +function wave_cube8() ( this.wave_hermite5() ); + +function wave_quint6() ( this.wave_quint() ); +function wave_quint8() ( this.wave_quint() ); diff --git a/Synth/tilr_JSAdditiv/add.zdf_filter.jsfx-inc b/Synth/tilr_JSAdditiv/add.zdf_filter.jsfx-inc new file mode 100644 index 0000000..48c4b58 --- /dev/null +++ b/Synth/tilr_JSAdditiv/add.zdf_filter.jsfx-inc @@ -0,0 +1,403 @@ +desc:2nd-order zero-delay feedback state variable filter + +// Copyright (C) 2013-2021 Theo Niessink +// This work is free. You can redistribute it and/or modify it under the +// terms of the Do What The Fuck You Want To Public License, Version 2, +// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + +// Initially based on the rsStateVariableFilter C++ class by Robin Schmidt, +// as posted (in the public domain) on the KVR forum. +// http://www.kvraudio.com/forum/viewtopic.php?p=5243733#p5243733 + +/* Example + + desc:Low-pass filter + + slider1:1000<20,20000,1>Cutoff (Hz) + slider2:0.5<0.01,4.0,0.01>Q + + import Tale/zdf_filter.jsfx-inc + + @slider + lp.zdf_lp(slider1, slider2); + lp.zdf_gain(0.5); + + @sample + spl0 = spl1 = lp.zdf_svf(spl0 + spl1); + + Setting Functions + + * zdf_lp(freq, q) -- Low-pass + * zdf_hp(freq, q) -- High-pass + * zdf_bp(freq, q) -- Band-pass (constant skirt gain) + * zdf_bp2(freq, q) -- Band-pass (constant peak gain) + * zdf_bs(freq, q) -- Band-stop + * zdf_ap(freq, q) -- All-pass + * zdf_eq(freq, q, gain) -- Peaking EQ + * zdf_ls(freq, q, gain) -- Low-shelf + * zdf_hs(freq, q, gain) -- High-shelf + Example: lp.zdf_lp(1000, 0.7); + Sets up the filter for the specified cutoff frequency (in Hz), and Q + and gain factors, and returns the feedback precomputation factor (h). + + (To convert from dB to gain: gain=10^(db/20).) + + Note: In v20151024 the behavior of zdf_bp2() and zdf_ap() has been + changed in such a way that these functions are not backward + compatible. To convert code relying on the old behavior, replace + zdf_bp2(freq, bw) with zdf_bp(freq, zdf_bwtoq(bw)), and + zdf_ap(freq, bw) with zdf_ap(freq, zdf_bwtoq(bw)). + + * zdf_gain(gain) + Example: lp.zdf_lp(1000, 0.5); lp.zdf_gain(2.0); + Modifies the filter by applying the specified output gain. + + Note: You should always first setup the filter, and then modify it. If + you change the filter frequency/Q afterwards, then this will reset the + gain to 1.0, and so you will have to modify it again. + + * zdf_setf(freq, q) + Example: lp.zdf_setf(1000, 0.7); + Sets up the specialized low-pass, high-pass, or band-pass filter. + + Note: This works only with zdf_svf_lp(), zdf_svf_hp(), or zdf_svf_bp(). + + Filter Functions + + * zdf_svf(sample) + Example: output = lp.zdf_svf(input); + Sends a sample through the filter, and returns its output. + + * zdf_svf_multi(sample) + Example: output = lp.zdf_svf_multi(input); + Sends a sample through the filter, returns its output, and also stores + the individual low-pass, band-pass, and high-pass outputs. + + * zdf_svf_lp(sample) -- Low-pass + * zdf_svf_hp(sample) -- High-pass + * zdf_svf_bp(sample) -- Band-pass + Example: output = lp.zdf_svf_lp(input); + Specialized versions of zdf_svf(), each optimized for a specific + filter type. + + Miscellaneous Functions + + * zdf_reset([input]) + Example: lp.zdf_reset(); + Resets the filter state to the specified input value, or to zero if + the value is omitted. + + * zdf_bwtoq(bw) + * zdf_qtobw(q) + Example: q = zdf_bwtoq(2.0); + Converts bandwidth (in octaves) to Q factor, or vice versa. + + Instance Variables + + * g -- Embedded integrator gain + * r2 -- Damping (1/Q) + * h -- Feedback precomputation factor + Example: lp2.g = lp1.g; lp2.r2 = lp1.r2; lp2.h = lp1.h; + Filter coefficients. + + * cl -- Low-pass mix + * cb -- Band-pass mix + * ch -- High-pass mix + Example: lp2.cl = lp1.cl; lp2.cb = lp1.cb; lp2.ch = lp1.ch; + Filter mode output mix. + + * s1 + * s2 + Example: lp2.s1 = lp1.s1; lp2.s2 = lp1.s2; + Filter state. + + * yl -- Low-pass output + * yb -- Band-pass output + * yh -- High-pass output + Example: hp = lp.yh; + Multi-mode filter outputs. + +*/ + +@init + +function zdf_bwtoq(bw) + local(x) +( + // q = 1/(2 * sinh(log(2) / 2 * bw)) + x = exp(0.5*log(2) * bw); + x/(sqr(x) - 1); +); + +function zdf_qtobw(q) + local(x) +( + // bw = 2 * asinh(1/(2 * q)) / log(2) + x = 0.5 / q; + 2/log(2) * log(x + sqrt(sqr(x) + 1)); +); + +function _zdf_seth() + instance(g, r2, h) +( + h = 1/((r2 + g)*g + 1); +); + +// Low-pass + +function zdf_lp(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/q; + cl = 1; + cb = 0; + ch = 0; + + this._zdf_seth(); +); + +// High-pass + +function zdf_hp(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/q; + cl = 0; + cb = 0; + ch = 1; + + this._zdf_seth(); +); + +// Band-pass (constant skirt gain, peak gain = Q) + +function zdf_bp(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/q; + cl = 0; + cb = 1; + ch = 0; + + this._zdf_seth(); +); + +// Band-pass (constant 0 dB peak gain) + +function zdf_bp2(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + cl = 0; + cb = r2 = 1/q; + ch = 0; + + this._zdf_seth(); +); + +// Band-stop + +function zdf_bs(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/q; + cl = 1; + cb = 0; + ch = 1; + + this._zdf_seth(); +); + +// All-pass + +function zdf_ap(freq, q) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/q; + cl = 1; + cb = -r2; + ch = 1; + + this._zdf_seth(); +); + +// Peaking EQ + +function zdf_eq(freq, q, gain) + // global(srate) + instance(g, r2, cl, cb, ch) +( + g = tan($pi * min(freq / srate, 0.49)); + + r2 = 1/(q * sqrt(gain)); + cl = 1; + cb = r2 * gain; + ch = 1; + + this._zdf_seth(); +); + +// Low-shelf + +function zdf_ls(freq, q, gain) + // global(srate) + instance(g, r2, cl, cb, ch) + local(a) +( + a = sqrt(gain); + g = tan($pi * min(freq / srate, 0.49)) / sqrt(a); + + r2 = 1/q; + cl = gain; + cb = r2 * a; + ch = 1; + + this._zdf_seth(); +); + +// High-shelf + +function zdf_hs(freq, q, gain) + // global(srate) + instance(g, r2, cl, cb, ch) + local(a) +( + a = sqrt(gain); + g = tan($pi * min(freq / srate, 0.49)) * sqrt(a); + + r2 = 1/q; + cl = 1; + cb = r2 * a; + ch = gain; + + this._zdf_seth(); +); + +function zdf_gain(gain) + instance(cl, cb, ch) +( + cl *= gain; + cb *= gain; + ch *= gain; +); + +function zdf_svf(sample) + instance(s1, s2, g, r2, h, cl, cb, ch) + local(yl, yb, yh) +( + // High-pass + yh = (sample - (r2 + g) * s1 - s2) * h; + + // Band-pass + yb = g*yh + s1; + s1 = g*yh + yb; + + // Zero denormals + abs(s1) < 0.00000000000000006 ? s1 = 0; + + // Low-pass + yl = g*yb + s2; + s2 = g*yb + yl; + + abs(s2) < 0.00000000000000006 ? s2 = 0; + + cl*yl + cb*yb + ch*yh; +); + +function zdf_svf_multi(sample) + instance(s1, s2, g, r2, h, cl, cb, ch, yl, yb, yh) +( + yh = (sample - (r2 + g) * s1 - s2) * h; + yb = g*yh + s1; + s1 = g*yh + yb; + abs(s1) < 0.00000000000000006 ? s1 = 0; + yl = g*yb + s2; + s2 = g*yb + yl; + abs(s2) < 0.00000000000000006 ? s2 = 0; + cl*yl + cb*yb + ch*yh; +); + +// Optimized versions of zdf_svf() returning only 1 of the 3 outputs. + +function zdf_setf(freq, q) + // global(srate) + instance(g, r2) +( + g = tan($pi * min(freq / srate, 0.49)); + r2 = 1/q; + this._zdf_seth(); +); + +function zdf_svf_lp(sample) + instance(s1, s2, g, r2, h) + local(yl, yb, yh) +( + yh = (sample - (r2 + g) * s1 - s2) * h; + yb = g*yh + s1; s1 = g*yh + yb; + abs(s1) < 0.00000000000000006 ? s1 = 0; + yl = g*yb + s2; s2 = g*yb + yl; + abs(s2) < 0.00000000000000006 ? s2 = 0; + yl; +); + +function zdf_svf_hp(sample) + instance(s1, s2, g, r2, h) + local(yl, yb, yh) +( + yh = (sample - (r2 + g) * s1 - s2) * h; + yb = g*yh + s1; s1 = g*yh + yb; + abs(s1) < 0.00000000000000006 ? s1 = 0; + yl = g*yb + s2; s2 = g*yb + yl; + abs(s2) < 0.00000000000000006 ? s2 = 0; + yh; +); + +function zdf_svf_bp(sample) + instance(s1, s2, g, r2, h) + local(yl, yb, yh) +( + yh = (sample - (r2 + g) * s1 - s2) * h; + yb = g*yh + s1; s1 = g*yh + yb; + abs(s1) < 0.00000000000000006 ? s1 = 0; + yl = g*yb + s2; s2 = g*yb + yl; + abs(s2) < 0.00000000000000006 ? s2 = 0; + yb; +); + +// Reset SVF state. + +function zdf_reset(input) + instance(s1, s2) +( + s1 = 0; + s2 = input; +); + +// Legacy + +// function zdf_bp2(freq, bw) ( this.zdf_bp(freq, zdf_bwtoq(bw)) ); +// function zdf_ap(freq, bw) ( this.zdf_ap(freq, zdf_bwtoq(bw)) ); +function zdf_notch(freq, bw) ( this.zdf_bs(freq, zdf_bwtoq(bw)) ); +function zdf_peak(freq, gain, bw) local(fc) ( fc = $pi * min(freq / srate, 0.49); this.zdf_eq(freq, zdf_bwtoq(bw) * fc / tan(fc), gain); ); +function zdf_low_shelf(freq, gain, bw) ( this.zdf_ls(freq, zdf_bwtoq(bw), gain) ); +function zdf_high_shelf(freq, gain, bw) ( this.zdf_hs(freq, zdf_bwtoq(bw), gain) ); +function zdf_mute() instance(g, r2, cl, cb, ch) ( g = tan(0.49*$pi); r2 = 1; cl = cb = ch = 0; this._zdf_seth(); ); +function zdf_bypass() instance(g, r2, cl, cb, ch) ( g = tan(0.49*$pi); r2 = cl = cb = ch = 1; this._zdf_seth(); ); +function zdf_bypass(freq, q) ( this.zdf_bypass() );