diff --git a/Reverb/tilr_QuickDelay.jsfx b/Reverb/tilr_QuickDelay.jsfx new file mode 100644 index 00000000..a817c334 --- /dev/null +++ b/Reverb/tilr_QuickDelay.jsfx @@ -0,0 +1,155 @@ +desc: QuickDelay +author: tilr +version: 1.0 +provides: + tilr_QuickDelay/qd.delay.jsfx-inc + tilr_QuickDelay/qd.rbj_filter.jsfx-inc +about: + # QuickDelay + + Simple delay with ping-pong mode, tempo sync and feedback filters. + + #### Features + + * Normal and ping-pong modes + * Ping-pong stereo width control + * Tempo sync + * Low pass and high pass feedback filters with Q controls + * Delay modulation + +desc:QuickDelay + +slider1:delayms=250<1,1000>Delay Time (ms) +slider2:sync=0<0,16,1{Off,1/16,1/8,1/4,1/2,1/1,1/16t,1/8t,1/4t,1/2t,1/1t,1/16.,1/8.,1/4.,1/2.,1/1.}>Tempo sync +slider3:_feedback=0<0,100>Feedback +slider4:mode=0<0,1,1{Normal,Ping-Pong}>Mode +slider5:spread=0<-100,100,1>Ping-Pong Spread + +slider8:lp_freq=20000<20,20000,1:log>Low Pass +slider9:lp_q=0.707<0.707,2,0.01:log>Low Pass Q + +slider10:hp_freq=20<20,20000,1:log>High Pass +slider11:hp_q=0.707<0.707,2.0,0.01:log>High Pass Q + +slider13:mod_amp=0<0,100>Modulation amp +slider14:mod_freq=1<1,8>Modulation freq (Hz) + +slider16:_dry=100<0,100>Dry signal +slider17:_wet=100<0,100>Wet signal + +import qd.delay.jsfx-inc +import qd.rbj_filter.jsfx-inc + +@init +// 10 second stereo delay line +d0.delay(srate * 10); +d1.delay(srate * 10); + +function sine_wave(cycle) ( + sin(cycle * 2 * $pi); +); + +function rc_set(rc) + instance(a) ( + a = 1 / (rc * srate + 1); +); +function rc_lp(sample) + instance(lp, a) ( + lp += a * (sample - lp); +); +function smooth() + instance (lp, smooth) ( + lp = smooth; + smooth = this.rc_lp(this); +); + +samps.rc_set(0.33); + +@slider +feedback = _feedback / 100; +lfactor = spread > 0 ? 1 - spread / 100 : 1; +rfactor = spread < 0 ? 1 + spread / 100 : 1; + +lp_l.rbj_lp(lp_freq, lp_q); +lp_r.rbj_lp(lp_freq, lp_q); +hp_l.rbj_hp(hp_freq, hp_q); +hp_r.rbj_hp(hp_freq, hp_q); + +dry = _dry / 100; +wet = _wet / 100; + +@block +while (midirecv(offset, msg1, note, vel)) ( + event = msg1 & 0xF0; + event == 0xB0 && note == 123 ? ( // All notes off + memset(d0.buf, 0, d0.size); + memset(d1.buf, 0, d1.size); + ); +); + +sync > 0 ? ( + sync == 1 ? delayms = 60000 / 4 / tempo: // 1/16 + sync == 2 ? delayms = 60000 / 2 / tempo: // 1/8 + sync == 3 ? delayms = 60000 / tempo: // 1/4 + sync == 4 ? delayms = 60000 * 2 / tempo: // 1/2 + sync == 5 ? delayms = 60000 * 4 / tempo: // 1/1 + sync == 6 ? delayms = 60000 / 6 / tempo: // 1/16t + sync == 7 ? delayms = 60000 / 3 / tempo: // 1/8t + sync == 8 ? delayms = 60000 / 3 * 2 / tempo: // 1/4t + sync == 9 ? delayms = 60000 / 3 * 4 / tempo: // 1/2t + sync == 10 ? delayms = 60000 / 3 * 8 / tempo: // 1/1t + sync == 11 ? delayms = 60000 / 4 * 1.5 / tempo: // 1/16. + sync == 12 ? delayms = 60000 / 2 * 1.5 / tempo: // 1/8. + sync == 13 ? delayms = 60000 * 1.5 / tempo: // 1/4. + sync == 14 ? delayms = 60000 * 2 * 1.5 / tempo: // 1/2. + sync == 15 ? delayms = 60000 * 4 * 1.5 / tempo: // 1/1. + 1; +); + +delayms > 10000 ? delayms = 10000; +samps = delayms * 0.001 * srate; // delay time in samples + +@sample + +play_state & 1 && !(lplay_state & 1) ? ( + samps.smooth = samps; +); +lplay_state = play_state; + +samps.smooth(); + +// modulation sinewave +mod_amp ? ( + cycle += mod_freq / srate; + cycle > 1 ? cycle -= 1; + wave = sine_wave(cycle); +) : ( + wave = 0; +); + +// read with cubic interpolation +val0 = d0.delay_read3(samps.smooth + wave * mod_amp); +val1 = d1.delay_read3(samps.smooth + wave * mod_amp); + +// apply filters on delay signal +lp_freq < 20000 ? ( + val0 = lp_l.rbj_df1(val0); + val1 = lp_r.rbj_df1(val1); +); +hp_freq > 20 ? ( + val0 = hp_l.rbj_df1(val0); + val1 = hp_r.rbj_df1(val1); +); + +// write values in delay lines +mode == 0 ? ( + d0.delay_write(spl0 + val0 * feedback); + d1.delay_write(spl1 + val1 * feedback); +) : mode == 1 ? ( + d0.delay_write(spl0 * lfactor + val1 * feedback); + d1.delay_write(spl1 * rfactor + val0 * feedback); +); + +// sum and output +spl0 = spl0*dry + val0*wet; +spl1 = spl1*dry + val1*wet; diff --git a/Reverb/tilr_QuickDelay/qd.delay.jsfx-inc b/Reverb/tilr_QuickDelay/qd.delay.jsfx-inc new file mode 100644 index 00000000..39dd3c4a --- /dev/null +++ b/Reverb/tilr_QuickDelay/qd.delay.jsfx-inc @@ -0,0 +1,635 @@ +///////////////////////////////////////////////////////////////// +// Copyright (c) - 2014 - Olivier Belanger // +// Permission to use, copy, modify, and/or distribute this // +// software for any purpose with or without fee is hereby // +// granted, provided that the above copyright notice and this // +// permission notice appear in all copies. CookDSP IS PROVIDED // +// "AS IS", WITHOUT WARRANTY OF ANY KIND FROM THE AUTHOR. // +///////////////////////////////////////////////////////////////// + +@init + +function memalloc(size) +( + __memory_index = __memory_next + 8; + __memory_next = __memory_index + size; + memset(__memory_index, 0.0, size); + __memory_index; +); + +function memalloc(size, noreinit) +( + noreinit == 0 ? ( + __memory_index = __memory_next + 8; + __memory_next = __memory_index + size; + memset(__memory_index, 0.0, size); + ) : ( + __memory_index = __memory_next + 8; + __memory_next = __memory_index + size; + ); + __memory_index; +); + +/******************************************************* +DELAY +===== + +Initialize a delay line for writing and reading audio samples. + +Description +----------- + +A delay object reserves a region of the local memory buffer +and initializes a delay line using this buffer. Several +interpolation algorithms can be used to read the samples +stored in the delay line. + +It must be used with a variable name as prefix (object oriented). + +Initializer +----------- + +#### delay(size) #### + +Initializes the delay line with a buffer of length `size`, in samples. + +Methods +------- + +#### delay_write(signal) #### + +Writes a sample value, `signal`, at the current position in the +delay's buffer. This function also increments the current position. + +#### delay_read(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function checks for delay's validity. + +#### delay_fread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function is faster than `delay_read` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### delay_read2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### delay_fread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `delay_read2` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### delay_read3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### delay_fread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `delay_read3` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### delay_sread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function checks for delay's validity. + +#### delay_fsread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function is faster than `delay_sread` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### delay_sread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### delay_fsread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `delay_sread2` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### delay_sread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### delay_fsread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `delay_sread3` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +Example +------- + + desc:Stereo Delay + Feedback + + slider1:250<1,1000>Delay Time (ms) + slider2:0<-1,1>Feedback + + import cookdsp.jsfx-inc + + @init + // one second stereo delay line + d0.delay(srate); + d1.delay(srate); + + @slider + // delay time in samples + samps = slider1 * 0.001 * srate; + + @sample + // read with cubic interpolation + val0 = d0.delay_read3(samps); + val1 = d1.delay_read3(samps); + // write values in delay lines + d0.delay_write(spl0 + val0 * slider2); + d1.delay_write(spl1 + val1 * slider2); + // sum and output + spl0 = (spl0 + val0) * 0.5; + spl1 = (spl1 + val1) * 0.5; + +********************************************************/ +function delay(size) +( + this.buf = memalloc(size); + this.size = size; + this.curpos = 0; + this.curval = 0; +); + +function delay_write(signal) +( + this.buf[this.curpos] = signal; + this.buf[this.size] = this.buf[0]; + this.buf[this.size+1] = this.buf[1]; + this.buf[-1] = this.buf[this.size-1]; + this.curpos += 1; + this.curpos == this.size ? this.curpos = 0; +); + +function delay_read(delay) +( + delay >= 0 && delay < this.size ? ( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + this.curval = this.buf[floor(pos_)]; + ); + this.curval; +); + +function delay_fread(delay) +( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + this.curval = this.buf[floor(pos_)]; + this.curval; +); + +function delay_sread(delay) +( + this.delay_read(delay * srate); +); + +function delay_fsread(delay) +( + this.delay_fread(delay * srate); +); + +function delay_read2(delay) +( + delay >= 0 && delay < this.size ? ( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + ip_ = floor(pos_); + this.curval = this.buf[ip_] + (this.buf[ip_+1] - this.buf[ip_]) * (pos_ - ip_); + ); + this.curval; +); + +function delay_fread2(delay) +( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + ip_ = floor(pos_); + this.curval = this.buf[ip_] + (this.buf[ip_+1] - this.buf[ip_]) * (pos_ - ip_); + this.curval; +); + +function delay_sread2(delay) +( + this.delay_read2(delay * srate); +); + +function delay_fsread2(delay) +( + this.delay_fread2(delay * srate); +); + +function delay_read3(delay) +( + delay >= 0 && delay < this.size ? ( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + i_ = floor(pos_); + f_ = pos_ - i_; + x0_ = this.buf[i_-1]; x1_ = this.buf[i_]; + x2_ = this.buf[i_+1]; x3_ = this.buf[i_+2]; + a3_ = f_ * f_; a3_ -= 1.0; a3_ *= (1.0 / 6.0); + a2_ = (f_ + 1.0) * 0.5; a0_ = a2_ - 1.0; + a1_ = a3_ * 3.0; a2_ -= a1_; a0_ -= a3_; a1_ -= f_; + a0_ *= f_; a1_ *= f_; a2_ *= f_; a3_ *= f_; a1_ += 1.0; + this.curval = a0_*x0_ + a1_*x1_ + a2_*x2_ + a3_*x3_; + ); + this.curval; +); + +function delay_fread3(delay) +( + pos_ = this.curpos - delay; + pos_ < 0 ? pos_ += this.size; + i_ = floor(pos_); + f_ = pos_ - i_; + x0_ = this.buf[i_-1]; x1_ = this.buf[i_]; + x2_ = this.buf[i_+1]; x3_ = this.buf[i_+2]; + a3_ = f_ * f_; a3_ -= 1.0; a3_ *= (1.0 / 6.0); + a2_ = (f_ + 1.0) * 0.5; a0_ = a2_ - 1.0; + a1_ = a3_ * 3.0; a2_ -= a1_; a0_ -= a3_; a1_ -= f_; + a0_ *= f_; a1_ *= f_; a2_ *= f_; a3_ *= f_; a1_ += 1.0; + this.curval = a0_*x0_ + a1_*x1_ + a2_*x2_ + a3_*x3_; + this.curval; +); + +function delay_sread3(delay) +( + this.delay_read3(delay * srate); +); + +function delay_fsread3(delay) +( + this.delay_fread3(delay * srate); +); + +/******************************************************* +SDELAY +====== + +Initialize an artifact-free delay line for writing and reading audio samples. + +Description +----------- + +A sdelay object reserves a region of the local memory buffer +and initializes an artifact-free delay line using this buffer. +Several interpolation algorithms can be used to read the samples +stored in the delay line. + +The sdelay object implements a delay line that does not produce +clicks or pitch shifting when the delay time is changing. + +It must be used with a variable name as prefix (object oriented). + +Initializer +----------- + +#### sdelay(size, xfade) #### + +Initializes the delay line with a buffer of length `size`, and a +crossfade duration of length `xfade`, both in samples. + +Methods +------- + +#### sdelay_set_xfade(xfade) #### + +Sets a new crossfade time, `xfade`, in samples. + +#### sdelay_write(signal) #### + +Writes a sample value, `signal`, at the current position in the +delay's buffer. This function also increments the current position. + +#### sdelay_read(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function checks for delay's validity. + +#### sdelay_fread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function is faster than `sdelay_read` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### sdelay_read2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### sdelay_fread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `sdelay_read2` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### sdelay_read3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### sdelay_fread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in samples). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `sdelay_read3` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### sdelay_sread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function checks for delay's validity. + +#### sdelay_fsread(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses no interpolation, ie. the position +is truncated to the biggest integer not greater than the real delayed +position. + +This function is faster than `sdelay_sread` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### sdelay_sread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### sdelay_fsread2(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a linear interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `sdelay_sread2` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +#### sdelay_sread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function checks for delay's validity. + +#### sdelay_fsread3(delay) #### + +Returns the value at the delayed position given in argument `delay` +(in seconds). This function uses a cubic interpolation to read the +"true" value for a floating-point sample position. + +This function is faster than `sdelay_sread3` because there is no check +for delay's validity. Use it only if `delay` is known to be valid. + +Example +------- + + desc:Smooth Stereo Delay + Feedback + + slider1:250<1,1000>Delay Time (ms) + slider2:0<-1,1>Feedback + slider3:100<1,250>Crossfade Time (ms) + + import cookdsp.jsfx-inc + + @init + // one second stereo delay line + xfade = slider3 * 0.001 * srate; + d0.sdelay(srate, xfade); + d1.sdelay(srate, xfade); + + @slider + // delay time in samples + samps = slider1 * 0.001 * srate; + xfade = slider3 * 0.001 * srate; + d0.sdelay_set_xfade(xfade); + d1.sdelay_set_xfade(xfade); + + @sample + // read with cubic interpolation + val0 = d0.sdelay_read2(samps); + val1 = d1.sdelay_read2(samps); + // write values in delay lines + d0.sdelay_write(spl0 + val0 * slider2); + d1.sdelay_write(spl1 + val1 * slider2); + // sum and output + spl0 = (spl0 + val0) * 0.5; + spl1 = (spl1 + val1) * 0.5; + +********************************************************/ +function _sdelay_compute_time(delay) +( + this.timer == 0 ? ( + this.current = (this.current + 1) % 2; + this.sampdel = floor(delay + 0.5); + this.xfade = floor(this.xfade + 0.5); + this.xfade > this.sampdel ? this.xfade = this.sampdel; + this.xfade <= 0 ? this.xfade = 1; + this.current == 0 ? ( + this.sampdel1 = delay; + this.inc1 = 1.0 / this.xfade; + this.inc2 = -this.inc1; + ) : ( + this.sampdel2 = delay; + this.inc2 = 1.0 / this.xfade; + this.inc1 = -this.inc2; + ); + ); + this.timer += 1; + this.timer == this.sampdel ? this.timer = 0; +); + +function _sdelay_update_amps() +( + this.amp1 += this.inc1; + this.amp1 < 0 ? this.amp1 = 0.0 : this.amp1 > 1 ? this.amp1 = 1.0; + this.amp2 += this.inc2; + this.amp2 < 0 ? this.amp2 = 0.0 : this.amp2 > 1 ? this.amp2 = 1.0; +); + +function sdelay(size xfade) +( + this.delay.delay(size); + this.xfade = xfade; + this.current = 1; + this.timer = 0; + this.amp1 = 0.0; + this.amp2 = 1.0; + this.inc1 = this.inc2 = 0.0; +); + +function sdelay_write(signal) +( + this.delay.delay_write(signal); +); + +function sdelay_set_xfade(xfade) +( + this.xfade = xfade; +); + +function sdelay_read(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_read(this.sampdel1) * this.amp1; + val_ += this.delay.delay_read(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_fread(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_fread(this.sampdel1) * this.amp1; + val_ += this.delay.delay_fread(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_sread(delay) +( + this.sdelay_read(delay * srate); +); + +function sdelay_fsread(delay) +( + this.sdelay_fread(delay * srate); +); + +function sdelay_read2(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_read2(this.sampdel1) * this.amp1; + val_ += this.delay.delay_read2(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_fread2(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_fread2(this.sampdel1) * this.amp1; + val_ += this.delay.delay_fread2(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_sread2(delay) +( + this.sdelay_read2(delay * srate); +); + +function sdelay_fsread2(delay) +( + this.sdelay_fread2(delay * srate); +); + +function sdelay_read3(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_read3(this.sampdel1) * this.amp1; + val_ += this.delay.delay_read3(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_fread3(delay) +( + this._sdelay_compute_time(delay); + val_ = this.delay.delay_fread3(this.sampdel1) * this.amp1; + val_ += this.delay.delay_fread3(this.sampdel2) * this.amp2; + this._sdelay_update_amps(); + val_; +); + +function sdelay_sread3(delay) +( + this.sdelay_read3(delay * srate); +); + +function sdelay_fsread3(delay) +( + this.sdelay_fread3(delay * srate); +); diff --git a/Reverb/tilr_QuickDelay/qd.rbj_filter.jsfx-inc b/Reverb/tilr_QuickDelay/qd.rbj_filter.jsfx-inc new file mode 100644 index 00000000..fd34ccee --- /dev/null +++ b/Reverb/tilr_QuickDelay/qd.rbj_filter.jsfx-inc @@ -0,0 +1,431 @@ +desc:2nd-order RBJ filter + +// Copyright (C) 2012-2022 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. + +// Based on "Cookbook formulae for audio EQ biquad filter coefficients" by +// Robert Bristow-Johnson. +// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + +/* Example + + desc:Low-pass filter + + slider1:1000<20,20000,1>Cutoff (Hz) + slider2:0.5<0.01,4.0,0.01>Q + + import Tale/rbj_filter.jsfx-inc + + @slider + lp.rbj_lp(slider1, slider2); + lp.rbj_gain(0.5); + + @sample + spl0 = spl1 = lp.rbj_df1(spl0 + spl1); + + Setting Functions + + * rbj_lp(freq, q) -- Low-pass + * rbj_hp(freq, q) -- High-pass + * rbj_bp(freq, q) -- Band-pass (constant skirt gain) + * rbj_bp2(freq, q) -- Band-pass (constant peak gain) + * rbj_bs(freq, q) -- Band-stop + * rbj_ap(freq, q) -- All-pass + * rbj_eq(freq, q, gain) -- Peaking EQ + * rbj_ls(freq, q, gain) -- Low-shelf + * rbj_hs(freq, q, gain) -- High-shelf + Example: lp.rbj_lp(1000, 0.5); + Sets up the filter for the specified cutoff frequency (in Hz), and Q + and gain factors, and returns the a0 coefficient. + + (To convert from dB to gain: gain=10^(db/20).) + + * rbj_gain(gain) + * rbj_dry_wet(dry, wet) + Example: lp.rbj_lp(1000, 0.5); lp.rbj_gain(-2.0); + Example: lp.rbj_lp(1000, 0.5); lp.rbj_dry_wet(0.1, 0.9); + Modifies the filter by applying the specified output gain or dry/wet + mix. + + 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 and dry/wet values, and so you will have to modify them again. + + Filter Functions + + * rbj_df1(sample) -- Direct Form 1 + * rbj_df2(sample) -- Direct Form 2 + * rbj_tdf2(sample) -- Transposed Direct Form 2 + Example: output = lp.rbj_tdf2(input); + Sends a sample through the filter, and returns its output. + + Miscellaneous Functions + + * rbj_reset_df1([input]) -- Direct Form 1 + * rbj_reset_df2([input]) -- Direct Form 2 + * rbj_reset_tdf2([input]) -- Transposed Direct Form 2 + Example: lp.rbj_reset_tdf2(); + Resets the filter state to the specified input value, or to zero if + the value is omitted. + + * rbj_bwtoq(bw) + * rbj_qtobw(q) + Example: q = rbj_bwtoq(2.0); + Converts bandwidth (in octaves) to Q factor, or vice versa. + + Instance Variables + + * a1 + * a2 + * b0 + * b1 + * b2 + Example: lp2.a1 = lp1.a1; lp2.a2 = lp1.a2; lp2.b0 = lp1.b0; lp2.b1 = lp1.b1; lp2.b2 = lp1.b2; + Filter coefficients. + + Note: The first coefficient (a0) is not included here, because all + coefficients are scaled (i.e. divided) by a0, after which a0 itself + would always be 1. The setting functions return the original a0 value, + should you need it (e.g. to get the original, non-scaled + coefficients). + + * x0 + * x1 + * y0 + * y1 + Example: current_input = lp.x0; + Example: previous_output = lp.y1; + Direct Form 1 inputs/outputs. + + * w0 + * w1 + Example: lp2.w0 = lp1.w0; lp2.w1 = lp1.w1; + Direct Form 2 filter state. + + * s0 + * s1 + Example: lp2.s0 = lp1.s0; lp2.s1 = lp1.s1; + Transposed Direct Form 2 filter state. + +*/ + +@init + +function rbj_bwtoq(bw) + local(x) +( + // q = 1/(2 * sinh(log(2) / 2 * bw)) + x = exp(0.5*log(2) * bw); + x/(sqr(x) - 1); +); + +function rbj_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)); +); + +// Low-pass + +function rbj_lp(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + a1 = cos(w0) * -2 * scale; + a2 = (1 - alpha) * scale; + + b2 = b0 = (1 + a1 + a2) * 0.25; + b1 = b0 * 2; + + a0; +); + +// High-pass + +function rbj_hp(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + a1 = cos(w0) * -2 * scale; + a2 = (1 - alpha) * scale; + + b2 = b0 = (1 - a1 + a2) * 0.25; + b1 = b0 * -2; + + a0; +); + +// Band-pass (constant skirt gain, peak gain = Q) + +function rbj_bp(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + a1 = cos(w0) * -2 * scale; + a2 = (1 - alpha) * scale; + + b2 = -(b0 = (1 - a2) * 0.5 * q); + b1 = 0; + + a0; +); + +// Band-pass (constant 0 dB peak gain) + +function rbj_bp2(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + a1 = cos(w0) * -2 * scale; + a2 = (1 - alpha) * scale; + + b2 = -(b0 = (1 - a2) * 0.5); + b1 = 0; + + a0; +); + +// Band-stop + +function rbj_bs(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + a1 = cos(w0) * -2 * scale; + a2 = (1 - alpha) * scale; + + b2 = b0 = scale; + b1 = a1; + + a0; +); + +// All-pass + +function rbj_ap(freq, q) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + + scale = 1/(a0 = 1 + alpha); + b1 = a1 = cos(w0) * -2 * scale; + b0 = a2 = (1 - alpha) * scale; + + b2 = 1; + + a0; +); + +// Peaking EQ + +function rbj_eq(freq, q, gain) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, alpha, a, tmp, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + alpha = sin(w0) / (2*q); + a = sqrt(gain); + + tmp = alpha / a; + + scale = 1/(a0 = 1 + tmp); + b1 = a1 = cos(w0) * -2 * scale; + a2 = (1 - tmp) * scale; + + tmp = alpha * a * scale; + + b0 = scale + tmp; + b2 = scale - tmp; + + a0; +); + +// Low-shelf + +function rbj_ls(freq, q, gain) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, cos_w0, a, tmp0, tmp1, tmp2, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + cos_w0 = cos(w0); + a = sqrt(gain); + + tmp0 = (a - 1) * cos_w0 + a + 1; + tmp1 = sqrt(a) * sin(w0) / q; + tmp2 = (a + 1) * cos_w0; + + scale = 1/(a0 = tmp0 + tmp1); + a1 = (1 - a - tmp2) * 2 * scale; + a2 = (tmp0 - tmp1) * scale; + + tmp0 = (1 - a) * cos_w0 + a + 1; + scale *= a; + + b0 = (tmp0 + tmp1) * scale; + b1 = (a - 1 - tmp2) * 2 * scale; + b2 = (tmp0 - tmp1) * scale; + + a0; +); + +// High-shelf + +function rbj_hs(freq, q, gain) + // global(srate) + instance(a1, a2, b0, b1, b2) + local(w0, cos_w0, a, tmp0, tmp1, tmp2, a0, scale) +( + w0 = 2*$pi * min(freq / srate, 0.49); + cos_w0 = cos(w0); + a = sqrt(gain); + + tmp0 = (1 - a) * cos_w0 + a + 1; + tmp1 = sqrt(a) * sin(w0) / q; + tmp2 = (a + 1) * cos_w0; + + scale = 1/(a0 = tmp0 + tmp1); + a1 = (a - 1 - tmp2) * 2 * scale; + a2 = (tmp0 - tmp1) * scale; + + tmp0 = (a - 1) * cos_w0 + a + 1; + scale *= a; + + b0 = (tmp0 + tmp1) * scale; + b1 = (1 - a - tmp2) * 2 * scale; + b2 = (tmp0 - tmp1) * scale; + + a0; +); + +function rbj_gain(gain) + instance(b0, b1, b2) +( + b0 *= gain; + b1 *= gain; + b2 *= gain; +); + +function rbj_dry_wet(dry, wet) + instance(a1, a2, b0, b1, b2) +( + b0 = b0 * wet + dry; + b1 = b1 * wet + a1 * dry; + b2 = b2 * wet + a2 * dry; +); + +// Direct Form 1 + +function rbj_df1(sample) + instance(a1, a2, b0, b1, b2, x0, x1, y0, y1) + local(x2, y2) +( + x2 = x1; + x1 = x0; + x0 = sample; + + y2 = y1; + y1 = y0; + y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2; +); + +function rbj_reset_df1(input) + instance(a1, a2, b0, b1, b2, x0, x1, y0, y1) +( + x0 = x1 = input; + y0 = y1 = input / (1 + a1 + a2) * (b0 + b1 + b2); +); + +// Direct Form 2 + +function rbj_df2(sample) + instance(a1, a2, b0, b1, b2, w0, w1) + local(w2) +( + w2 = w1; + w1 = w0; + w0 = sample - a1*w1 - a2*w2; + + b0*w0 + b1*w1 + b2*w2; +); + +function rbj_reset_df2(input) + instance(a1, a2, w0, w1) +( + w0 = w1 = input / (1 + a1 + a2); +); + +// Transposed Direct Form 2 + +function rbj_tdf2(sample) + instance(a1, a2, b0, b1, b2, s0, s1) + local(y) +( + y = b0 * sample + s0; + + s0 = b1 * sample - a1*y + s1; + s1 = b2 * sample - a2*y; + + y; +); + +function rbj_reset_tdf2(input) + instance(a1, a2, b0, b1, b2, s0, s1) +( + s0 = ((b0 + b1 + b2) / (1 + a1 + a2) - b0) * input; + s1 = b2 * input - a2 * (b0 * input + s0); +); + +// Legacy + +function rbj_notch(freq, q) ( this.rbj_bs(freq, q) ); +function rbj_peak(freq, q, db_gain) ( this.rbj_eq(freq, q, exp(/* log(10)/20 */ 0.11512925464970228 * db_gain)) ); +function rbj_low_shelf(freq, q, db_gain) ( this.rbj_ls(freq, q, exp(0.11512925464970228 * db_gain)) ); +function rbj_high_shelf(freq, q, db_gain) ( this.rbj_hs(freq, q, exp(0.11512925464970228 * db_gain)) ); + +function rbj_bypass() instance(a1, a2, b0, b1, b2) ( a1 = a2 = b1 = b2 = 0; b0 = 1; ); +function rbj_mute() instance(a1, a2, b0, b1, b2) ( a1 = a2 = b0 = b1 = b2 = 0; ); + +function rbj_reset1(input) ( this.rbj_reset_df1(input) ); +function rbj_reset2(input) ( this.rbj_reset_df2(input) ); + +function rbj_scale(a0) + instance(a1, a2, b0, b1, b2) + local(scale) +( + a1 *= (scale = 1/a0); a2 *= scale; b0 *= scale; b1 *= scale; b2 *= scale; + a0; +);