From 2a731549b45d7c709f247e6725c35e7f6c0a2419 Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 13:42:03 +0200 Subject: [PATCH 1/5] feat: chorus --- packages/chorus/CHANGELOG.md | 5 + packages/chorus/README.md | 5 + packages/chorus/dsp/chorus.dsp | 35 ++ packages/chorus/dsp/chorus.rs | 624 +++++++++++++++++++++++++++++++ packages/chorus/dsp/compie.sh | 1 + packages/chorus/package.json | 35 ++ packages/chorus/src/_worklet.ts | 119 ++++++ packages/chorus/src/dsp.ts | 306 +++++++++++++++ packages/chorus/src/index.ts | 29 ++ packages/chorus/src/processor.ts | 1 + packages/chorus/src/worklet.ts | 47 +++ 11 files changed, 1207 insertions(+) create mode 100644 packages/chorus/CHANGELOG.md create mode 100644 packages/chorus/README.md create mode 100644 packages/chorus/dsp/chorus.dsp create mode 100644 packages/chorus/dsp/chorus.rs create mode 100755 packages/chorus/dsp/compie.sh create mode 100644 packages/chorus/package.json create mode 100644 packages/chorus/src/_worklet.ts create mode 100644 packages/chorus/src/dsp.ts create mode 100644 packages/chorus/src/index.ts create mode 100644 packages/chorus/src/processor.ts create mode 100644 packages/chorus/src/worklet.ts diff --git a/packages/chorus/CHANGELOG.md b/packages/chorus/CHANGELOG.md new file mode 100644 index 0000000..fe9f342 --- /dev/null +++ b/packages/chorus/CHANGELOG.md @@ -0,0 +1,5 @@ +# @synthlet/chorus + +## 0.1.0 + +- Initial release diff --git a/packages/chorus/README.md b/packages/chorus/README.md new file mode 100644 index 0000000..251fe7f --- /dev/null +++ b/packages/chorus/README.md @@ -0,0 +1,5 @@ +# @synthlet/chorus + +> Chorus mono effect written in Faust and ported to the web + +Part of [Synthlet](https://github.com/danigb/synthlet) diff --git a/packages/chorus/dsp/chorus.dsp b/packages/chorus/dsp/chorus.dsp new file mode 100644 index 0000000..3ef6975 --- /dev/null +++ b/packages/chorus/dsp/chorus.dsp @@ -0,0 +1,35 @@ + +import("stdfaust.lib"); + +voices = 8; // MUST BE EVEN +process = ba.bypass1to2(cbp,chorus_mono(dmax,curdel,rate,sigma,do2,voices)); + +dmax = 4096; // 8192; +curdel = dmax * vslider("[0] Delay [midi:ctrl 4] [style:knob]", 0.5, 0, 1, 1) : si.smooth(0.999); +rateMax = 7.0; // Hz +rateMin = 0.01; +rateT60 = 0.15661; +rate = vslider("[1] Rate [midi:ctrl 2] [unit:Hz] [style:knob]", 0.5, rateMin, rateMax, 0.0001) + : si.smooth(ba.tau2pole(rateT60/6.91)); + + depth = vslider("[4] Depth [midi:ctrl 3] [style:knob]", 0.5, 0, 1, 0.001) : si.smooth(ba.tau2pole(depthT60/6.91)); + +depthT60 = 0.15661; +delayPerVoice = 0.5*curdel/voices; +sigma = delayPerVoice * vslider("[6] Deviation [midi:ctrl 58] [style:knob]",0.5,0,1,0.001) : si.smooth(0.999); + +periodic = 1; + +do2 = depth; // use when depth=1 means "multivibrato" effect (no original => all are modulated) +cbp = 1-int(vslider("[0] Enable [midi:ctrl 105][style:knob]",0,0,1,1)); + +chorus_mono(dmax,curdel,rate,sigma,do2,voices) + = _ <: (*(1-do2)<:_,_),(*(do2) <: par(i,voices,voice(i)) :> _,_) : ro.interleave(2,2) : +,+ + with { + angle(i) = 2*ma.PI*(i/2)/voices + (i%2)*ma.PI/2; + voice(i) = de.fdelay(dmax,min(dmax,del(i))) * cos(angle(i)); + del(i) = curdel*(i+1)/voices + dev(i); + rates(i) = rate/float(i+1); + dev(i) = sigma * os.oscp(rates(i),i*2*ma.PI/voices); + }; + diff --git a/packages/chorus/dsp/chorus.rs b/packages/chorus/dsp/chorus.rs new file mode 100644 index 0000000..6e027f5 --- /dev/null +++ b/packages/chorus/dsp/chorus.rs @@ -0,0 +1,624 @@ +/* ------------------------------------------------------------ +name: "chorus" +Code generated with Faust 2.72.14 (https://faust.grame.fr) +Compilation options: -lang rust -ct 1 -cn Chorus -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0 +------------------------------------------------------------ */ + +pub struct ChorusSIG0 { + iVec2: [i32; 2], + iRec3: [i32; 2], +} + +impl ChorusSIG0 { + fn get_num_inputsChorusSIG0(&self) -> i32 { + return 0; + } + fn get_num_outputsChorusSIG0(&self) -> i32 { + return 1; + } + + fn instance_initChorusSIG0(&mut self, sample_rate: i32) { + for l5 in 0..2 { + self.iVec2[l5 as usize] = 0; + } + for l6 in 0..2 { + self.iRec3[l6 as usize] = 0; + } + } + + fn fillChorusSIG0(&mut self, count: i32, table: &mut [F32]) { + for i1 in 0..count { + self.iVec2[0] = 1; + self.iRec3[0] = (i32::wrapping_add(self.iVec2[1], self.iRec3[1])) % 65536; + table[i1 as usize] = F32::cos(9.58738e-05 * (self.iRec3[0]) as F32); + self.iVec2[1] = self.iVec2[0]; + self.iRec3[1] = self.iRec3[0]; + } + } +} + +pub fn newChorusSIG0() -> ChorusSIG0 { + ChorusSIG0 { + iVec2: [0; 2], + iRec3: [0; 2], + } +} + +pub struct ChorusSIG1 { + iVec3: [i32; 2], + iRec6: [i32; 2], +} + +impl ChorusSIG1 { + fn get_num_inputsChorusSIG1(&self) -> i32 { + return 0; + } + fn get_num_outputsChorusSIG1(&self) -> i32 { + return 1; + } + + fn instance_initChorusSIG1(&mut self, sample_rate: i32) { + for l9 in 0..2 { + self.iVec3[l9 as usize] = 0; + } + for l10 in 0..2 { + self.iRec6[l10 as usize] = 0; + } + } + + fn fillChorusSIG1(&mut self, count: i32, table: &mut [F32]) { + for i2 in 0..count { + self.iVec3[0] = 1; + self.iRec6[0] = (i32::wrapping_add(self.iVec3[1], self.iRec6[1])) % 65536; + table[i2 as usize] = F32::sin(9.58738e-05 * (self.iRec6[0]) as F32); + self.iVec3[1] = self.iVec3[0]; + self.iRec6[1] = self.iRec6[0]; + } + } +} + +pub fn newChorusSIG1() -> ChorusSIG1 { + ChorusSIG1 { + iVec3: [0; 2], + iRec6: [0; 2], + } +} +static mut ftbl0ChorusSIG0: [F32; 65536] = [0.0; 65536]; +static mut ftbl1ChorusSIG1: [F32; 65536] = [0.0; 65536]; +mod ffi { + use std::os::raw::c_float; + #[link(name = "m")] + extern "C" { + pub fn remainderf(from: c_float, to: c_float) -> c_float; + pub fn rintf(val: c_float) -> c_float; + } +} +fn remainder_f32(from: f32, to: f32) -> f32 { + unsafe { ffi::remainderf(from, to) } +} +fn rint_f32(val: f32) -> f32 { + unsafe { ffi::rintf(val) } +} + +#[cfg_attr(feature = "default-boxed", derive(default_boxed::DefaultBoxed))] +#[repr(C)] +pub struct Chorus { + iVec0: [i32; 2], + fVslider0: F32, + fSampleRate: i32, + fConst0: F32, + fConst1: F32, + fConst2: F32, + fVslider1: F32, + fRec0: [F32; 2], + IOTA0: i32, + fVec1: [F32; 8192], + fVslider2: F32, + fRec1: [F32; 2], + fVslider3: F32, + fRec2: [F32; 2], + fConst3: F32, + fVslider4: F32, + fRec5: [F32; 2], + fRec4: [F32; 2], + fConst4: F32, + fRec7: [F32; 2], + fConst5: F32, + fRec8: [F32; 2], + fConst6: F32, + fRec9: [F32; 2], + fConst7: F32, + fRec10: [F32; 2], + fConst8: F32, + fRec11: [F32; 2], + fConst9: F32, + fRec12: [F32; 2], +} + +impl FaustDsp for Chorus { + type T = F32; + + fn new() -> Chorus { + Chorus { + iVec0: [0; 2], + fVslider0: 0.0, + fSampleRate: 0, + fConst0: 0.0, + fConst1: 0.0, + fConst2: 0.0, + fVslider1: 0.0, + fRec0: [0.0; 2], + IOTA0: 0, + fVec1: [0.0; 8192], + fVslider2: 0.0, + fRec1: [0.0; 2], + fVslider3: 0.0, + fRec2: [0.0; 2], + fConst3: 0.0, + fVslider4: 0.0, + fRec5: [0.0; 2], + fRec4: [0.0; 2], + fConst4: 0.0, + fRec7: [0.0; 2], + fConst5: 0.0, + fRec8: [0.0; 2], + fConst6: 0.0, + fRec9: [0.0; 2], + fConst7: 0.0, + fRec10: [0.0; 2], + fConst8: 0.0, + fRec11: [0.0; 2], + fConst9: 0.0, + fRec12: [0.0; 2], + } + } + fn metadata(&self, m: &mut dyn Meta) { + m.declare("basics.lib/bypass1to2:author", r"Julius Smith"); + m.declare("basics.lib/name", r"Faust Basic Element Library"); + m.declare( + "basics.lib/tabulateNd", + r"Copyright (C) 2023 Bart Brouns ", + ); + m.declare("basics.lib/version", r"1.15.0"); + m.declare( + "compile_options", + r"-lang rust -ct 1 -cn Chorus -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0", + ); + m.declare("delays.lib/name", r"Faust Delay Library"); + m.declare("delays.lib/version", r"1.1.0"); + m.declare("filename", r"chorus.dsp"); + m.declare("maths.lib/author", r"GRAME"); + m.declare("maths.lib/copyright", r"GRAME"); + m.declare("maths.lib/license", r"LGPL with exception"); + m.declare("maths.lib/name", r"Faust Math Library"); + m.declare("maths.lib/version", r"2.8.0"); + m.declare("name", r"chorus"); + m.declare("oscillators.lib/name", r"Faust Oscillator Library"); + m.declare("oscillators.lib/version", r"1.5.1"); + m.declare("platform.lib/name", r"Generic Platform Library"); + m.declare("platform.lib/version", r"1.3.0"); + m.declare("routes.lib/name", r"Faust Signal Routing Library"); + m.declare("routes.lib/version", r"1.2.0"); + m.declare("signals.lib/name", r"Faust Signal Routing Library"); + m.declare("signals.lib/version", r"1.5.0"); + } + + fn get_sample_rate(&self) -> i32 { + return self.fSampleRate; + } + fn get_num_inputs(&self) -> i32 { + return 1; + } + fn get_num_outputs(&self) -> i32 { + return 2; + } + + fn class_init(sample_rate: i32) { + let mut sig0: ChorusSIG0 = newChorusSIG0(); + sig0.instance_initChorusSIG0(sample_rate); + sig0.fillChorusSIG0(65536, unsafe { &mut ftbl0ChorusSIG0 }); + let mut sig1: ChorusSIG1 = newChorusSIG1(); + sig1.instance_initChorusSIG1(sample_rate); + sig1.fillChorusSIG1(65536, unsafe { &mut ftbl1ChorusSIG1 }); + } + fn instance_reset_params(&mut self) { + self.fVslider0 = 0.0; + self.fVslider1 = 0.5; + self.fVslider2 = 0.5; + self.fVslider3 = 0.5; + self.fVslider4 = 0.5; + } + fn instance_clear(&mut self) { + for l0 in 0..2 { + self.iVec0[l0 as usize] = 0; + } + for l1 in 0..2 { + self.fRec0[l1 as usize] = 0.0; + } + self.IOTA0 = 0; + for l2 in 0..8192 { + self.fVec1[l2 as usize] = 0.0; + } + for l3 in 0..2 { + self.fRec1[l3 as usize] = 0.0; + } + for l4 in 0..2 { + self.fRec2[l4 as usize] = 0.0; + } + for l7 in 0..2 { + self.fRec5[l7 as usize] = 0.0; + } + for l8 in 0..2 { + self.fRec4[l8 as usize] = 0.0; + } + for l11 in 0..2 { + self.fRec7[l11 as usize] = 0.0; + } + for l12 in 0..2 { + self.fRec8[l12 as usize] = 0.0; + } + for l13 in 0..2 { + self.fRec9[l13 as usize] = 0.0; + } + for l14 in 0..2 { + self.fRec10[l14 as usize] = 0.0; + } + for l15 in 0..2 { + self.fRec11[l15 as usize] = 0.0; + } + for l16 in 0..2 { + self.fRec12[l16 as usize] = 0.0; + } + } + fn instance_constants(&mut self, sample_rate: i32) { + self.fSampleRate = sample_rate; + self.fConst0 = F32::min(1.92e+05, F32::max(1.0, (self.fSampleRate) as F32)); + self.fConst1 = F32::exp(-(44.12234 / self.fConst0)); + self.fConst2 = 1.0 - self.fConst1; + self.fConst3 = 0.33333334 / self.fConst0; + self.fConst4 = 1.0 / self.fConst0; + self.fConst5 = 0.14285715 / self.fConst0; + self.fConst6 = 0.5 / self.fConst0; + self.fConst7 = 0.25 / self.fConst0; + self.fConst8 = 0.16666667 / self.fConst0; + self.fConst9 = 0.125 / self.fConst0; + } + fn instance_init(&mut self, sample_rate: i32) { + self.instance_constants(sample_rate); + self.instance_reset_params(); + self.instance_clear(); + } + fn init(&mut self, sample_rate: i32) { + Chorus::class_init(sample_rate); + self.instance_init(sample_rate); + } + + fn build_user_interface(&self, ui_interface: &mut dyn UI) { + Self::build_user_interface_static(ui_interface); + } + + fn build_user_interface_static(ui_interface: &mut dyn UI) { + ui_interface.open_vertical_box("chorus"); + ui_interface.declare(Some(ParamIndex(0)), "0", ""); + ui_interface.declare(Some(ParamIndex(0)), "midi", "ctrl 4"); + ui_interface.declare(Some(ParamIndex(0)), "style", "knob"); + ui_interface.add_vertical_slider("Delay", ParamIndex(0), 0.5, 0.0, 1.0, 1.0); + ui_interface.declare(Some(ParamIndex(1)), "0", ""); + ui_interface.declare(Some(ParamIndex(1)), "midi", "ctrl 105"); + ui_interface.declare(Some(ParamIndex(1)), "style", "knob"); + ui_interface.add_vertical_slider("Enable", ParamIndex(1), 0.0, 0.0, 1.0, 1.0); + ui_interface.declare(Some(ParamIndex(2)), "1", ""); + ui_interface.declare(Some(ParamIndex(2)), "midi", "ctrl 2"); + ui_interface.declare(Some(ParamIndex(2)), "style", "knob"); + ui_interface.declare(Some(ParamIndex(2)), "unit", "Hz"); + ui_interface.add_vertical_slider("Rate", ParamIndex(2), 0.5, 0.01, 7.0, 0.0001); + ui_interface.declare(Some(ParamIndex(3)), "4", ""); + ui_interface.declare(Some(ParamIndex(3)), "midi", "ctrl 3"); + ui_interface.declare(Some(ParamIndex(3)), "style", "knob"); + ui_interface.add_vertical_slider("Depth", ParamIndex(3), 0.5, 0.0, 1.0, 0.001); + ui_interface.declare(Some(ParamIndex(4)), "6", ""); + ui_interface.declare(Some(ParamIndex(4)), "midi", "ctrl 58"); + ui_interface.declare(Some(ParamIndex(4)), "style", "knob"); + ui_interface.add_vertical_slider("Deviation", ParamIndex(4), 0.5, 0.0, 1.0, 0.001); + ui_interface.close_box(); + } + + fn get_param(&self, param: ParamIndex) -> Option { + match param.0 { + 1 => Some(self.fVslider0), + 3 => Some(self.fVslider1), + 0 => Some(self.fVslider2), + 4 => Some(self.fVslider3), + 2 => Some(self.fVslider4), + _ => None, + } + } + + fn set_param(&mut self, param: ParamIndex, value: Self::T) { + match param.0 { + 1 => self.fVslider0 = value, + 3 => self.fVslider1 = value, + 0 => self.fVslider2 = value, + 4 => self.fVslider3 = value, + 2 => self.fVslider4 = value, + _ => {} + } + } + + fn compute(&mut self, count: i32, inputs: &[&[Self::T]], outputs: &mut [&mut [Self::T]]) { + let (inputs0) = if let [inputs0, ..] = inputs { + let inputs0 = inputs0[..count as usize].iter(); + (inputs0) + } else { + panic!("wrong number of inputs"); + }; + let (outputs0, outputs1) = if let [outputs0, outputs1, ..] = outputs { + let outputs0 = outputs0[..count as usize].iter_mut(); + let outputs1 = outputs1[..count as usize].iter_mut(); + (outputs0, outputs1) + } else { + panic!("wrong number of outputs"); + }; + let mut iSlow0: i32 = i32::wrapping_sub(1, (self.fVslider0) as i32); + let mut fSlow1: F32 = self.fConst2 * self.fVslider1; + let mut fSlow2: F32 = 4.096 * self.fVslider2; + let mut fSlow3: F32 = 6.25e-05 * self.fVslider3; + let mut fSlow4: F32 = self.fConst2 * self.fVslider4; + let zipped_iterators = inputs0.zip(outputs0).zip(outputs1); + for ((input0, output0), output1) in zipped_iterators { + self.iVec0[0] = 1; + self.fRec0[0] = fSlow1 + self.fConst1 * self.fRec0[1]; + let mut fTemp0: F32 = *input0; + let mut fTemp1: F32 = if iSlow0 != 0 { 0.0 } else { fTemp0 }; + let mut fTemp2: F32 = self.fRec0[0] * fTemp1; + self.fVec1[(self.IOTA0 & 8191) as usize] = fTemp2; + self.fRec1[0] = fSlow2 + 0.999 * self.fRec1[1]; + self.fRec2[0] = fSlow3 * self.fRec1[0] + 0.999 * self.fRec2[1]; + let mut iTemp3: i32 = i32::wrapping_sub(1, self.iVec0[1]); + self.fRec5[0] = fSlow4 + self.fConst1 * self.fRec5[1]; + let mut fTemp4: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec4[1] + self.fConst3 * self.fRec5[0] + }; + self.fRec4[0] = fTemp4 - F32::floor(fTemp4); + let mut fTemp5: F32 = F32::min( + 4096.0, + 0.375 * self.fRec1[0] + + self.fRec2[0] + * unsafe { + ftbl0ChorusSIG0[(std::cmp::max( + 0, + std::cmp::min((65536.0 * self.fRec4[0]) as i32, 65535), + )) as usize] + }, + ); + let mut iTemp6: i32 = (fTemp5) as i32; + let mut fTemp7: F32 = F32::floor(fTemp5); + let mut fTemp8: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec7[1] + self.fConst4 * self.fRec5[0] + }; + self.fRec7[0] = fTemp8 - F32::floor(fTemp8); + let mut fTemp9: F32 = F32::min( + 4096.0, + 0.125 * self.fRec1[0] + + self.fRec2[0] + * unsafe { + ftbl1ChorusSIG1[(std::cmp::max( + 0, + std::cmp::min((65536.0 * self.fRec7[0]) as i32, 65535), + )) as usize] + }, + ); + let mut fTemp10: F32 = F32::floor(fTemp9); + let mut iTemp11: i32 = (fTemp9) as i32; + let mut fTemp12: F32 = fTemp1 * (1.0 - self.fRec0[0]); + let mut fTemp13: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec8[1] + self.fConst5 * self.fRec5[0] + }; + self.fRec8[0] = fTemp13 - F32::floor(fTemp13); + let mut fTemp14: F32 = F32::min( + 4096.0, + 0.875 * self.fRec1[0] + - self.fRec2[0] + * unsafe { + ftbl0ChorusSIG0[(std::cmp::max( + 0, + std::cmp::min((65536.0 * self.fRec8[0]) as i32, 65535), + )) as usize] + }, + ); + let mut iTemp15: i32 = (fTemp14) as i32; + let mut fTemp16: F32 = F32::floor(fTemp14); + *output0 = if iSlow0 != 0 { + fTemp0 + } else { + 0.70710677 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp6)), + )) & 8191) as usize] + * (fTemp7 + (1.0 - fTemp5)) + + (fTemp5 - fTemp7) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp6, 1))), + )) & 8191) as usize]) + + (fTemp9 - fTemp10) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp11, 1))), + )) & 8191) as usize] + + fTemp12 + + self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp11)), + )) & 8191) as usize] + * (fTemp10 + (1.0 - fTemp9)) + - 0.70710677 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp15)), + )) & 8191) as usize] + * (fTemp16 + (1.0 - fTemp14)) + + (fTemp14 - fTemp16) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp15, 1)), + ), + )) & 8191) as usize]) + }; + let mut fTemp17: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec9[1] + self.fConst6 * self.fRec5[0] + }; + self.fRec9[0] = fTemp17 - F32::floor(fTemp17); + let mut iTemp18: i32 = + std::cmp::max(0, std::cmp::min((65536.0 * self.fRec9[0]) as i32, 65535)); + let mut fTemp19: F32 = F32::min( + 4096.0, + 0.25 * self.fRec1[0] + + self.fRec2[0] + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp18 as usize] } + + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp18 as usize] }), + ); + let mut iTemp20: i32 = (fTemp19) as i32; + let mut fTemp21: F32 = F32::floor(fTemp19); + let mut fTemp22: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec10[1] + self.fConst7 * self.fRec5[0] + }; + self.fRec10[0] = fTemp22 - F32::floor(fTemp22); + let mut iTemp23: i32 = + std::cmp::max(0, std::cmp::min((65536.0 * self.fRec10[0]) as i32, 65535)); + let mut fTemp24: F32 = F32::min( + 4096.0, + 0.5 * self.fRec1[0] + + self.fRec2[0] + * (0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp23 as usize] } + - 0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp23 as usize] }), + ); + let mut iTemp25: i32 = (fTemp24) as i32; + let mut fTemp26: F32 = F32::floor(fTemp24); + let mut fTemp27: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec11[1] + self.fConst8 * self.fRec5[0] + }; + self.fRec11[0] = fTemp27 - F32::floor(fTemp27); + let mut iTemp28: i32 = + std::cmp::max(0, std::cmp::min((65536.0 * self.fRec11[0]) as i32, 65535)); + let mut fTemp29: F32 = F32::min( + 4096.0, + 0.75 * self.fRec1[0] + - self.fRec2[0] + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp28 as usize] } + + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp28 as usize] }), + ); + let mut iTemp30: i32 = (fTemp29) as i32; + let mut fTemp31: F32 = F32::floor(fTemp29); + let mut fTemp32: F32 = if iTemp3 != 0 { + 0.0 + } else { + self.fRec12[1] + self.fConst9 * self.fRec5[0] + }; + self.fRec12[0] = fTemp32 - F32::floor(fTemp32); + let mut iTemp33: i32 = + std::cmp::max(0, std::cmp::min((65536.0 * self.fRec12[0]) as i32, 65535)); + let mut fTemp34: F32 = F32::min( + 4096.0, + self.fRec1[0] + + self.fRec2[0] + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp33 as usize] } + - 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp33 as usize] }), + ); + let mut iTemp35: i32 = (fTemp34) as i32; + let mut fTemp36: F32 = F32::floor(fTemp34); + *output1 = if iSlow0 != 0 { + fTemp0 + } else { + fTemp12 + - (0.38268343 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp20)), + )) & 8191) as usize] + * (fTemp21 + (1.0 - fTemp19)) + + (fTemp19 - fTemp21) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp20, 1)), + ), + )) & 8191) as usize]) + + 0.9238795 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp25)), + )) & 8191) as usize] + * (fTemp26 + (1.0 - fTemp24)) + + (fTemp24 - fTemp26) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp25, 1)), + ), + )) & 8191) + as usize]) + + 0.9238795 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp30)), + )) & 8191) as usize] + * (fTemp31 + (1.0 - fTemp29)) + + (fTemp29 - fTemp31) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp30, 1)), + ), + )) & 8191) + as usize]) + + 0.38268343 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp35)), + )) & 8191) as usize] + * (fTemp36 + (1.0 - fTemp34)) + + (fTemp34 - fTemp36) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp35, 1)), + ), + )) & 8191) + as usize])) + }; + self.iVec0[1] = self.iVec0[0]; + self.fRec0[1] = self.fRec0[0]; + self.IOTA0 = i32::wrapping_add(self.IOTA0, 1); + self.fRec1[1] = self.fRec1[0]; + self.fRec2[1] = self.fRec2[0]; + self.fRec5[1] = self.fRec5[0]; + self.fRec4[1] = self.fRec4[0]; + self.fRec7[1] = self.fRec7[0]; + self.fRec8[1] = self.fRec8[0]; + self.fRec9[1] = self.fRec9[0]; + self.fRec10[1] = self.fRec10[0]; + self.fRec11[1] = self.fRec11[0]; + self.fRec12[1] = self.fRec12[0]; + } + } +} diff --git a/packages/chorus/dsp/compie.sh b/packages/chorus/dsp/compie.sh new file mode 100755 index 0000000..f6b230f --- /dev/null +++ b/packages/chorus/dsp/compie.sh @@ -0,0 +1 @@ +faust -lang rust -cn Chorus chorus.dsp -o chorus.rs \ No newline at end of file diff --git a/packages/chorus/package.json b/packages/chorus/package.json new file mode 100644 index 0000000..52a7e92 --- /dev/null +++ b/packages/chorus/package.json @@ -0,0 +1,35 @@ +{ + "name": "@synthlet/chorus", + "version": "0.0.0", + "description": "Chorus generator audio worklet", + "keywords": [ + "chorus", + "modular", + "synthesis", + "synthlet" + ], + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/jest": "^29.5.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.1" + }, + "jest": { + "preset": "ts-jest" + }, + "scripts": { + "worklet": "esbuild src/worklet.ts --bundle --minify | sed -e 's/^/export const PROCESSOR = \\`/' -e 's/$/\\`;/' > src/processor.ts", + "lib": "tsup src/index.ts --sourcemap --dts --format esm,cjs", + "build": "npm run worklet && npm run lib" + } +} diff --git a/packages/chorus/src/_worklet.ts b/packages/chorus/src/_worklet.ts new file mode 100644 index 0000000..9a26891 --- /dev/null +++ b/packages/chorus/src/_worklet.ts @@ -0,0 +1,119 @@ +// DON'T EDIT THIS FILE unless inside scripts/_worklet.ts +// use ./scripts/copy_files.ts to copy this file to the right place +// the goal is to avoid external dependencies on packages + +// A "Connector" is a function that takes an AudioContext and returns an AudioNode +// or an custom object with a connect method (that returns a disconnect method) +export type Connector = (context: AudioContext) => N; + +export type ParamInput = number | Connector | AudioNode; + +type CreateWorkletOptions = { + processorName: string; + paramNames: readonly string[]; + workletOptions: (params: Partial

) => AudioWorkletNodeOptions; + postCreate?: (node: N) => void; +}; + +export type Disposable = N & { dispose: () => void }; + +export function createWorkletConstructor< + N extends AudioWorkletNode, + P extends Record +>(options: CreateWorkletOptions) { + return ( + audioContext: AudioContext, + inputs: Partial

= {} + ): Disposable => { + const node = new AudioWorkletNode( + audioContext, + options.processorName, + options.workletOptions(inputs) + ) as N; + + (node as any).__PROCESSOR_NAME__ = options.processorName; + const connected = connectParams(node, options.paramNames, inputs); + options.postCreate?.(node); + return disposable(node, connected); + }; +} + +type ConnectedUnit = AudioNode | (() => void); + +export function connectParams( + node: any, + paramNames: readonly string[], + inputs: any +): ConnectedUnit[] { + const connected: ConnectedUnit[] = []; + + for (const paramName of paramNames) { + if (node.parameters) { + node[paramName] = node.parameters.get(paramName); + } + const param = node[paramName]; + if (!param) throw Error("Invalid param name: " + paramName); + const input = inputs[paramName]; + if (typeof input === "number") { + param.value = input; + } else if (input instanceof AudioNode) { + param.value = 0; + input.connect(param); + connected.push(input); + } else if (typeof input === "function") { + param.value = 0; + const source = input(node.context); + source.connect(param); + connected.push(source); + } + } + + return connected; +} + +export function disposable( + node: N, + dependencies?: ConnectedUnit[] +): Disposable { + let disposed = false; + return Object.assign(node, { + dispose() { + if (disposed) return; + disposed = true; + + node.disconnect(); + (node as any).port?.postMessage({ type: "DISPOSE" }); + if (!dependencies) return; + + while (dependencies.length) { + const conn = dependencies.pop(); + if (conn instanceof AudioNode) { + if (typeof (conn as any).dispose === "function") { + (conn as any).dispose?.(); + } else { + conn.disconnect(); + } + } else if (typeof conn === "function") { + conn(); + } + } + }, + }); +} + +export function createRegistrar(processorName: string, processor: string) { + return function (context: AudioContext): Promise { + const key = "__" + processorName + "__"; + if (key in context) return (context as any)[key]; + + if (!context.audioWorklet || !context.audioWorklet.addModule) { + throw Error("AudioWorklet not supported"); + } + + const blob = new Blob([processor], { type: "application/javascript" }); + const url = URL.createObjectURL(blob); + const promise = context.audioWorklet.addModule(url); + (context as any)[key] = promise; + return promise; + }; +} diff --git a/packages/chorus/src/dsp.ts b/packages/chorus/src/dsp.ts new file mode 100644 index 0000000..0a48f22 --- /dev/null +++ b/packages/chorus/src/dsp.ts @@ -0,0 +1,306 @@ +export function createChorus(sampleRate: number) { + const ftbl0ChorusSIG0 = new Float32Array(65536); + const ftbl1ChorusSIG1 = new Float32Array(65536); + + let $iVec0 = new Int32Array(2); + let $fVslider0 = 0.0; + let $fSampleRate = 0; + let $fConst0 = 0.0; + let $fConst1 = 0.0; + let $fConst2 = 0.0; + let $fVslider1 = 0.0; + let $fRec0 = new Float32Array(2); + let $IOTA0 = 0; + let $fVec1 = new Float32Array(8192); + let $fVslider2 = 0.0; + let $fRec1 = new Float32Array(2); + let $fVslider3 = 0.0; + let $fRec2 = new Float32Array(2); + let $fConst3 = 0.0; + let $fVslider4 = 0.0; + let $fRec5 = new Float32Array(2); + let $fRec4 = new Float32Array(2); + let $fConst4 = 0.0; + let $fRec7 = new Float32Array(2); + let $fConst5 = 0.0; + let $fRec8 = new Float32Array(2); + let $fConst6 = 0.0; + let $fRec9 = new Float32Array(2); + let $fConst7 = 0.0; + let $fRec10 = new Float32Array(2); + let $fConst8 = 0.0; + let $fRec11 = new Float32Array(2); + let $fConst9 = 0.0; + let $fRec12 = new Float32Array(2); + + function init(): void { + $fSampleRate = sampleRate; + $fConst0 = Math.min(1.92e5, Math.max(1.0, $fSampleRate)); + $fConst1 = Math.exp(-(44.12234 / $fConst0)); + $fConst2 = 1.0 - $fConst1; + $fConst3 = 0.33333334 / $fConst0; + $fConst4 = 1.0 / $fConst0; + $fConst5 = 0.14285715 / $fConst0; + $fConst6 = 0.5 / $fConst0; + $fConst7 = 0.25 / $fConst0; + $fConst8 = 0.16666667 / $fConst0; + $fConst9 = 0.125 / $fConst0; + fillTable(ftbl0ChorusSIG0, sig0Fn); + fillTable(ftbl1ChorusSIG1, sig1Fn); + } + + function compute( + inputs0: Float32Array, + outputs0: Float32Array, + outputs1: Float32Array + ): void { + let iSlow0: number = 1 - Math.floor($fVslider0); + let fSlow1: number = $fConst2 * $fVslider1; + let fSlow2: number = 4.096 * $fVslider2; + let fSlow3: number = 6.25e-5 * $fVslider3; + let fSlow4: number = $fConst2 * $fVslider4; + + for (let i = 0; i < inputs0.length; i++) { + let input0 = inputs0[i]; + let output0 = outputs0[i]; + let output1 = outputs1[i]; + + $iVec0[0] = 1; + $fRec0[0] = fSlow1 + $fConst1 * $fRec0[1]; + let fTemp0: number = input0; + let fTemp1: number = iSlow0 !== 0 ? 0.0 : fTemp0; + let fTemp2: number = $fRec0[0] * fTemp1; + $fVec1[$IOTA0 & 8191] = fTemp2; + $fRec1[0] = fSlow2 + 0.999 * $fRec1[1]; + $fRec2[0] = fSlow3 * $fRec1[0] + 0.999 * $fRec2[1]; + + let iTemp3: number = 1 - $iVec0[1]; + $fRec5[0] = fSlow4 + $fConst1 * $fRec5[1]; + let fTemp4: number = + iTemp3 !== 0 ? 0.0 : $fRec4[1] + $fConst3 * $fRec5[0]; + $fRec4[0] = fTemp4 - Math.floor(fTemp4); + + let fTemp5: number = Math.min( + 4096.0, + 0.375 * $fRec1[0] + + $fRec2[0] * + ftbl0ChorusSIG0[ + Math.max(0, Math.min(Math.floor(65536.0 * $fRec4[0]), 65535)) + ] + ); + let iTemp6: number = Math.floor(fTemp5); + let fTemp7: number = Math.floor(fTemp5); + + let fTemp8: number = + iTemp3 !== 0 ? 0.0 : $fRec7[1] + $fConst4 * $fRec5[0]; + $fRec7[0] = fTemp8 - Math.floor(fTemp8); + + let fTemp9: number = Math.min( + 4096.0, + 0.125 * $fRec1[0] + + $fRec2[0] * + ftbl1ChorusSIG1[ + Math.max(0, Math.min(Math.floor(65536.0 * $fRec7[0]), 65535)) + ] + ); + let fTemp10: number = Math.floor(fTemp9); + let iTemp11: number = Math.floor(fTemp9); + + let fTemp12: number = fTemp1 * (1.0 - $fRec0[0]); + + let fTemp13: number = + iTemp3 !== 0 ? 0.0 : $fRec8[1] + $fConst5 * $fRec5[0]; + $fRec8[0] = fTemp13 - Math.floor(fTemp13); + + let fTemp14: number = Math.min( + 4096.0, + 0.875 * $fRec1[0] - + $fRec2[0] * + ftbl0ChorusSIG0[ + Math.max(0, Math.min(Math.floor(65536.0 * $fRec8[0]), 65535)) + ] + ); + let iTemp15: number = Math.floor(fTemp14); + let fTemp16: number = Math.floor(fTemp14); + + output0 = + iSlow0 !== 0 + ? fTemp0 + : 0.70710677 * + ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp6))) & 8191] * + (fTemp7 + (1.0 - fTemp5)) + + (fTemp5 - fTemp7) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp6 + 1))) & 8191 + ]) + + (fTemp9 - fTemp10) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp11 + 1))) & 8191 + ] + + fTemp12 + + $fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp11))) & 8191] * + (fTemp10 + (1.0 - fTemp9)) - + 0.70710677 * + ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp15))) & 8191] * + (fTemp16 + (1.0 - fTemp14)) + + (fTemp14 - fTemp16) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp15 + 1))) & 8191 + ]); + + let fTemp17: number = + iTemp3 !== 0 ? 0.0 : $fRec9[1] + $fConst6 * $fRec5[0]; + $fRec9[0] = fTemp17 - Math.floor(fTemp17); + + let iTemp18: number = Math.max( + 0, + Math.min(Math.floor(65536.0 * $fRec9[0]), 65535) + ); + + let fTemp19: number = Math.min( + 4096.0, + 0.25 * $fRec1[0] + + $fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp18] + + 0.70710677 * ftbl0ChorusSIG0[iTemp18]) + ); + let iTemp20: number = Math.floor(fTemp19); + let fTemp21: number = Math.floor(fTemp19); + + let fTemp22: number = + iTemp3 !== 0 ? 0.0 : $fRec10[1] + $fConst7 * $fRec5[0]; + $fRec10[0] = fTemp22 - Math.floor(fTemp22); + + let iTemp23: number = Math.max( + 0, + Math.min(Math.floor(65536.0 * $fRec10[0]), 65535) + ); + + let fTemp24: number = Math.min( + 4096.0, + 0.5 * $fRec1[0] + + $fRec2[0] * + (0.70710677 * ftbl0ChorusSIG0[iTemp23] - + 0.70710677 * ftbl1ChorusSIG1[iTemp23]) + ); + let iTemp25: number = Math.floor(fTemp24); + let fTemp26: number = Math.floor(fTemp24); + + let fTemp27: number = + iTemp3 !== 0 ? 0.0 : $fRec11[1] + $fConst8 * $fRec5[0]; + $fRec11[0] = fTemp27 - Math.floor(fTemp27); + + let iTemp28: number = Math.max( + 0, + Math.min(Math.floor(65536.0 * $fRec11[0]), 65535) + ); + + let fTemp29: number = Math.min( + 4096.0, + 0.75 * $fRec1[0] - + $fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp28] + + 0.70710677 * ftbl0ChorusSIG0[iTemp28]) + ); + let iTemp30: number = Math.floor(fTemp29); + let fTemp31: number = Math.floor(fTemp29); + + let fTemp32: number = + iTemp3 !== 0 ? 0.0 : $fRec12[1] + $fConst9 * $fRec5[0]; + $fRec12[0] = fTemp32 - Math.floor(fTemp32); + + let iTemp33: number = Math.max( + 0, + Math.min(Math.floor(65536.0 * $fRec12[0]), 65535) + ); + + let fTemp34: number = Math.min( + 4096.0, + $fRec1[0] + + $fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp33] - + 0.70710677 * ftbl0ChorusSIG0[iTemp33]) + ); + let iTemp35: number = Math.floor(fTemp34); + let fTemp36: number = Math.floor(fTemp34); + + output1 = + iSlow0 !== 0 + ? fTemp0 + : fTemp12 - + (0.38268343 * + ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp20))) & 8191] * + (fTemp21 + (1.0 - fTemp19)) + + (fTemp19 - fTemp21) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp20 + 1))) & 8191 + ]) + + 0.9238795 * + ($fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp25))) & 8191 + ] * + (fTemp26 + (1.0 - fTemp24)) + + (fTemp24 - fTemp26) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp25 + 1))) & 8191 + ]) + + 0.9238795 * + ($fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp30))) & 8191 + ] * + (fTemp31 + (1.0 - fTemp29)) + + (fTemp29 - fTemp31) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp30 + 1))) & 8191 + ]) + + 0.38268343 * + ($fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp35))) & 8191 + ] * + (fTemp36 + (1.0 - fTemp34)) + + (fTemp34 - fTemp36) * + $fVec1[ + ($IOTA0 - Math.min(4097, Math.max(0, iTemp35 + 1))) & 8191 + ])); + + $iVec0[1] = $iVec0[0]; + $fRec0[1] = $fRec0[0]; + $IOTA0 = ($IOTA0 + 1) & 8191; + $fRec1[1] = $fRec1[0]; + $fRec2[1] = $fRec2[0]; + $fRec5[1] = $fRec5[0]; + $fRec4[1] = $fRec4[0]; + $fRec7[1] = $fRec7[0]; + $fRec8[1] = $fRec8[0]; + $fRec9[1] = $fRec9[0]; + $fRec10[1] = $fRec10[0]; + $fRec11[1] = $fRec11[0]; + $fRec12[1] = $fRec12[0]; + } + } + + init(); + + return { compute }; +} + +const sig0Fn = (x: number) => Math.cos(9.58738e-5 * x); +const sig1Fn = (x: number) => Math.sin(9.58738e-5 * x); + +function fillTable(table: Float32Array, fn: (phase: number) => void) { + if (table.length !== 65536) { + throw new Error("Table must be 65536 samples long"); + } + let iVec2A = 0; + let iVec2B = 0; + let iRec3A = 0; + let iRec3B = 0; + for (let i1 = 0; i1 < 65536; i1++) { + iVec2A = 1; + iRec3A = (iVec2B + iRec3B) % 65536; + table[i1] = Math.cos(9.58738e-5 * iRec3A); + iVec2B = iVec2A; + iRec3B = iRec3A; + } + return table; +} diff --git a/packages/chorus/src/index.ts b/packages/chorus/src/index.ts new file mode 100644 index 0000000..0fd5502 --- /dev/null +++ b/packages/chorus/src/index.ts @@ -0,0 +1,29 @@ +import { + createRegistrar, + createWorkletConstructor, + ParamInput, +} from "./_worklet"; +import { PROCESSOR } from "./processor"; + +export const registerChorusWorklet = createRegistrar("CHORUS", PROCESSOR); + +export type ChorusInputs = { + trigger: ParamInput; +}; + +export type ChorusWorkletNode = AudioWorkletNode & { + trigger: AudioParam; + dispose(): void; +}; + +export const Chorus = createWorkletConstructor( + { + processorName: "ChorusProcessor", + paramNames: ["trigger"], + workletOptions: () => ({ + numberOfInputs: 1, + numberOfOutputs: 2, + outputChannelCount: [2], + }), + } +); diff --git a/packages/chorus/src/processor.ts b/packages/chorus/src/processor.ts new file mode 100644 index 0000000..0d045b0 --- /dev/null +++ b/packages/chorus/src/processor.ts @@ -0,0 +1 @@ +export const PROCESSOR = `"use strict";(()=>{function ct(h){let r=new Float32Array(65536),e=new Float32Array(65536),f=new Int32Array(2),i=0,M=0,a=0,w=0,I=0,Tt=0,s=new Float32Array(2),t=0,l=new Float32Array(8192),$t=0,m=new Float32Array(2),xt=0,o=new Float32Array(2),B=0,yt=0,n=new Float32Array(2),p=new Float32Array(2),D=0,b=new Float32Array(2),E=0,c=new Float32Array(2),G=0,T=new Float32Array(2),O=0,$=new Float32Array(2),L=0,x=new Float32Array(2),W=0,y=new Float32Array(2);function At(){M=h,a=Math.min(192e3,Math.max(1,M)),w=Math.exp(-(44.12234/a)),I=1-w,B=.33333334/a,D=1/a,E=.14285715/a,G=.5/a,O=.25/a,L=.16666667/a,W=.125/a,bt(r,Pt),bt(e,Bt)}function wt(j,Ft,Rt){let k=1-Math.floor(i),Ct=I*Tt,gt=4.096*$t,dt=625e-7*xt,St=I*yt;for(let A=0;AMath.cos(958738e-10*h),Bt=h=>Math.sin(958738e-10*h);function bt(h,r){if(h.length!==65536)throw new Error("Table must be 65536 samples long");let e=0,f=0,i=0,M=0;for(let a=0;a<65536;a++)e=1,i=(f+M)%65536,h[a]=Math.cos(958738e-10*i),f=e,M=i;return h}var P=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:r}=ct(sampleRate);this.g=r,this.port.onmessage=e=>{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(r,e,f){if(r[0].length===0)return this.r;let i=r[0][0],M=e[0][0],a=e[0][1];return this.g(i,M,a),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([r,e,f,i])=>({name:r,defaultValue:e,minValue:f,maxValue:i,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",P);})();`; diff --git a/packages/chorus/src/worklet.ts b/packages/chorus/src/worklet.ts new file mode 100644 index 0000000..eed3c64 --- /dev/null +++ b/packages/chorus/src/worklet.ts @@ -0,0 +1,47 @@ +import { createChorus } from "./dsp"; + +export class ChorusProcessor extends AudioWorkletProcessor { + r: boolean; // running + g: ReturnType["compute"]; + + constructor() { + super(); + this.r = true; + const { compute } = createChorus(sampleRate); + this.g = compute; + this.port.onmessage = (event) => { + switch (event.data.type) { + case "DISPOSE": + this.r = false; + break; + } + }; + } + + process( + inputs: Float32Array[][], + outputs: Float32Array[][], + parameters: any + ) { + if (inputs[0].length === 0) return this.r; + const input = inputs[0][0]; + const outputL = outputs[0][0]; + const outputR = outputs[0][1]; + this.g(input, outputL, outputR); + return this.r; + } + + static get parameterDescriptors() { + return [["trigger", 0, 0, 1]].map( + ([name, defaultValue, minValue, maxValue]) => ({ + name, + defaultValue, + minValue, + maxValue, + automationRate: "k-rate", + }) + ); + } +} + +registerProcessor("ChorusProcessor", ChorusProcessor); From 732e1f4338dbb119aa8c53242377fd38d44cbac1 Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 19:51:08 +0200 Subject: [PATCH 2/5] chore: add example --- package-lock.json | 13 ++++-- packages/chorus-t/src/index.ts | 2 +- packages/chorus/src/dsp.ts | 65 +++++++++++++++----------- packages/chorus/src/index.ts | 2 +- packages/chorus/src/processor.ts | 2 +- packages/synthlet/package.json | 1 + packages/synthlet/src/index.ts | 3 ++ site/.source/index.js | 45 +++++++++--------- site/content/docs/(effects)/chorus.mdx | 25 ++++++++++ site/examples/ChorusExample.tsx | 34 ++++++++++++++ 10 files changed, 136 insertions(+), 56 deletions(-) create mode 100644 site/content/docs/(effects)/chorus.mdx create mode 100644 site/examples/ChorusExample.tsx diff --git a/package-lock.json b/package-lock.json index be0c1aa..c0d3e32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2312,6 +2312,10 @@ "resolved": "packages/arp", "link": true }, + "node_modules/@synthlet/chorus": { + "resolved": "packages/chorus", + "link": true + }, "node_modules/@synthlet/chorus-t": { "resolved": "packages/chorus-t", "link": true @@ -7750,7 +7754,8 @@ } }, "packages/arp": { - "version": "0.0.0", + "name": "@synthlet/arp", + "version": "0.1.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.11", @@ -7760,7 +7765,6 @@ }, "packages/chorus": { "version": "0.0.0", - "extraneous": true, "license": "MIT", "devDependencies": { "@types/jest": "^29.5.12", @@ -7890,12 +7894,13 @@ } }, "packages/synthlet": { - "version": "0.2.1", + "version": "0.4.0", "license": "MIT", "dependencies": { "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", - "@synthlet/arp": "^0.0.0", + "@synthlet/arp": "^0.1.0", + "@synthlet/chorus": "^0.0.0", "@synthlet/chorus-t": "^0.1.0", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", diff --git a/packages/chorus-t/src/index.ts b/packages/chorus-t/src/index.ts index b2c0c13..9390467 100644 --- a/packages/chorus-t/src/index.ts +++ b/packages/chorus-t/src/index.ts @@ -21,7 +21,7 @@ export type ChorusTWorkletNode = AudioWorkletNode & { setBypass(value: boolean): void; }; -export const registerChorusTWorklet = createRegistrar("CHORUS", PROCESSOR); +export const registerChorusTWorklet = createRegistrar("CHORUS-T", PROCESSOR); export const ChorusT = createWorkletConstructor< ChorusTWorkletNode, diff --git a/packages/chorus/src/dsp.ts b/packages/chorus/src/dsp.ts index 0a48f22..bdea882 100644 --- a/packages/chorus/src/dsp.ts +++ b/packages/chorus/src/dsp.ts @@ -4,7 +4,7 @@ export function createChorus(sampleRate: number) { let $iVec0 = new Int32Array(2); let $fVslider0 = 0.0; - let $fSampleRate = 0; + const $fSampleRate = sampleRate; let $fConst0 = 0.0; let $fConst1 = 0.0; let $fConst2 = 0.0; @@ -33,26 +33,41 @@ export function createChorus(sampleRate: number) { let $fConst9 = 0.0; let $fRec12 = new Float32Array(2); - function init(): void { - $fSampleRate = sampleRate; - $fConst0 = Math.min(1.92e5, Math.max(1.0, $fSampleRate)); - $fConst1 = Math.exp(-(44.12234 / $fConst0)); - $fConst2 = 1.0 - $fConst1; - $fConst3 = 0.33333334 / $fConst0; - $fConst4 = 1.0 / $fConst0; - $fConst5 = 0.14285715 / $fConst0; - $fConst6 = 0.5 / $fConst0; - $fConst7 = 0.25 / $fConst0; - $fConst8 = 0.16666667 / $fConst0; - $fConst9 = 0.125 / $fConst0; - fillTable(ftbl0ChorusSIG0, sig0Fn); - fillTable(ftbl1ChorusSIG1, sig1Fn); + $fConst0 = Math.min(1.92e5, Math.max(1.0, $fSampleRate)); + $fConst1 = Math.exp(-(44.12234 / $fConst0)); + $fConst2 = 1.0 - $fConst1; + $fConst3 = 0.33333334 / $fConst0; + $fConst4 = 1.0 / $fConst0; + $fConst5 = 0.14285715 / $fConst0; + $fConst6 = 0.5 / $fConst0; + $fConst7 = 0.25 / $fConst0; + $fConst8 = 0.16666667 / $fConst0; + $fConst9 = 0.125 / $fConst0; + $fVslider0 = 0.5; + $fVslider1 = 1.0; + $fVslider2 = 0.5; + $fVslider3 = 0.5; + $fVslider4 = 0.5; + fillTable(ftbl0ChorusSIG0, sig0Fn); + fillTable(ftbl1ChorusSIG1, sig1Fn); + + function update( + delay: number, + rate: number, + depth: number, + deviation: number + ): void { + $fVslider0 = delay; + $fVslider1 = 1; // enable + $fVslider2 = rate; + $fVslider3 = depth; + $fVslider4 = deviation; } function compute( - inputs0: Float32Array, - outputs0: Float32Array, - outputs1: Float32Array + inputMono: Float32Array, + outLeft: Float32Array, + outRight: Float32Array ): void { let iSlow0: number = 1 - Math.floor($fVslider0); let fSlow1: number = $fConst2 * $fVslider1; @@ -60,10 +75,8 @@ export function createChorus(sampleRate: number) { let fSlow3: number = 6.25e-5 * $fVslider3; let fSlow4: number = $fConst2 * $fVslider4; - for (let i = 0; i < inputs0.length; i++) { - let input0 = inputs0[i]; - let output0 = outputs0[i]; - let output1 = outputs1[i]; + for (let i = 0; i < inputMono.length; i++) { + let input0 = inputMono[i]; $iVec0[0] = 1; $fRec0[0] = fSlow1 + $fConst1 * $fRec0[1]; @@ -123,7 +136,7 @@ export function createChorus(sampleRate: number) { let iTemp15: number = Math.floor(fTemp14); let fTemp16: number = Math.floor(fTemp14); - output0 = + outLeft[i] = iSlow0 !== 0 ? fTemp0 : 0.70710677 * @@ -224,7 +237,7 @@ export function createChorus(sampleRate: number) { let iTemp35: number = Math.floor(fTemp34); let fTemp36: number = Math.floor(fTemp34); - output1 = + outRight[i] = iSlow0 !== 0 ? fTemp0 : fTemp12 - @@ -279,9 +292,7 @@ export function createChorus(sampleRate: number) { } } - init(); - - return { compute }; + return { update, compute }; } const sig0Fn = (x: number) => Math.cos(9.58738e-5 * x); diff --git a/packages/chorus/src/index.ts b/packages/chorus/src/index.ts index 0fd5502..39e4951 100644 --- a/packages/chorus/src/index.ts +++ b/packages/chorus/src/index.ts @@ -22,7 +22,7 @@ export const Chorus = createWorkletConstructor( paramNames: ["trigger"], workletOptions: () => ({ numberOfInputs: 1, - numberOfOutputs: 2, + numberOfOutputs: 1, outputChannelCount: [2], }), } diff --git a/packages/chorus/src/processor.ts b/packages/chorus/src/processor.ts index 0d045b0..7f7fc4a 100644 --- a/packages/chorus/src/processor.ts +++ b/packages/chorus/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function ct(h){let r=new Float32Array(65536),e=new Float32Array(65536),f=new Int32Array(2),i=0,M=0,a=0,w=0,I=0,Tt=0,s=new Float32Array(2),t=0,l=new Float32Array(8192),$t=0,m=new Float32Array(2),xt=0,o=new Float32Array(2),B=0,yt=0,n=new Float32Array(2),p=new Float32Array(2),D=0,b=new Float32Array(2),E=0,c=new Float32Array(2),G=0,T=new Float32Array(2),O=0,$=new Float32Array(2),L=0,x=new Float32Array(2),W=0,y=new Float32Array(2);function At(){M=h,a=Math.min(192e3,Math.max(1,M)),w=Math.exp(-(44.12234/a)),I=1-w,B=.33333334/a,D=1/a,E=.14285715/a,G=.5/a,O=.25/a,L=.16666667/a,W=.125/a,bt(r,Pt),bt(e,Bt)}function wt(j,Ft,Rt){let k=1-Math.floor(i),Ct=I*Tt,gt=4.096*$t,dt=625e-7*xt,St=I*yt;for(let A=0;AMath.cos(958738e-10*h),Bt=h=>Math.sin(958738e-10*h);function bt(h,r){if(h.length!==65536)throw new Error("Table must be 65536 samples long");let e=0,f=0,i=0,M=0;for(let a=0;a<65536;a++)e=1,i=(f+M)%65536,h[a]=Math.cos(958738e-10*i),f=e,M=i;return h}var P=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:r}=ct(sampleRate);this.g=r,this.port.onmessage=e=>{switch(e.data.type){case"DISPOSE":this.r=!1;break}}}process(r,e,f){if(r[0].length===0)return this.r;let i=r[0][0],M=e[0][0],a=e[0][1];return this.g(i,M,a),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([r,e,f,i])=>({name:r,defaultValue:e,minValue:f,maxValue:i,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",P);})();`; +export const PROCESSOR = `"use strict";(()=>{function we(h){let r=new Float32Array(65536),t=new Float32Array(65536),i=new Int32Array(2),o=0,M=0,a=0,F=0,k=0,P=0,s=new Float32Array(2),e=0,l=new Float32Array(8192),B=0,m=new Float32Array(2),D=0,n=new Float32Array(2),j=0,E=0,f=new Float32Array(2),p=new Float32Array(2),q=0,b=new Float32Array(2),z=0,c=new Float32Array(2),H=0,T=new Float32Array(2),J=0,$=new Float32Array(2),K=0,x=new Float32Array(2),N=0,y=new Float32Array(2);function Fe(){M=h,a=Math.min(192e3,Math.max(1,M)),F=Math.exp(-(44.12234/a)),k=1-F,j=.33333334/a,q=1/a,z=.14285715/a,H=.5/a,J=.25/a,K=.16666667/a,N=.125/a,Ae(r,ve),Ae(t,ke),o=.5,P=1,B=.5,D=.5,E=.5}function Pe(R,G,O,A){o=R,P=1,B=G,D=O,E=A}function Re(R,G,O){let A=1-Math.floor(o),Ce=k*P,ge=4.096*B,de=625e-7*D,Se=k*E;for(let w=0;wMath.cos(958738e-10*h),ke=h=>Math.sin(958738e-10*h);function Ae(h,r){if(h.length!==65536)throw new Error("Table must be 65536 samples long");let t=0,i=0,o=0,M=0;for(let a=0;a<65536;a++)t=1,o=(i+M)%65536,h[a]=Math.cos(958738e-10*o),i=t,M=o;return h}var W=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:r}=we(sampleRate);this.g=r,this.port.onmessage=t=>{switch(t.data.type){case"DISPOSE":this.r=!1;break}}}process(r,t,i){if(r[0].length===0)return this.r;let o=r[0][0],M=t[0][0],a=t[0][1];return this.g(o,M,a),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([r,t,i,o])=>({name:r,defaultValue:t,minValue:i,maxValue:o,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",W);})();`; diff --git a/packages/synthlet/package.json b/packages/synthlet/package.json index ecb369d..e479656 100644 --- a/packages/synthlet/package.json +++ b/packages/synthlet/package.json @@ -23,6 +23,7 @@ "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", "@synthlet/arp": "^0.1.0", + "@synthlet/chorus": "^0.0.0", "@synthlet/chorus-t": "^0.1.0", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", diff --git a/packages/synthlet/src/index.ts b/packages/synthlet/src/index.ts index 164bd7b..671e911 100644 --- a/packages/synthlet/src/index.ts +++ b/packages/synthlet/src/index.ts @@ -1,6 +1,7 @@ import { registerAdWorklet } from "@synthlet/ad"; import { registerAdsrWorklet } from "@synthlet/adsr"; import { registerArpWorklet } from "@synthlet/arp"; +import { registerChorusWorklet } from "@synthlet/chorus"; import { registerChorusTWorklet } from "@synthlet/chorus-t"; import { registerClipAmpWorklet } from "@synthlet/clip-amp"; import { registerClockWorklet } from "@synthlet/clock"; @@ -17,6 +18,7 @@ import { registerWavetableOscillatorWorklet } from "@synthlet/wavetable-oscillat export * from "@synthlet/ad"; export * from "@synthlet/adsr"; export * from "@synthlet/arp"; +export * from "@synthlet/chorus"; export * from "@synthlet/chorus-t"; export * from "@synthlet/clip-amp"; export * from "@synthlet/clock"; @@ -43,6 +45,7 @@ export function registerAllWorklets( registerAdWorklet(context), registerAdsrWorklet(context), registerArpWorklet(context), + registerChorusWorklet(context), registerChorusTWorklet(context), registerClipAmpWorklet(context), registerClockWorklet(context), diff --git a/site/.source/index.js b/site/.source/index.js index 499f217..2b00e87 100644 --- a/site/.source/index.js +++ b/site/.source/index.js @@ -5,26 +5,27 @@ import * as file_2 from "../content/docs/quick-start.mdx?collection=docs&hash=a0 import * as file_3 from "../content/docs/synths.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_4 from "../content/docs/troubleshoo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_5 from "../content/docs/(effects)/chorus-t.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_6 from "../content/docs/(effects)/dattorro.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_7 from "../content/docs/(modifiers)/clip-amp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_8 from "../content/docs/(modifiers)/state-variable-filter.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_9 from "../content/docs/(modifiers)/vca.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_10 from "../content/docs/(modulators)/ad.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_11 from "../content/docs/(modulators)/adsr.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_12 from "../content/docs/(modulators)/lfo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_13 from "../content/docs/(modulators)/param.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_14 from "../content/docs/(sequencers)/arp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_15 from "../content/docs/(sequencers)/clock.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_16 from "../content/docs/(sequencers)/euclid.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_17 from "../content/docs/(sources)/impulse.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_18 from "../content/docs/(sources)/noise.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_19 from "../content/docs/(sources)/polyblep.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_20 from "../content/docs/(sources)/wavetable.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_21 from "../content/docs/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_22 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_6 from "../content/docs/(effects)/chorus.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_7 from "../content/docs/(effects)/dattorro.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_8 from "../content/docs/(modifiers)/clip-amp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_9 from "../content/docs/(modifiers)/state-variable-filter.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_10 from "../content/docs/(modifiers)/vca.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_11 from "../content/docs/(modulators)/ad.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_12 from "../content/docs/(modulators)/adsr.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_13 from "../content/docs/(modulators)/lfo.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_14 from "../content/docs/(modulators)/param.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_15 from "../content/docs/(sources)/impulse.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_16 from "../content/docs/(sources)/noise.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_17 from "../content/docs/(sources)/polyblep.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_18 from "../content/docs/(sources)/wavetable.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_19 from "../content/docs/(sequencers)/arp.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_20 from "../content/docs/(sequencers)/clock.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_21 from "../content/docs/(sequencers)/euclid.mdx?collection=docs&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_22 from "../content/docs/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" import * as file_23 from "../content/docs/(effects)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_24 from "../content/docs/(modulators)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_25 from "../content/docs/(sequencers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -import * as file_26 from "../content/docs/(sources)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" -export const docs = [toRuntime("doc", file_0, {"path":"dsl.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/dsl.mdx"}),toRuntime("doc", file_1, {"path":"guide.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/guide.mdx"}),toRuntime("doc", file_2, {"path":"quick-start.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/quick-start.mdx"}),toRuntime("doc", file_3, {"path":"synths.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/synths.mdx"}),toRuntime("doc", file_4, {"path":"troubleshoo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/troubleshoo.mdx"}),toRuntime("doc", file_5, {"path":"(effects)/chorus-t.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus-t.mdx"}),toRuntime("doc", file_6, {"path":"(effects)/dattorro.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/dattorro.mdx"}),toRuntime("doc", file_7, {"path":"(modifiers)/clip-amp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/clip-amp.mdx"}),toRuntime("doc", file_8, {"path":"(modifiers)/state-variable-filter.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/state-variable-filter.mdx"}),toRuntime("doc", file_9, {"path":"(modifiers)/vca.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/vca.mdx"}),toRuntime("doc", file_10, {"path":"(modulators)/ad.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/ad.mdx"}),toRuntime("doc", file_11, {"path":"(modulators)/adsr.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/adsr.mdx"}),toRuntime("doc", file_12, {"path":"(modulators)/lfo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/lfo.mdx"}),toRuntime("doc", file_13, {"path":"(modulators)/param.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/param.mdx"}),toRuntime("doc", file_14, {"path":"(sequencers)/arp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/arp.mdx"}),toRuntime("doc", file_15, {"path":"(sequencers)/clock.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/clock.mdx"}),toRuntime("doc", file_16, {"path":"(sequencers)/euclid.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/euclid.mdx"}),toRuntime("doc", file_17, {"path":"(sources)/impulse.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/impulse.mdx"}),toRuntime("doc", file_18, {"path":"(sources)/noise.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/noise.mdx"}),toRuntime("doc", file_19, {"path":"(sources)/polyblep.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/polyblep.mdx"}),toRuntime("doc", file_20, {"path":"(sources)/wavetable.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/wavetable.mdx"})] -export const meta = [toRuntime("meta", file_21, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_22, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_23, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_24, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_25, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_26, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file +import * as file_24 from "../content/docs/(modifiers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_25 from "../content/docs/(modulators)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_26 from "../content/docs/(sequencers)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +import * as file_27 from "../content/docs/(sources)/meta.json?collection=meta&hash=a0e5c83919940bc930420dd0f9f7d68e8c5dbe7f6983d8b3ed42ebccd021e4f6" +export const docs = [toRuntime("doc", file_0, {"path":"dsl.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/dsl.mdx"}),toRuntime("doc", file_1, {"path":"guide.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/guide.mdx"}),toRuntime("doc", file_2, {"path":"quick-start.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/quick-start.mdx"}),toRuntime("doc", file_3, {"path":"synths.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/synths.mdx"}),toRuntime("doc", file_4, {"path":"troubleshoo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/troubleshoo.mdx"}),toRuntime("doc", file_5, {"path":"(effects)/chorus-t.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus-t.mdx"}),toRuntime("doc", file_6, {"path":"(effects)/chorus.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/chorus.mdx"}),toRuntime("doc", file_7, {"path":"(effects)/dattorro.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/dattorro.mdx"}),toRuntime("doc", file_8, {"path":"(modifiers)/clip-amp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/clip-amp.mdx"}),toRuntime("doc", file_9, {"path":"(modifiers)/state-variable-filter.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/state-variable-filter.mdx"}),toRuntime("doc", file_10, {"path":"(modifiers)/vca.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/vca.mdx"}),toRuntime("doc", file_11, {"path":"(modulators)/ad.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/ad.mdx"}),toRuntime("doc", file_12, {"path":"(modulators)/adsr.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/adsr.mdx"}),toRuntime("doc", file_13, {"path":"(modulators)/lfo.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/lfo.mdx"}),toRuntime("doc", file_14, {"path":"(modulators)/param.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/param.mdx"}),toRuntime("doc", file_15, {"path":"(sources)/impulse.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/impulse.mdx"}),toRuntime("doc", file_16, {"path":"(sources)/noise.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/noise.mdx"}),toRuntime("doc", file_17, {"path":"(sources)/polyblep.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/polyblep.mdx"}),toRuntime("doc", file_18, {"path":"(sources)/wavetable.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/wavetable.mdx"}),toRuntime("doc", file_19, {"path":"(sequencers)/arp.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/arp.mdx"}),toRuntime("doc", file_20, {"path":"(sequencers)/clock.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/clock.mdx"}),toRuntime("doc", file_21, {"path":"(sequencers)/euclid.mdx","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/euclid.mdx"})] +export const meta = [toRuntime("meta", file_22, {"path":"meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/meta.json"}),toRuntime("meta", file_23, {"path":"(effects)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(effects)/meta.json"}),toRuntime("meta", file_24, {"path":"(modifiers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modifiers)/meta.json"}),toRuntime("meta", file_25, {"path":"(modulators)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(modulators)/meta.json"}),toRuntime("meta", file_26, {"path":"(sequencers)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sequencers)/meta.json"}),toRuntime("meta", file_27, {"path":"(sources)/meta.json","absolutePath":"/Users/danigb/Projects/Synthlet/site/content/docs/(sources)/meta.json"})] \ No newline at end of file diff --git a/site/content/docs/(effects)/chorus.mdx b/site/content/docs/(effects)/chorus.mdx new file mode 100644 index 0000000..a16f141 --- /dev/null +++ b/site/content/docs/(effects)/chorus.mdx @@ -0,0 +1,25 @@ +--- +title: Chorus +description: A chorus effect implemented in Faust +--- + +import ChorusExample from "../../../examples/ChorusExample"; + +```ts +import { Chorus } from "synthlet"; + +const osc = new OscillatorNode(audioContext); +const chorus = Chorus(audioContext, { + rate: 0.5, + depth: 0.5, + feedback: 0.5, +}); + +osc.connect(chorus).connect(audioContext.destination); + +chorus.lfoRate1.value = 0.2; +``` + + + +## Parameters diff --git a/site/examples/ChorusExample.tsx b/site/examples/ChorusExample.tsx new file mode 100644 index 0000000..c934f6a --- /dev/null +++ b/site/examples/ChorusExample.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { AdsrAmp, Oscillator, Param } from "synthlet"; +import { Chorus } from "../../packages/chorus/src"; +import { ExamplePane, GateButton } from "./components/ExamplePane"; +import { useSynth } from "./useSynth"; + +function ChorusSynth(context: AudioContext) { + const gate = Param(context, { input: 0.1 }); + const osc = Oscillator(context, { frequency: 440 }); + const amp = AdsrAmp(context, { gate }); + const chorus = Chorus(context, {}); + + osc.connect(amp).connect(chorus); + + return Object.assign(chorus, { osc, amp, gate, chorus }); +} + +function Example() { + const synth = useSynth(ChorusSynth); + if (!synth) return null; + + return ( + <> + + + ); +} + +export default () => ( + + + +); From 5599f9e8bf0d367e94b23c320ce3693037596e2e Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 20:21:07 +0200 Subject: [PATCH 3/5] fix: remove bypass and fix ts code --- packages/chorus/dsp/chorus.dsp | 7 +- packages/chorus/dsp/chorus.rs | 364 ++++++++++++-------------- packages/chorus/src/dsp.ts | 436 ++++++++++++++----------------- packages/chorus/src/processor.ts | 2 +- 4 files changed, 365 insertions(+), 444 deletions(-) diff --git a/packages/chorus/dsp/chorus.dsp b/packages/chorus/dsp/chorus.dsp index 3ef6975..a522ab4 100644 --- a/packages/chorus/dsp/chorus.dsp +++ b/packages/chorus/dsp/chorus.dsp @@ -2,17 +2,17 @@ import("stdfaust.lib"); voices = 8; // MUST BE EVEN -process = ba.bypass1to2(cbp,chorus_mono(dmax,curdel,rate,sigma,do2,voices)); +process = chorus_mono(dmax,curdel,rate,sigma,do2,voices); dmax = 4096; // 8192; -curdel = dmax * vslider("[0] Delay [midi:ctrl 4] [style:knob]", 0.5, 0, 1, 1) : si.smooth(0.999); +curdel = dmax * vslider("[0] Delay [midi:ctrl 4] [style:knob]", 0.5, 0, 1, 0.001) : si.smooth(0.999); rateMax = 7.0; // Hz rateMin = 0.01; rateT60 = 0.15661; rate = vslider("[1] Rate [midi:ctrl 2] [unit:Hz] [style:knob]", 0.5, rateMin, rateMax, 0.0001) : si.smooth(ba.tau2pole(rateT60/6.91)); - depth = vslider("[4] Depth [midi:ctrl 3] [style:knob]", 0.5, 0, 1, 0.001) : si.smooth(ba.tau2pole(depthT60/6.91)); +depth = vslider("[4] Depth [midi:ctrl 3] [style:knob]", 0.5, 0, 1, 0.001) : si.smooth(ba.tau2pole(depthT60/6.91)); depthT60 = 0.15661; delayPerVoice = 0.5*curdel/voices; @@ -21,7 +21,6 @@ sigma = delayPerVoice * vslider("[6] Deviation [midi:ctrl 58] [style:knob]",0.5, periodic = 1; do2 = depth; // use when depth=1 means "multivibrato" effect (no original => all are modulated) -cbp = 1-int(vslider("[0] Enable [midi:ctrl 105][style:knob]",0,0,1,1)); chorus_mono(dmax,curdel,rate,sigma,do2,voices) = _ <: (*(1-do2)<:_,_),(*(do2) <: par(i,voices,voice(i)) :> _,_) : ro.interleave(2,2) : +,+ diff --git a/packages/chorus/dsp/chorus.rs b/packages/chorus/dsp/chorus.rs index 6e027f5..4c4f3c5 100644 --- a/packages/chorus/dsp/chorus.rs +++ b/packages/chorus/dsp/chorus.rs @@ -103,22 +103,21 @@ fn rint_f32(val: f32) -> f32 { #[cfg_attr(feature = "default-boxed", derive(default_boxed::DefaultBoxed))] #[repr(C)] pub struct Chorus { - iVec0: [i32; 2], - fVslider0: F32, fSampleRate: i32, fConst0: F32, fConst1: F32, fConst2: F32, - fVslider1: F32, + fVslider0: F32, + iVec0: [i32; 2], fRec0: [F32; 2], IOTA0: i32, fVec1: [F32; 8192], - fVslider2: F32, + fVslider1: F32, fRec1: [F32; 2], - fVslider3: F32, + fVslider2: F32, fRec2: [F32; 2], fConst3: F32, - fVslider4: F32, + fVslider3: F32, fRec5: [F32; 2], fRec4: [F32; 2], fConst4: F32, @@ -140,22 +139,21 @@ impl FaustDsp for Chorus { fn new() -> Chorus { Chorus { - iVec0: [0; 2], - fVslider0: 0.0, fSampleRate: 0, fConst0: 0.0, fConst1: 0.0, fConst2: 0.0, - fVslider1: 0.0, + fVslider0: 0.0, + iVec0: [0; 2], fRec0: [0.0; 2], IOTA0: 0, fVec1: [0.0; 8192], - fVslider2: 0.0, + fVslider1: 0.0, fRec1: [0.0; 2], - fVslider3: 0.0, + fVslider2: 0.0, fRec2: [0.0; 2], fConst3: 0.0, - fVslider4: 0.0, + fVslider3: 0.0, fRec5: [0.0; 2], fRec4: [0.0; 2], fConst4: 0.0, @@ -173,7 +171,6 @@ impl FaustDsp for Chorus { } } fn metadata(&self, m: &mut dyn Meta) { - m.declare("basics.lib/bypass1to2:author", r"Julius Smith"); m.declare("basics.lib/name", r"Faust Basic Element Library"); m.declare( "basics.lib/tabulateNd", @@ -222,11 +219,10 @@ impl FaustDsp for Chorus { sig1.fillChorusSIG1(65536, unsafe { &mut ftbl1ChorusSIG1 }); } fn instance_reset_params(&mut self) { - self.fVslider0 = 0.0; + self.fVslider0 = 0.5; self.fVslider1 = 0.5; self.fVslider2 = 0.5; self.fVslider3 = 0.5; - self.fVslider4 = 0.5; } fn instance_clear(&mut self) { for l0 in 0..2 { @@ -302,45 +298,39 @@ impl FaustDsp for Chorus { ui_interface.declare(Some(ParamIndex(0)), "0", ""); ui_interface.declare(Some(ParamIndex(0)), "midi", "ctrl 4"); ui_interface.declare(Some(ParamIndex(0)), "style", "knob"); - ui_interface.add_vertical_slider("Delay", ParamIndex(0), 0.5, 0.0, 1.0, 1.0); - ui_interface.declare(Some(ParamIndex(1)), "0", ""); - ui_interface.declare(Some(ParamIndex(1)), "midi", "ctrl 105"); + ui_interface.add_vertical_slider("Delay", ParamIndex(0), 0.5, 0.0, 1.0, 0.001); + ui_interface.declare(Some(ParamIndex(1)), "1", ""); + ui_interface.declare(Some(ParamIndex(1)), "midi", "ctrl 2"); ui_interface.declare(Some(ParamIndex(1)), "style", "knob"); - ui_interface.add_vertical_slider("Enable", ParamIndex(1), 0.0, 0.0, 1.0, 1.0); - ui_interface.declare(Some(ParamIndex(2)), "1", ""); - ui_interface.declare(Some(ParamIndex(2)), "midi", "ctrl 2"); + ui_interface.declare(Some(ParamIndex(1)), "unit", "Hz"); + ui_interface.add_vertical_slider("Rate", ParamIndex(1), 0.5, 0.01, 7.0, 0.0001); + ui_interface.declare(Some(ParamIndex(2)), "4", ""); + ui_interface.declare(Some(ParamIndex(2)), "midi", "ctrl 3"); ui_interface.declare(Some(ParamIndex(2)), "style", "knob"); - ui_interface.declare(Some(ParamIndex(2)), "unit", "Hz"); - ui_interface.add_vertical_slider("Rate", ParamIndex(2), 0.5, 0.01, 7.0, 0.0001); - ui_interface.declare(Some(ParamIndex(3)), "4", ""); - ui_interface.declare(Some(ParamIndex(3)), "midi", "ctrl 3"); + ui_interface.add_vertical_slider("Depth", ParamIndex(2), 0.5, 0.0, 1.0, 0.001); + ui_interface.declare(Some(ParamIndex(3)), "6", ""); + ui_interface.declare(Some(ParamIndex(3)), "midi", "ctrl 58"); ui_interface.declare(Some(ParamIndex(3)), "style", "knob"); - ui_interface.add_vertical_slider("Depth", ParamIndex(3), 0.5, 0.0, 1.0, 0.001); - ui_interface.declare(Some(ParamIndex(4)), "6", ""); - ui_interface.declare(Some(ParamIndex(4)), "midi", "ctrl 58"); - ui_interface.declare(Some(ParamIndex(4)), "style", "knob"); - ui_interface.add_vertical_slider("Deviation", ParamIndex(4), 0.5, 0.0, 1.0, 0.001); + ui_interface.add_vertical_slider("Deviation", ParamIndex(3), 0.5, 0.0, 1.0, 0.001); ui_interface.close_box(); } fn get_param(&self, param: ParamIndex) -> Option { match param.0 { - 1 => Some(self.fVslider0), - 3 => Some(self.fVslider1), - 0 => Some(self.fVslider2), - 4 => Some(self.fVslider3), - 2 => Some(self.fVslider4), + 2 => Some(self.fVslider0), + 0 => Some(self.fVslider1), + 3 => Some(self.fVslider2), + 1 => Some(self.fVslider3), _ => None, } } fn set_param(&mut self, param: ParamIndex, value: Self::T) { match param.0 { - 1 => self.fVslider0 = value, - 3 => self.fVslider1 = value, - 0 => self.fVslider2 = value, - 4 => self.fVslider3 = value, - 2 => self.fVslider4 = value, + 2 => self.fVslider0 = value, + 0 => self.fVslider1 = value, + 3 => self.fVslider2 = value, + 1 => self.fVslider3 = value, _ => {} } } @@ -359,30 +349,28 @@ impl FaustDsp for Chorus { } else { panic!("wrong number of outputs"); }; - let mut iSlow0: i32 = i32::wrapping_sub(1, (self.fVslider0) as i32); - let mut fSlow1: F32 = self.fConst2 * self.fVslider1; - let mut fSlow2: F32 = 4.096 * self.fVslider2; - let mut fSlow3: F32 = 6.25e-05 * self.fVslider3; - let mut fSlow4: F32 = self.fConst2 * self.fVslider4; + let mut fSlow0: F32 = self.fConst2 * self.fVslider0; + let mut fSlow1: F32 = 4.096 * self.fVslider1; + let mut fSlow2: F32 = 6.25e-05 * self.fVslider2; + let mut fSlow3: F32 = self.fConst2 * self.fVslider3; let zipped_iterators = inputs0.zip(outputs0).zip(outputs1); for ((input0, output0), output1) in zipped_iterators { - self.iVec0[0] = 1; - self.fRec0[0] = fSlow1 + self.fConst1 * self.fRec0[1]; let mut fTemp0: F32 = *input0; - let mut fTemp1: F32 = if iSlow0 != 0 { 0.0 } else { fTemp0 }; - let mut fTemp2: F32 = self.fRec0[0] * fTemp1; - self.fVec1[(self.IOTA0 & 8191) as usize] = fTemp2; - self.fRec1[0] = fSlow2 + 0.999 * self.fRec1[1]; - self.fRec2[0] = fSlow3 * self.fRec1[0] + 0.999 * self.fRec2[1]; - let mut iTemp3: i32 = i32::wrapping_sub(1, self.iVec0[1]); - self.fRec5[0] = fSlow4 + self.fConst1 * self.fRec5[1]; - let mut fTemp4: F32 = if iTemp3 != 0 { + self.iVec0[0] = 1; + self.fRec0[0] = fSlow0 + self.fConst1 * self.fRec0[1]; + let mut fTemp1: F32 = fTemp0 * self.fRec0[0]; + self.fVec1[(self.IOTA0 & 8191) as usize] = fTemp1; + self.fRec1[0] = fSlow1 + 0.999 * self.fRec1[1]; + self.fRec2[0] = fSlow2 * self.fRec1[0] + 0.999 * self.fRec2[1]; + let mut iTemp2: i32 = i32::wrapping_sub(1, self.iVec0[1]); + self.fRec5[0] = fSlow3 + self.fConst1 * self.fRec5[1]; + let mut fTemp3: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec4[1] + self.fConst3 * self.fRec5[0] }; - self.fRec4[0] = fTemp4 - F32::floor(fTemp4); - let mut fTemp5: F32 = F32::min( + self.fRec4[0] = fTemp3 - F32::floor(fTemp3); + let mut fTemp4: F32 = F32::min( 4096.0, 0.375 * self.fRec1[0] + self.fRec2[0] @@ -393,15 +381,15 @@ impl FaustDsp for Chorus { )) as usize] }, ); - let mut iTemp6: i32 = (fTemp5) as i32; - let mut fTemp7: F32 = F32::floor(fTemp5); - let mut fTemp8: F32 = if iTemp3 != 0 { + let mut iTemp5: i32 = (fTemp4) as i32; + let mut fTemp6: F32 = F32::floor(fTemp4); + let mut fTemp7: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec7[1] + self.fConst4 * self.fRec5[0] }; - self.fRec7[0] = fTemp8 - F32::floor(fTemp8); - let mut fTemp9: F32 = F32::min( + self.fRec7[0] = fTemp7 - F32::floor(fTemp7); + let mut fTemp8: F32 = F32::min( 4096.0, 0.125 * self.fRec1[0] + self.fRec2[0] @@ -412,16 +400,16 @@ impl FaustDsp for Chorus { )) as usize] }, ); - let mut fTemp10: F32 = F32::floor(fTemp9); - let mut iTemp11: i32 = (fTemp9) as i32; - let mut fTemp12: F32 = fTemp1 * (1.0 - self.fRec0[0]); - let mut fTemp13: F32 = if iTemp3 != 0 { + let mut fTemp9: F32 = F32::floor(fTemp8); + let mut iTemp10: i32 = (fTemp8) as i32; + let mut fTemp11: F32 = fTemp0 * (1.0 - self.fRec0[0]); + let mut fTemp12: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec8[1] + self.fConst5 * self.fRec5[0] }; - self.fRec8[0] = fTemp13 - F32::floor(fTemp13); - let mut fTemp14: F32 = F32::min( + self.fRec8[0] = fTemp12 - F32::floor(fTemp12); + let mut fTemp13: F32 = F32::min( 4096.0, 0.875 * self.fRec1[0] - self.fRec2[0] @@ -432,180 +420,172 @@ impl FaustDsp for Chorus { )) as usize] }, ); - let mut iTemp15: i32 = (fTemp14) as i32; - let mut fTemp16: F32 = F32::floor(fTemp14); - *output0 = if iSlow0 != 0 { - fTemp0 - } else { - 0.70710677 + let mut iTemp14: i32 = (fTemp13) as i32; + let mut fTemp15: F32 = F32::floor(fTemp13); + + *output0 = 0.70710677 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp5)), + )) & 8191) as usize] + * (fTemp6 + (1.0 - fTemp4)) + + (fTemp4 - fTemp6) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp5, 1))), + )) & 8191) as usize]) + + (fTemp8 - fTemp9) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp10, 1))), + )) & 8191) as usize] + + fTemp11 + + self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp10)), + )) & 8191) as usize] + * (fTemp9 + (1.0 - fTemp8)) + - 0.70710677 * (self.fVec1[((i32::wrapping_sub( self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp6)), + std::cmp::min(4097, std::cmp::max(0, iTemp14)), )) & 8191) as usize] - * (fTemp7 + (1.0 - fTemp5)) - + (fTemp5 - fTemp7) + * (fTemp15 + (1.0 - fTemp13)) + + (fTemp13 - fTemp15) * self.fVec1[((i32::wrapping_sub( self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp6, 1))), - )) & 8191) as usize]) - + (fTemp9 - fTemp10) - * self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, i32::wrapping_add(iTemp11, 1))), - )) & 8191) as usize] - + fTemp12 - + self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp11)), - )) & 8191) as usize] - * (fTemp10 + (1.0 - fTemp9)) - - 0.70710677 - * (self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp15)), - )) & 8191) as usize] - * (fTemp16 + (1.0 - fTemp14)) - + (fTemp14 - fTemp16) - * self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min( - 4097, - std::cmp::max(0, i32::wrapping_add(iTemp15, 1)), - ), - )) & 8191) as usize]) - }; - let mut fTemp17: F32 = if iTemp3 != 0 { + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp14, 1)), + ), + )) & 8191) as usize]); + + let mut fTemp16: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec9[1] + self.fConst6 * self.fRec5[0] }; - self.fRec9[0] = fTemp17 - F32::floor(fTemp17); - let mut iTemp18: i32 = + self.fRec9[0] = fTemp16 - F32::floor(fTemp16); + let mut iTemp17: i32 = std::cmp::max(0, std::cmp::min((65536.0 * self.fRec9[0]) as i32, 65535)); - let mut fTemp19: F32 = F32::min( + let mut fTemp18: F32 = F32::min( 4096.0, 0.25 * self.fRec1[0] + self.fRec2[0] - * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp18 as usize] } - + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp18 as usize] }), + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp17 as usize] } + + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp17 as usize] }), ); - let mut iTemp20: i32 = (fTemp19) as i32; - let mut fTemp21: F32 = F32::floor(fTemp19); - let mut fTemp22: F32 = if iTemp3 != 0 { + let mut iTemp19: i32 = (fTemp18) as i32; + let mut fTemp20: F32 = F32::floor(fTemp18); + let mut fTemp21: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec10[1] + self.fConst7 * self.fRec5[0] }; - self.fRec10[0] = fTemp22 - F32::floor(fTemp22); - let mut iTemp23: i32 = + self.fRec10[0] = fTemp21 - F32::floor(fTemp21); + let mut iTemp22: i32 = std::cmp::max(0, std::cmp::min((65536.0 * self.fRec10[0]) as i32, 65535)); - let mut fTemp24: F32 = F32::min( + let mut fTemp23: F32 = F32::min( 4096.0, 0.5 * self.fRec1[0] + self.fRec2[0] - * (0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp23 as usize] } - - 0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp23 as usize] }), + * (0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp22 as usize] } + - 0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp22 as usize] }), ); - let mut iTemp25: i32 = (fTemp24) as i32; - let mut fTemp26: F32 = F32::floor(fTemp24); - let mut fTemp27: F32 = if iTemp3 != 0 { + let mut iTemp24: i32 = (fTemp23) as i32; + let mut fTemp25: F32 = F32::floor(fTemp23); + let mut fTemp26: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec11[1] + self.fConst8 * self.fRec5[0] }; - self.fRec11[0] = fTemp27 - F32::floor(fTemp27); - let mut iTemp28: i32 = + self.fRec11[0] = fTemp26 - F32::floor(fTemp26); + let mut iTemp27: i32 = std::cmp::max(0, std::cmp::min((65536.0 * self.fRec11[0]) as i32, 65535)); - let mut fTemp29: F32 = F32::min( + let mut fTemp28: F32 = F32::min( 4096.0, 0.75 * self.fRec1[0] - self.fRec2[0] - * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp28 as usize] } - + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp28 as usize] }), + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp27 as usize] } + + 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp27 as usize] }), ); - let mut iTemp30: i32 = (fTemp29) as i32; - let mut fTemp31: F32 = F32::floor(fTemp29); - let mut fTemp32: F32 = if iTemp3 != 0 { + let mut iTemp29: i32 = (fTemp28) as i32; + let mut fTemp30: F32 = F32::floor(fTemp28); + let mut fTemp31: F32 = if iTemp2 != 0 { 0.0 } else { self.fRec12[1] + self.fConst9 * self.fRec5[0] }; - self.fRec12[0] = fTemp32 - F32::floor(fTemp32); - let mut iTemp33: i32 = + self.fRec12[0] = fTemp31 - F32::floor(fTemp31); + let mut iTemp32: i32 = std::cmp::max(0, std::cmp::min((65536.0 * self.fRec12[0]) as i32, 65535)); - let mut fTemp34: F32 = F32::min( + let mut fTemp33: F32 = F32::min( 4096.0, self.fRec1[0] + self.fRec2[0] - * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp33 as usize] } - - 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp33 as usize] }), + * (0.70710677 * unsafe { ftbl1ChorusSIG1[iTemp32 as usize] } + - 0.70710677 * unsafe { ftbl0ChorusSIG0[iTemp32 as usize] }), ); - let mut iTemp35: i32 = (fTemp34) as i32; - let mut fTemp36: F32 = F32::floor(fTemp34); - *output1 = if iSlow0 != 0 { - fTemp0 - } else { - fTemp12 - - (0.38268343 + let mut iTemp34: i32 = (fTemp33) as i32; + let mut fTemp35: F32 = F32::floor(fTemp33); + + *output1 = fTemp11 + - (0.38268343 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp19)), + )) & 8191) as usize] + * (fTemp20 + (1.0 - fTemp18)) + + (fTemp18 - fTemp20) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp19, 1)), + ), + )) & 8191) as usize]) + + 0.9238795 * (self.fVec1[((i32::wrapping_sub( self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp20)), + std::cmp::min(4097, std::cmp::max(0, iTemp24)), )) & 8191) as usize] - * (fTemp21 + (1.0 - fTemp19)) - + (fTemp19 - fTemp21) + * (fTemp25 + (1.0 - fTemp23)) + + (fTemp23 - fTemp25) * self.fVec1[((i32::wrapping_sub( self.IOTA0, std::cmp::min( 4097, - std::cmp::max(0, i32::wrapping_add(iTemp20, 1)), + std::cmp::max(0, i32::wrapping_add(iTemp24, 1)), ), )) & 8191) as usize]) - + 0.9238795 - * (self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp25)), - )) & 8191) as usize] - * (fTemp26 + (1.0 - fTemp24)) - + (fTemp24 - fTemp26) - * self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min( - 4097, - std::cmp::max(0, i32::wrapping_add(iTemp25, 1)), - ), - )) & 8191) - as usize]) - + 0.9238795 - * (self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp30)), - )) & 8191) as usize] - * (fTemp31 + (1.0 - fTemp29)) - + (fTemp29 - fTemp31) - * self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min( - 4097, - std::cmp::max(0, i32::wrapping_add(iTemp30, 1)), - ), - )) & 8191) - as usize]) - + 0.38268343 - * (self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min(4097, std::cmp::max(0, iTemp35)), - )) & 8191) as usize] - * (fTemp36 + (1.0 - fTemp34)) - + (fTemp34 - fTemp36) - * self.fVec1[((i32::wrapping_sub( - self.IOTA0, - std::cmp::min( - 4097, - std::cmp::max(0, i32::wrapping_add(iTemp35, 1)), - ), - )) & 8191) - as usize])) - }; + + 0.9238795 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp29)), + )) & 8191) as usize] + * (fTemp30 + (1.0 - fTemp28)) + + (fTemp28 - fTemp30) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp29, 1)), + ), + )) & 8191) as usize]) + + 0.38268343 + * (self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min(4097, std::cmp::max(0, iTemp34)), + )) & 8191) as usize] + * (fTemp35 + (1.0 - fTemp33)) + + (fTemp33 - fTemp35) + * self.fVec1[((i32::wrapping_sub( + self.IOTA0, + std::cmp::min( + 4097, + std::cmp::max(0, i32::wrapping_add(iTemp34, 1)), + ), + )) & 8191) as usize])); self.iVec0[1] = self.iVec0[0]; self.fRec0[1] = self.fRec0[0]; self.IOTA0 = i32::wrapping_add(self.IOTA0, 1); diff --git a/packages/chorus/src/dsp.ts b/packages/chorus/src/dsp.ts index bdea882..867ff33 100644 --- a/packages/chorus/src/dsp.ts +++ b/packages/chorus/src/dsp.ts @@ -2,296 +2,238 @@ export function createChorus(sampleRate: number) { const ftbl0ChorusSIG0 = new Float32Array(65536); const ftbl1ChorusSIG1 = new Float32Array(65536); - let $iVec0 = new Int32Array(2); - let $fVslider0 = 0.0; - const $fSampleRate = sampleRate; - let $fConst0 = 0.0; - let $fConst1 = 0.0; - let $fConst2 = 0.0; - let $fVslider1 = 0.0; - let $fRec0 = new Float32Array(2); - let $IOTA0 = 0; - let $fVec1 = new Float32Array(8192); - let $fVslider2 = 0.0; - let $fRec1 = new Float32Array(2); - let $fVslider3 = 0.0; - let $fRec2 = new Float32Array(2); - let $fConst3 = 0.0; - let $fVslider4 = 0.0; - let $fRec5 = new Float32Array(2); - let $fRec4 = new Float32Array(2); - let $fConst4 = 0.0; - let $fRec7 = new Float32Array(2); - let $fConst5 = 0.0; - let $fRec8 = new Float32Array(2); - let $fConst6 = 0.0; - let $fRec9 = new Float32Array(2); - let $fConst7 = 0.0; - let $fRec10 = new Float32Array(2); - let $fConst8 = 0.0; - let $fRec11 = new Float32Array(2); - let $fConst9 = 0.0; - let $fRec12 = new Float32Array(2); + const fSampleRate = sampleRate; + let fRec0 = new Float32Array(2); + let fRec1 = new Float32Array(2); + let fRec10 = new Float32Array(2); + let fRec11 = new Float32Array(2); + let fRec12 = new Float32Array(2); + let fRec2 = new Float32Array(2); + let fRec4 = new Float32Array(2); + let fRec5 = new Float32Array(2); + let fRec7 = new Float32Array(2); + let fRec8 = new Float32Array(2); + let fRec9 = new Float32Array(2); + let fVec1 = new Float32Array(8192); + let IOTA0 = 0; + let iVec0 = new Int32Array(2); - $fConst0 = Math.min(1.92e5, Math.max(1.0, $fSampleRate)); - $fConst1 = Math.exp(-(44.12234 / $fConst0)); - $fConst2 = 1.0 - $fConst1; - $fConst3 = 0.33333334 / $fConst0; - $fConst4 = 1.0 / $fConst0; - $fConst5 = 0.14285715 / $fConst0; - $fConst6 = 0.5 / $fConst0; - $fConst7 = 0.25 / $fConst0; - $fConst8 = 0.16666667 / $fConst0; - $fConst9 = 0.125 / $fConst0; - $fVslider0 = 0.5; - $fVslider1 = 1.0; - $fVslider2 = 0.5; - $fVslider3 = 0.5; - $fVslider4 = 0.5; + let fConst0 = Math.min(1.92e5, Math.max(1.0, fSampleRate)); + let fConst1 = Math.exp(-(44.12234 / fConst0)); + let fConst2 = 1.0 - fConst1; + let fConst3 = 0.33333334 / fConst0; + let fConst4 = 1.0 / fConst0; + let fConst5 = 0.14285715 / fConst0; + let fConst6 = 0.5 / fConst0; + let fConst7 = 0.25 / fConst0; + let fConst8 = 0.16666667 / fConst0; + let fConst9 = 0.125 / fConst0; + let fVslider0 = 0.5; + let fVslider1 = 0.5; + let fVslider2 = 0.5; + let fVslider3 = 0.5; fillTable(ftbl0ChorusSIG0, sig0Fn); fillTable(ftbl1ChorusSIG1, sig1Fn); - function update( - delay: number, - rate: number, - depth: number, - deviation: number - ): void { - $fVslider0 = delay; - $fVslider1 = 1; // enable - $fVslider2 = rate; - $fVslider3 = depth; - $fVslider4 = deviation; - } - function compute( - inputMono: Float32Array, - outLeft: Float32Array, - outRight: Float32Array + input0: Float32Array, + output0: Float32Array, + output1: Float32Array ): void { - let iSlow0: number = 1 - Math.floor($fVslider0); - let fSlow1: number = $fConst2 * $fVslider1; - let fSlow2: number = 4.096 * $fVslider2; - let fSlow3: number = 6.25e-5 * $fVslider3; - let fSlow4: number = $fConst2 * $fVslider4; - - for (let i = 0; i < inputMono.length; i++) { - let input0 = inputMono[i]; + let fSlow0 = fConst2 * fVslider0; + let fSlow1 = 4.096 * fVslider1; + let fSlow2 = 6.25e-5 * fVslider2; + let fSlow3 = fConst2 * fVslider3; + for (let i = 0; i < input0.length; i++) { + // Assume all necessary variables exist (fRec0, fRec1, fRec2, fRec4, fRec5, fRec7, fRec8, fVec1, ftbl0ChorusSIG0, ftbl1ChorusSIG1, IOTA0, etc.) - $iVec0[0] = 1; - $fRec0[0] = fSlow1 + $fConst1 * $fRec0[1]; - let fTemp0: number = input0; - let fTemp1: number = iSlow0 !== 0 ? 0.0 : fTemp0; - let fTemp2: number = $fRec0[0] * fTemp1; - $fVec1[$IOTA0 & 8191] = fTemp2; - $fRec1[0] = fSlow2 + 0.999 * $fRec1[1]; - $fRec2[0] = fSlow3 * $fRec1[0] + 0.999 * $fRec2[1]; + let fTemp0 = input0[i]; // Assume input0 is an array and we're in a loop + iVec0[0] = 1; + fRec0[0] = fSlow0 + fConst1 * fRec0[1]; + let fTemp1 = fTemp0 * fRec0[0]; + fVec1[IOTA0 & 8191] = fTemp1; - let iTemp3: number = 1 - $iVec0[1]; - $fRec5[0] = fSlow4 + $fConst1 * $fRec5[1]; - let fTemp4: number = - iTemp3 !== 0 ? 0.0 : $fRec4[1] + $fConst3 * $fRec5[0]; - $fRec4[0] = fTemp4 - Math.floor(fTemp4); - - let fTemp5: number = Math.min( + fRec1[0] = fSlow1 + 0.999 * fRec1[1]; + fRec2[0] = fSlow2 * fRec1[0] + 0.999 * fRec2[1]; + // Simulate i32::wrapping_sub: not sure if needed + //let iTemp2 = (1 - iVec0[1] + 4294967296) % 4294967296; + let iTemp2 = 1 - iVec0[1]; + fRec5[0] = fSlow3 + fConst1 * fRec5[1]; + let fTemp3 = iTemp2 !== 0 ? 0.0 : fRec4[1] + fConst3 * fRec5[0]; + fRec4[0] = fTemp3 - Math.floor(fTemp3); + let fTemp4 = Math.min( 4096.0, - 0.375 * $fRec1[0] + - $fRec2[0] * + 0.375 * fRec1[0] + + fRec2[0] * ftbl0ChorusSIG0[ - Math.max(0, Math.min(Math.floor(65536.0 * $fRec4[0]), 65535)) + Math.max(0, Math.min(Math.floor(65536.0 * fRec4[0]), 65535)) ] ); - let iTemp6: number = Math.floor(fTemp5); - let fTemp7: number = Math.floor(fTemp5); - - let fTemp8: number = - iTemp3 !== 0 ? 0.0 : $fRec7[1] + $fConst4 * $fRec5[0]; - $fRec7[0] = fTemp8 - Math.floor(fTemp8); - - let fTemp9: number = Math.min( + let iTemp5 = Math.floor(fTemp4); + let fTemp6 = Math.floor(fTemp4); + let fTemp7 = iTemp2 !== 0 ? 0.0 : fRec7[1] + fConst4 * fRec5[0]; + fRec7[0] = fTemp7 - Math.floor(fTemp7); + let fTemp8 = Math.min( 4096.0, - 0.125 * $fRec1[0] + - $fRec2[0] * + 0.125 * fRec1[0] + + fRec2[0] * ftbl1ChorusSIG1[ - Math.max(0, Math.min(Math.floor(65536.0 * $fRec7[0]), 65535)) + Math.max(0, Math.min(Math.floor(65536.0 * fRec7[0]), 65535)) ] ); - let fTemp10: number = Math.floor(fTemp9); - let iTemp11: number = Math.floor(fTemp9); - - let fTemp12: number = fTemp1 * (1.0 - $fRec0[0]); - - let fTemp13: number = - iTemp3 !== 0 ? 0.0 : $fRec8[1] + $fConst5 * $fRec5[0]; - $fRec8[0] = fTemp13 - Math.floor(fTemp13); - - let fTemp14: number = Math.min( + let fTemp9 = Math.floor(fTemp8); + let iTemp10 = Math.floor(fTemp8); + let fTemp11 = fTemp0 * (1.0 - fRec0[0]); + let fTemp12 = iTemp2 !== 0 ? 0.0 : fRec8[1] + fConst5 * fRec5[0]; + fRec8[0] = fTemp12 - Math.floor(fTemp12); + let fTemp13 = Math.min( 4096.0, - 0.875 * $fRec1[0] - - $fRec2[0] * + 0.875 * fRec1[0] - + fRec2[0] * ftbl0ChorusSIG0[ - Math.max(0, Math.min(Math.floor(65536.0 * $fRec8[0]), 65535)) + Math.max(0, Math.min(Math.floor(65536.0 * fRec8[0]), 65535)) ] ); - let iTemp15: number = Math.floor(fTemp14); - let fTemp16: number = Math.floor(fTemp14); + let iTemp14 = Math.floor(fTemp13); + let fTemp15 = Math.floor(fTemp13); - outLeft[i] = - iSlow0 !== 0 - ? fTemp0 - : 0.70710677 * - ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp6))) & 8191] * - (fTemp7 + (1.0 - fTemp5)) + - (fTemp5 - fTemp7) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp6 + 1))) & 8191 - ]) + - (fTemp9 - fTemp10) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp11 + 1))) & 8191 - ] + - fTemp12 + - $fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp11))) & 8191] * - (fTemp10 + (1.0 - fTemp9)) - - 0.70710677 * - ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp15))) & 8191] * - (fTemp16 + (1.0 - fTemp14)) + - (fTemp14 - fTemp16) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp15 + 1))) & 8191 - ]); + output0[i] = + 0.70710677 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp5))) & 8191] * + (fTemp6 + (1.0 - fTemp4)) + + (fTemp4 - fTemp6) * + fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp5 + 1))) & 8191]) + + (fTemp8 - fTemp9) * + fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp10 + 1))) & 8191] + + fTemp11 + + fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp10))) & 8191] * + (fTemp9 + (1.0 - fTemp8)) - + 0.70710677 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp14))) & 8191] * + (fTemp15 + (1.0 - fTemp13)) + + (fTemp13 - fTemp15) * + fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp14 + 1))) & 8191]); - let fTemp17: number = - iTemp3 !== 0 ? 0.0 : $fRec9[1] + $fConst6 * $fRec5[0]; - $fRec9[0] = fTemp17 - Math.floor(fTemp17); - - let iTemp18: number = Math.max( + let fTemp16 = iTemp2 !== 0 ? 0.0 : fRec9[1] + fConst6 * fRec5[0]; + fRec9[0] = fTemp16 - Math.floor(fTemp16); + let iTemp17 = Math.max( 0, - Math.min(Math.floor(65536.0 * $fRec9[0]), 65535) + Math.min(Math.floor(65536.0 * fRec9[0]), 65535) ); - - let fTemp19: number = Math.min( + let fTemp18 = Math.min( 4096.0, - 0.25 * $fRec1[0] + - $fRec2[0] * - (0.70710677 * ftbl1ChorusSIG1[iTemp18] + - 0.70710677 * ftbl0ChorusSIG0[iTemp18]) + 0.25 * fRec1[0] + + fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp17] + + 0.70710677 * ftbl0ChorusSIG0[iTemp17]) ); - let iTemp20: number = Math.floor(fTemp19); - let fTemp21: number = Math.floor(fTemp19); - - let fTemp22: number = - iTemp3 !== 0 ? 0.0 : $fRec10[1] + $fConst7 * $fRec5[0]; - $fRec10[0] = fTemp22 - Math.floor(fTemp22); - - let iTemp23: number = Math.max( + let iTemp19 = Math.floor(fTemp18); + let fTemp20 = Math.floor(fTemp18); + let fTemp21 = iTemp2 !== 0 ? 0.0 : fRec10[1] + fConst7 * fRec5[0]; + fRec10[0] = fTemp21 - Math.floor(fTemp21); + let iTemp22 = Math.max( 0, - Math.min(Math.floor(65536.0 * $fRec10[0]), 65535) + Math.min(Math.floor(65536.0 * fRec10[0]), 65535) ); - - let fTemp24: number = Math.min( + let fTemp23 = Math.min( 4096.0, - 0.5 * $fRec1[0] + - $fRec2[0] * - (0.70710677 * ftbl0ChorusSIG0[iTemp23] - - 0.70710677 * ftbl1ChorusSIG1[iTemp23]) + 0.5 * fRec1[0] + + fRec2[0] * + (0.70710677 * ftbl0ChorusSIG0[iTemp22] - + 0.70710677 * ftbl1ChorusSIG1[iTemp22]) ); - let iTemp25: number = Math.floor(fTemp24); - let fTemp26: number = Math.floor(fTemp24); - - let fTemp27: number = - iTemp3 !== 0 ? 0.0 : $fRec11[1] + $fConst8 * $fRec5[0]; - $fRec11[0] = fTemp27 - Math.floor(fTemp27); - - let iTemp28: number = Math.max( + let iTemp24 = Math.floor(fTemp23); + let fTemp25 = Math.floor(fTemp23); + let fTemp26 = iTemp2 !== 0 ? 0.0 : fRec11[1] + fConst8 * fRec5[0]; + fRec11[0] = fTemp26 - Math.floor(fTemp26); + let iTemp27 = Math.max( 0, - Math.min(Math.floor(65536.0 * $fRec11[0]), 65535) + Math.min(Math.floor(65536.0 * fRec11[0]), 65535) ); - - let fTemp29: number = Math.min( + let fTemp28 = Math.min( 4096.0, - 0.75 * $fRec1[0] - - $fRec2[0] * - (0.70710677 * ftbl1ChorusSIG1[iTemp28] + - 0.70710677 * ftbl0ChorusSIG0[iTemp28]) + 0.75 * fRec1[0] - + fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp27] + + 0.70710677 * ftbl0ChorusSIG0[iTemp27]) ); - let iTemp30: number = Math.floor(fTemp29); - let fTemp31: number = Math.floor(fTemp29); - - let fTemp32: number = - iTemp3 !== 0 ? 0.0 : $fRec12[1] + $fConst9 * $fRec5[0]; - $fRec12[0] = fTemp32 - Math.floor(fTemp32); - - let iTemp33: number = Math.max( + let iTemp29 = Math.floor(fTemp28); + let fTemp30 = Math.floor(fTemp28); + let fTemp31 = iTemp2 !== 0 ? 0.0 : fRec12[1] + fConst9 * fRec5[0]; + fRec12[0] = fTemp31 - Math.floor(fTemp31); + let iTemp32 = Math.max( 0, - Math.min(Math.floor(65536.0 * $fRec12[0]), 65535) + Math.min(Math.floor(65536.0 * fRec12[0]), 65535) ); - - let fTemp34: number = Math.min( + let fTemp33 = Math.min( 4096.0, - $fRec1[0] + - $fRec2[0] * - (0.70710677 * ftbl1ChorusSIG1[iTemp33] - - 0.70710677 * ftbl0ChorusSIG0[iTemp33]) + fRec1[0] + + fRec2[0] * + (0.70710677 * ftbl1ChorusSIG1[iTemp32] - + 0.70710677 * ftbl0ChorusSIG0[iTemp32]) ); - let iTemp35: number = Math.floor(fTemp34); - let fTemp36: number = Math.floor(fTemp34); + let iTemp34 = Math.floor(fTemp33); + let fTemp35 = Math.floor(fTemp33); - outRight[i] = - iSlow0 !== 0 - ? fTemp0 - : fTemp12 - - (0.38268343 * - ($fVec1[($IOTA0 - Math.min(4097, Math.max(0, iTemp20))) & 8191] * - (fTemp21 + (1.0 - fTemp19)) + - (fTemp19 - fTemp21) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp20 + 1))) & 8191 - ]) + - 0.9238795 * - ($fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp25))) & 8191 - ] * - (fTemp26 + (1.0 - fTemp24)) + - (fTemp24 - fTemp26) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp25 + 1))) & 8191 - ]) + - 0.9238795 * - ($fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp30))) & 8191 - ] * - (fTemp31 + (1.0 - fTemp29)) + - (fTemp29 - fTemp31) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp30 + 1))) & 8191 - ]) + - 0.38268343 * - ($fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp35))) & 8191 - ] * - (fTemp36 + (1.0 - fTemp34)) + - (fTemp34 - fTemp36) * - $fVec1[ - ($IOTA0 - Math.min(4097, Math.max(0, iTemp35 + 1))) & 8191 - ])); + output1[i] = + fTemp11 - + (0.38268343 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp19))) & 8191] * + (fTemp20 + (1.0 - fTemp18)) + + (fTemp18 - fTemp20) * + fVec1[ + (IOTA0 - Math.min(4097, Math.max(0, iTemp19 + 1))) & 8191 + ]) + + 0.9238795 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp24))) & 8191] * + (fTemp25 + (1.0 - fTemp23)) + + (fTemp23 - fTemp25) * + fVec1[ + (IOTA0 - Math.min(4097, Math.max(0, iTemp24 + 1))) & 8191 + ]) + + 0.9238795 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp29))) & 8191] * + (fTemp30 + (1.0 - fTemp28)) + + (fTemp28 - fTemp30) * + fVec1[ + (IOTA0 - Math.min(4097, Math.max(0, iTemp29 + 1))) & 8191 + ]) + + 0.38268343 * + (fVec1[(IOTA0 - Math.min(4097, Math.max(0, iTemp34))) & 8191] * + (fTemp35 + (1.0 - fTemp33)) + + (fTemp33 - fTemp35) * + fVec1[ + (IOTA0 - Math.min(4097, Math.max(0, iTemp34 + 1))) & 8191 + ])); - $iVec0[1] = $iVec0[0]; - $fRec0[1] = $fRec0[0]; - $IOTA0 = ($IOTA0 + 1) & 8191; - $fRec1[1] = $fRec1[0]; - $fRec2[1] = $fRec2[0]; - $fRec5[1] = $fRec5[0]; - $fRec4[1] = $fRec4[0]; - $fRec7[1] = $fRec7[0]; - $fRec8[1] = $fRec8[0]; - $fRec9[1] = $fRec9[0]; - $fRec10[1] = $fRec10[0]; - $fRec11[1] = $fRec11[0]; - $fRec12[1] = $fRec12[0]; + iVec0[1] = iVec0[0]; + fRec0[1] = fRec0[0]; + IOTA0 = (IOTA0 + 1) >>> 0; // Wrapping addition + fRec1[1] = fRec1[0]; + fRec2[1] = fRec2[0]; + fRec5[1] = fRec5[0]; + fRec4[1] = fRec4[0]; + fRec7[1] = fRec7[0]; + fRec8[1] = fRec8[0]; + fRec9[1] = fRec9[0]; + fRec10[1] = fRec10[0]; + fRec11[1] = fRec11[0]; + fRec12[1] = fRec12[0]; } } + function update( + delay: number, + rate: number, + depth: number, + deviation: number + ): void { + fVslider0 = delay; + fVslider1 = rate; + fVslider2 = depth; + fVslider3 = deviation; + } + return { update, compute }; } diff --git a/packages/chorus/src/processor.ts b/packages/chorus/src/processor.ts index 7f7fc4a..65e2b6b 100644 --- a/packages/chorus/src/processor.ts +++ b/packages/chorus/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function we(h){let r=new Float32Array(65536),t=new Float32Array(65536),i=new Int32Array(2),o=0,M=0,a=0,F=0,k=0,P=0,s=new Float32Array(2),e=0,l=new Float32Array(8192),B=0,m=new Float32Array(2),D=0,n=new Float32Array(2),j=0,E=0,f=new Float32Array(2),p=new Float32Array(2),q=0,b=new Float32Array(2),z=0,c=new Float32Array(2),H=0,T=new Float32Array(2),J=0,$=new Float32Array(2),K=0,x=new Float32Array(2),N=0,y=new Float32Array(2);function Fe(){M=h,a=Math.min(192e3,Math.max(1,M)),F=Math.exp(-(44.12234/a)),k=1-F,j=.33333334/a,q=1/a,z=.14285715/a,H=.5/a,J=.25/a,K=.16666667/a,N=.125/a,Ae(r,ve),Ae(t,ke),o=.5,P=1,B=.5,D=.5,E=.5}function Pe(R,G,O,A){o=R,P=1,B=G,D=O,E=A}function Re(R,G,O){let A=1-Math.floor(o),Ce=k*P,ge=4.096*B,de=625e-7*D,Se=k*E;for(let w=0;wMath.cos(958738e-10*h),ke=h=>Math.sin(958738e-10*h);function Ae(h,r){if(h.length!==65536)throw new Error("Table must be 65536 samples long");let t=0,i=0,o=0,M=0;for(let a=0;a<65536;a++)t=1,o=(i+M)%65536,h[a]=Math.cos(958738e-10*o),i=t,M=o;return h}var W=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:r}=we(sampleRate);this.g=r,this.port.onmessage=t=>{switch(t.data.type){case"DISPOSE":this.r=!1;break}}}process(r,t,i){if(r[0].length===0)return this.r;let o=r[0][0],M=t[0][0],a=t[0][1];return this.g(o,M,a),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([r,t,i,o])=>({name:r,defaultValue:t,minValue:i,maxValue:o,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",W);})();`; +export const PROCESSOR = `"use strict";(()=>{function pt(f){let l=new Float32Array(65536),a=new Float32Array(65536),i=f,r=new Float32Array(2),t=new Float32Array(2),m=new Float32Array(2),p=new Float32Array(2),c=new Float32Array(2),h=new Float32Array(2),T=new Float32Array(2),n=new Float32Array(2),u=new Float32Array(2),x=new Float32Array(2),y=new Float32Array(2),o=new Float32Array(8192),e=0,w=new Int32Array(2),M=Math.min(192e3,Math.max(1,i)),I=Math.exp(-(44.12234/M)),D=1-I,ct=.33333334/M,Tt=1/M,ut=.14285715/M,xt=.5/M,yt=.25/M,At=.16666667/M,wt=.125/M,E=.5,G=.5,O=.5,L=.5;st(l,St),st(a,Vt);function Ft(F,k,v){let P=D*E,Ct=4.096*G,gt=625e-7*O,bt=D*L;for(let A=0;A>>0,t[1]=t[0],h[1]=h[0],n[1]=n[0],T[1]=T[0],u[1]=u[0],x[1]=x[0],y[1]=y[0],m[1]=m[0],p[1]=p[0],c[1]=c[0]}}function Rt(F,k,v,P){E=F,G=k,O=v,L=P}return{update:Rt,compute:Ft}}var St=f=>Math.cos(958738e-10*f),Vt=f=>Math.sin(958738e-10*f);function st(f,l){if(f.length!==65536)throw new Error("Table must be 65536 samples long");let a=0,i=0,r=0,t=0;for(let m=0;m<65536;m++)a=1,r=(i+t)%65536,f[m]=Math.cos(958738e-10*r),i=a,t=r;return f}var B=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:l}=pt(sampleRate);this.g=l,this.port.onmessage=a=>{switch(a.data.type){case"DISPOSE":this.r=!1;break}}}process(l,a,i){if(l[0].length===0)return this.r;let r=l[0][0],t=a[0][0],m=a[0][1];return this.g(r,t,m),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([l,a,i,r])=>({name:l,defaultValue:a,minValue:i,maxValue:r,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",B);})();`; From 2a925573f329e400bb85dd61560bd2008ed05cb6 Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 20:34:36 +0200 Subject: [PATCH 4/5] feat: expose parameters --- packages/chorus/README.md | 2 +- packages/chorus/src/index.ts | 12 ++++++-- packages/chorus/src/processor.ts | 2 +- packages/chorus/src/worklet.ts | 38 ++++++++++++++---------- site/content/docs/(effects)/chorus-t.mdx | 4 +++ site/content/docs/(effects)/chorus.mdx | 13 ++++++-- site/examples/ChorusExample.tsx | 23 ++++++++++++++ 7 files changed, 72 insertions(+), 22 deletions(-) diff --git a/packages/chorus/README.md b/packages/chorus/README.md index 251fe7f..73bbb83 100644 --- a/packages/chorus/README.md +++ b/packages/chorus/README.md @@ -1,5 +1,5 @@ # @synthlet/chorus -> Chorus mono effect written in Faust and ported to the web +> Chorus effect written in Faust and ported to the web Part of [Synthlet](https://github.com/danigb/synthlet) diff --git a/packages/chorus/src/index.ts b/packages/chorus/src/index.ts index 39e4951..9425664 100644 --- a/packages/chorus/src/index.ts +++ b/packages/chorus/src/index.ts @@ -8,18 +8,24 @@ import { PROCESSOR } from "./processor"; export const registerChorusWorklet = createRegistrar("CHORUS", PROCESSOR); export type ChorusInputs = { - trigger: ParamInput; + delay: ParamInput; + rate: ParamInput; + depth: ParamInput; + deviation: ParamInput; }; export type ChorusWorkletNode = AudioWorkletNode & { - trigger: AudioParam; + delay: AudioParam; + rate: AudioParam; + depth: AudioParam; + deviation: AudioParam; dispose(): void; }; export const Chorus = createWorkletConstructor( { processorName: "ChorusProcessor", - paramNames: ["trigger"], + paramNames: ["delay", "rate", "depth", "deviation"], workletOptions: () => ({ numberOfInputs: 1, numberOfOutputs: 1, diff --git a/packages/chorus/src/processor.ts b/packages/chorus/src/processor.ts index 65e2b6b..74ffb48 100644 --- a/packages/chorus/src/processor.ts +++ b/packages/chorus/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function pt(f){let l=new Float32Array(65536),a=new Float32Array(65536),i=f,r=new Float32Array(2),t=new Float32Array(2),m=new Float32Array(2),p=new Float32Array(2),c=new Float32Array(2),h=new Float32Array(2),T=new Float32Array(2),n=new Float32Array(2),u=new Float32Array(2),x=new Float32Array(2),y=new Float32Array(2),o=new Float32Array(8192),e=0,w=new Int32Array(2),M=Math.min(192e3,Math.max(1,i)),I=Math.exp(-(44.12234/M)),D=1-I,ct=.33333334/M,Tt=1/M,ut=.14285715/M,xt=.5/M,yt=.25/M,At=.16666667/M,wt=.125/M,E=.5,G=.5,O=.5,L=.5;st(l,St),st(a,Vt);function Ft(F,k,v){let P=D*E,Ct=4.096*G,gt=625e-7*O,bt=D*L;for(let A=0;A>>0,t[1]=t[0],h[1]=h[0],n[1]=n[0],T[1]=T[0],u[1]=u[0],x[1]=x[0],y[1]=y[0],m[1]=m[0],p[1]=p[0],c[1]=c[0]}}function Rt(F,k,v,P){E=F,G=k,O=v,L=P}return{update:Rt,compute:Ft}}var St=f=>Math.cos(958738e-10*f),Vt=f=>Math.sin(958738e-10*f);function st(f,l){if(f.length!==65536)throw new Error("Table must be 65536 samples long");let a=0,i=0,r=0,t=0;for(let m=0;m<65536;m++)a=1,r=(i+t)%65536,f[m]=Math.cos(958738e-10*r),i=a,t=r;return f}var B=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:l}=pt(sampleRate);this.g=l,this.port.onmessage=a=>{switch(a.data.type){case"DISPOSE":this.r=!1;break}}}process(l,a,i){if(l[0].length===0)return this.r;let r=l[0][0],t=a[0][0],m=a[0][1];return this.g(r,t,m),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([l,a,i,r])=>({name:l,defaultValue:a,minValue:i,maxValue:r,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",B);})();`; +export const PROCESSOR = `"use strict";(()=>{function pt(i){let l=new Float32Array(65536),a=new Float32Array(65536),h=i,r=new Float32Array(2),t=new Float32Array(2),m=new Float32Array(2),p=new Float32Array(2),c=new Float32Array(2),n=new Float32Array(2),u=new Float32Array(2),f=new Float32Array(2),T=new Float32Array(2),y=new Float32Array(2),x=new Float32Array(2),o=new Float32Array(8192),e=0,w=new Int32Array(2),M=Math.min(192e3,Math.max(1,h)),v=Math.exp(-(44.12234/M)),D=1-v,ct=.33333334/M,ut=1/M,Tt=.14285715/M,yt=.5/M,xt=.25/M,At=.16666667/M,wt=.125/M,E=.5,G=.5,O=.5,L=.5;st(l,St),st(a,Vt);function Ft(F,I,k){let P=D*E,Rt=4.096*G,Ct=625e-7*O,bt=D*L;for(let A=0;A>>0,t[1]=t[0],n[1]=n[0],f[1]=f[0],u[1]=u[0],T[1]=T[0],y[1]=y[0],x[1]=x[0],m[1]=m[0],p[1]=p[0],c[1]=c[0]}}function dt(F,I,k,P){E=F,G=I,O=k,L=P}return{update:dt,compute:Ft}}var St=i=>Math.cos(958738e-10*i),Vt=i=>Math.sin(958738e-10*i);function st(i,l){if(i.length!==65536)throw new Error("Table must be 65536 samples long");let a=0,h=0,r=0,t=0;for(let m=0;m<65536;m++)a=1,r=(h+t)%65536,i[m]=Math.cos(958738e-10*r),h=a,t=r;return i}var B=class extends AudioWorkletProcessor{r;u;g;constructor(){super(),this.r=!0;let{compute:l,update:a}=pt(sampleRate);this.u=a,this.g=l,this.port.onmessage=h=>{switch(h.data.type){case"DISPOSE":this.r=!1;break}}}process(l,a,h){if(l[0].length===0)return this.r;this.u(h.delay[0],h.rate[0],h.depth[0],h.deviation[0]);let r=l[0][0],t=a[0][0],m=a[0][1];return this.g(r,t,m),this.r}static get parameterDescriptors(){return[["delay",.5,0,1],["rate",.5,0,1],["depth",.5,0,1],["deviation",.5,0,1]].map(([l,a,h,r])=>({name:l,defaultValue:a,minValue:h,maxValue:r,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",B);})();`; diff --git a/packages/chorus/src/worklet.ts b/packages/chorus/src/worklet.ts index eed3c64..b9fb653 100644 --- a/packages/chorus/src/worklet.ts +++ b/packages/chorus/src/worklet.ts @@ -2,12 +2,14 @@ import { createChorus } from "./dsp"; export class ChorusProcessor extends AudioWorkletProcessor { r: boolean; // running + u: ReturnType["update"]; g: ReturnType["compute"]; constructor() { super(); this.r = true; - const { compute } = createChorus(sampleRate); + const { compute, update } = createChorus(sampleRate); + this.u = update; this.g = compute; this.port.onmessage = (event) => { switch (event.data.type) { @@ -18,12 +20,15 @@ export class ChorusProcessor extends AudioWorkletProcessor { }; } - process( - inputs: Float32Array[][], - outputs: Float32Array[][], - parameters: any - ) { + process(inputs: Float32Array[][], outputs: Float32Array[][], params: any) { if (inputs[0].length === 0) return this.r; + + this.u( + params.delay[0], + params.rate[0], + params.depth[0], + params.deviation[0] + ); const input = inputs[0][0]; const outputL = outputs[0][0]; const outputR = outputs[0][1]; @@ -32,15 +37,18 @@ export class ChorusProcessor extends AudioWorkletProcessor { } static get parameterDescriptors() { - return [["trigger", 0, 0, 1]].map( - ([name, defaultValue, minValue, maxValue]) => ({ - name, - defaultValue, - minValue, - maxValue, - automationRate: "k-rate", - }) - ); + return [ + ["delay", 0.5, 0, 1], + ["rate", 0.5, 0, 1], + ["depth", 0.5, 0, 1], + ["deviation", 0.5, 0, 1], + ].map(([name, defaultValue, minValue, maxValue]) => ({ + name, + defaultValue, + minValue, + maxValue, + automationRate: "k-rate", + })); } } diff --git a/site/content/docs/(effects)/chorus-t.mdx b/site/content/docs/(effects)/chorus-t.mdx index 68b06d0..ae90a45 100644 --- a/site/content/docs/(effects)/chorus-t.mdx +++ b/site/content/docs/(effects)/chorus-t.mdx @@ -5,6 +5,10 @@ description: A chorus effect based on the TAL-Noisemaker one A Chorus effect based on the TAL-Noisemaker that in turn models a Roland Juno-60 chorus. + + This module is WIP. + + ```ts import { ChorusT } from "synthlet"; diff --git a/site/content/docs/(effects)/chorus.mdx b/site/content/docs/(effects)/chorus.mdx index a16f141..8bbf29c 100644 --- a/site/content/docs/(effects)/chorus.mdx +++ b/site/content/docs/(effects)/chorus.mdx @@ -5,6 +5,8 @@ description: A chorus effect implemented in Faust import ChorusExample from "../../../examples/ChorusExample"; +This is a mono-to-stereo chorus effect implemented in Faust and ported to the web. + ```ts import { Chorus } from "synthlet"; @@ -16,10 +18,17 @@ const chorus = Chorus(audioContext, { }); osc.connect(chorus).connect(audioContext.destination); - -chorus.lfoRate1.value = 0.2; ``` ## Parameters + +- `delay`: The delay time [0, 1] +- `rate`: The modulation rate [0, 1] +- `depth`: The modulation depth [0, 1] +- `deviation`: The modulation deviation [0, 1] + +## References + +- [Faust original code](https://github.com/grame-cncm/faust/blob/master-dev/examples/SAM/chorus/chorus.dsp) diff --git a/site/examples/ChorusExample.tsx b/site/examples/ChorusExample.tsx index c934f6a..26a98ba 100644 --- a/site/examples/ChorusExample.tsx +++ b/site/examples/ChorusExample.tsx @@ -3,6 +3,7 @@ import { AdsrAmp, Oscillator, Param } from "synthlet"; import { Chorus } from "../../packages/chorus/src"; import { ExamplePane, GateButton } from "./components/ExamplePane"; +import { Slider } from "./components/Slider"; import { useSynth } from "./useSynth"; function ChorusSynth(context: AudioContext) { @@ -22,6 +23,28 @@ function Example() { return ( <> +

+ + + + +
); From 6c9bfdf7760cc3af0cc0a1b5d2cba163df836ba9 Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 20:41:22 +0200 Subject: [PATCH 5/5] chore: version bump --- packages/chorus-t/CHANGELOG.md | 5 +++-- packages/chorus-t/package.json | 2 +- packages/chorus/package.json | 4 ++-- packages/synthlet/CHANGELOG.md | 14 ++++++++++---- packages/synthlet/package.json | 6 +++--- site/content/docs/(effects)/chorus-t.mdx | 4 ---- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/chorus-t/CHANGELOG.md b/packages/chorus-t/CHANGELOG.md index c602c34..118766b 100644 --- a/packages/chorus-t/CHANGELOG.md +++ b/packages/chorus-t/CHANGELOG.md @@ -1,5 +1,6 @@ # @synthlet/chorus-t -## 0.1.0 +## 0.1.x -Initial release +- Initial release +- Fix registration name diff --git a/packages/chorus-t/package.json b/packages/chorus-t/package.json index 759f946..8cde05e 100644 --- a/packages/chorus-t/package.json +++ b/packages/chorus-t/package.json @@ -1,6 +1,6 @@ { "name": "@synthlet/chorus-t", - "version": "0.1.0", + "version": "0.1.1", "description": "A chorus (based on Tagu Audio Line) audio effect in a worklet", "keywords": [ "chorus", diff --git a/packages/chorus/package.json b/packages/chorus/package.json index 52a7e92..68de25b 100644 --- a/packages/chorus/package.json +++ b/packages/chorus/package.json @@ -1,7 +1,7 @@ { "name": "@synthlet/chorus", - "version": "0.0.0", - "description": "Chorus generator audio worklet", + "version": "0.1.0", + "description": "Chorus audio effect in as an audio worklet", "keywords": [ "chorus", "modular", diff --git a/packages/synthlet/CHANGELOG.md b/packages/synthlet/CHANGELOG.md index 0e05b59..a7e07c2 100644 --- a/packages/synthlet/CHANGELOG.md +++ b/packages/synthlet/CHANGELOG.md @@ -1,16 +1,22 @@ # synthlet +## 0.5.0 + +- New chorus @synthlet/chorus + +- Updated dependencies + - @synthlet/chorus@0.1.0 + - @synthlet/chorus-t@0.1.1 + ## 0.4.0 -- New @synthlet/arp package +- New arpeggiator @synthlet/arp package ## 0.3.0 +- New reverb @synthlet/dattorro-reverb - Function `registerSynthlet` renamed to `registerAllWorklets` -- Initial release of: - - @synthlet/dattorro-reverb@0.1.0 - ## 0.2.0 - Initial release of the following modules: diff --git a/packages/synthlet/package.json b/packages/synthlet/package.json index e479656..9b23e06 100644 --- a/packages/synthlet/package.json +++ b/packages/synthlet/package.json @@ -1,6 +1,6 @@ { "name": "synthlet", - "version": "0.4.0", + "version": "0.5.0", "description": "Modular synthesis in the browser", "keywords": [ "modular", @@ -23,8 +23,8 @@ "@synthlet/ad": "^0.1.0", "@synthlet/adsr": "^0.1.0", "@synthlet/arp": "^0.1.0", - "@synthlet/chorus": "^0.0.0", - "@synthlet/chorus-t": "^0.1.0", + "@synthlet/chorus": "^0.1.0", + "@synthlet/chorus-t": "^0.1.1", "@synthlet/clip-amp": "^0.1.0", "@synthlet/clock": "^0.1.0", "@synthlet/dattorro-reverb": "^0.1.0", diff --git a/site/content/docs/(effects)/chorus-t.mdx b/site/content/docs/(effects)/chorus-t.mdx index ae90a45..68b06d0 100644 --- a/site/content/docs/(effects)/chorus-t.mdx +++ b/site/content/docs/(effects)/chorus-t.mdx @@ -5,10 +5,6 @@ description: A chorus effect based on the TAL-Noisemaker one A Chorus effect based on the TAL-Noisemaker that in turn models a Roland Juno-60 chorus. - - This module is WIP. - - ```ts import { ChorusT } from "synthlet";