From dc766fe8968fdb603d9ea42875abceb44887a50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Mon, 29 Jun 2026 00:20:34 +0200 Subject: [PATCH] feat: more classic machine emulations Web-researched the iconic machines per style and added 7 more: Minimoog (ladder bass/lead), Roland JP-8000 supersaw (trance/ progressive), Juno-106 (chorus pad), Yamaha DX7 (FM), E-mu SP-1200 (12-bit drum crunch), Akai sampler (lo-fi grit) and Roland System-100. Standard UGens only (MoogFF/Latch/DelayC). Wired per voice into the choices + web mirror; all kit/test instrument targets kept in the choice lists. --- sound_algo/data_only/launchpad.scd | 73 +++++++++++++++++++++++++++ sound_algo/data_only/matrix.scd | 30 +++++------ web_realart/public/control/control.js | 18 +++---- 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/sound_algo/data_only/launchpad.scd b/sound_algo/data_only/launchpad.scd index 442bb73..4053937 100644 --- a/sound_algo/data_only/launchpad.scd +++ b/sound_algo/data_only/launchpad.scd @@ -242,6 +242,79 @@ SynthDef(\sh101, { |out=0, freq=55, amp=0.3, cutoff=1500, res=0.3, gate=1, pan=0 Out.ar(out, sig); ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); }).add; +// --- Minimoog : 3-osc fat bass/lead, Moog ladder LPF (gated) --- +SynthDef(\minimoog, { |out=0, freq=55, amp=0.3, cutoff=1200, res=0.5, gate=1, pan=0, rev=0| + var env = EnvGen.kr(Env.asr(0.005, 1, 0.22), gate, doneAction: 2); + var osc = Mix([Saw.ar(freq.clip(20,3000)), Saw.ar(freq.clip(20,3000) * 1.005), + Pulse.ar((freq * 0.5).clip(10, 1500), 0.5)]) * 0.4; + var sig = MoogFF.ar(osc, cutoff.clip(60, 8000), res.clip(0, 1) * 3.8); + sig = Pan2.ar(sig * env, pan.clip(-1, 1)) * amp * 0.5; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- Roland JP-8000 supersaw : 7 detuned saws (trance/progressive lead) --- +SynthDef(\supersaw, { |out=0, freq=220, amp=0.3, cutoff=6000, res=0.2, detune=0.05, gate=1, pan=0, rev=0| + var env = EnvGen.kr(Env.asr(0.01, 1, 0.3), gate, doneAction: 2); + var dets = [-3, -2, -1, 0, 1, 2, 3] * (detune / 3); + var osc = Mix(Saw.ar((freq.clip(20, 4000)) * (1 + dets))) * (1/7) * 2; + var sig = RLPF.ar(osc, cutoff.clip(200, 12000), (1 - res).clip(0.2, 1)); + sig = Pan2.ar(sig * env, pan.clip(-1, 1)) * amp * 0.7; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- Roland Juno-106 : saw+pulse+sub -> LPF + signature chorus (gated pad) --- +SynthDef(\juno, { |out=0, freq=110, amp=0.3, cutoff=2500, res=0.2, gate=1, pan=0, rev=0| + var env = EnvGen.kr(Env.asr(0.02, 1, 0.4), gate, doneAction: 2); + var osc = Saw.ar(freq.clip(20, 3000)) + + (Pulse.ar(freq.clip(20, 3000), SinOsc.kr(0.3).range(0.3, 0.7)) * 0.6) + + (Pulse.ar((freq * 0.5).clip(10, 1500), 0.5) * 0.4); + var flt = RLPF.ar(osc * 0.3, cutoff.clip(200, 9000), (1 - res).clip(0.2, 1)); + var ch = flt + + DelayC.ar(flt, 0.03, SinOsc.kr(0.6, 0, 0.003, 0.008)) + + DelayC.ar(flt, 0.03, SinOsc.kr(0.5, pi, 0.003, 0.008)); + var sig = Pan2.ar(ch * 0.5 * env, pan.clip(-1, 1)) * amp * 0.5; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- Yamaha DX7 : 2-operator FM (bass / bell / e-piano) --- +SynthDef(\dx7, { |out=0, freq=110, amp=0.3, ratio=2.0, index=3, pan=0, rev=0| + var env = EnvGen.kr(Env.perc(0.004, 0.6, 1, -4), doneAction: 2); + var menv = EnvGen.kr(Env.perc(0.001, 0.3, 1, -3)); + var mod = SinOsc.ar(freq.clip(20, 4000) * ratio) * freq.clip(20, 4000) * ratio * index * menv; + var sig = SinOsc.ar(freq.clip(20, 4000) + mod) * env; + sig = Pan2.ar(sig, pan.clip(-1, 1)) * amp * 0.5; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- E-mu SP-1200 : 12-bit / ~26 kHz crunchy drum character --- +SynthDef(\sp1200, { |out=0, freq=120, amp=0.5, pan=0, rev=0| + var env = EnvGen.kr(Env.perc(0.001, 0.3, 1, -4), doneAction: 2); + var src = (SinOsc.ar(freq.clip(30, 400)) + (WhiteNoise.ar * 0.5)) * env; + var crushed = (Latch.ar(src, Impulse.ar(26040)) * 2048).round / 2048; + var sig = Pan2.ar(crushed.tanh, pan.clip(-1, 1)) * amp; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- Akai sampler (S950/S1100-ish) : 12-bit / 22 kHz lo-fi tonal grit --- +SynthDef(\akai_s, { |out=0, freq=220, amp=0.3, pan=0, rev=0| + var env = EnvGen.kr(Env.perc(0.002, 0.5, 1, -3), doneAction: 2); + var src = (Saw.ar(freq.clip(20, 3000)) + (Pulse.ar(freq.clip(20, 3000) * 1.5, 0.4) * 0.5)) * 0.4; + var crushed = (Latch.ar(src, Impulse.ar(22050)) * 4096).round / 4096; + var sig = RLPF.ar(crushed, 6000, 0.6) * env; + sig = Pan2.ar(sig, pan.clip(-1, 1)) * amp * 0.6; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; +// --- Roland System-100 : semi-modular saw/pulse + Moog-ish LPF (bass/lead) --- +SynthDef(\system100, { |out=0, freq=110, amp=0.3, cutoff=1500, res=0.4, gate=1, pan=0, rev=0| + var env = EnvGen.kr(Env.asr(0.006, 1, 0.2), gate, doneAction: 2); + var osc = (Saw.ar(freq.clip(20, 3000)) + Pulse.ar(freq.clip(20, 3000) * 0.999, 0.45) + + (Pulse.ar((freq * 0.5).clip(10, 1500), 0.5) * 0.4)) * 0.3; + var sig = MoogFF.ar(osc, cutoff.clip(60, 8000), res.clip(0, 1) * 3.6); + sig = Pan2.ar(sig * env, pan.clip(-1, 1)) * amp * 0.5; + Out.ar(out, sig); + ~doReverbBus.notNil.if({ Out.ar(~doReverbBus, sig * rev) }); +}).add; s.sync; diff --git a/sound_algo/data_only/matrix.scd b/sound_algo/data_only/matrix.scd index 7c12c2d..ac899bc 100644 --- a/sound_algo/data_only/matrix.scd +++ b/sound_algo/data_only/matrix.scd @@ -81,22 +81,22 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); // they never free and pile up forever, so they are NOT offered. The gated \lp_ // sustained voices (lp_sub/lp_reese/lp_pad/lp_sweep) cover the drone textures. ~matInstChoices = ~matInstChoices ? IdentityDictionary[ - \kick -> [\lp_kick, \tr909_kick, \tr808_kick, \do_kick, \do_quake_sub], - \hats -> [\lp_hat, \tr909_hat, \tr808_hat, \do_hat], - \clap -> [\lp_clap, \tr909_clap, \tr808_clap, \lp_rim, \do_hat], - \perc -> [\lp_perc, \tr808_cowbell, \do_strike, \lp_tom], - \sub -> [\lp_sub, \sh101, \tr808_kick, \lp_reese, \do_quake_sub], - \acid -> [\lp_acid, \tb303, \ms20, \lp_pluck], - \arp -> [\lp_pluck, \tb303, \do_strike, \lp_bells], - \lead -> [\lp_pluck, \ms20, \sh101, \do_plane, \do_strike], - \stab -> [\lp_pluck, \ms20, \tb303, \lp_acid], - \pad -> [\lp_pad, \ms20, \lp_reese, \lp_sub], + \kick -> [\lp_kick, \tr909_kick, \tr808_kick, \sp1200, \do_kick, \do_quake_sub], + \hats -> [\lp_hat, \tr909_hat, \tr808_hat, \sp1200, \do_hat], + \clap -> [\lp_clap, \tr909_clap, \tr808_clap, \sp1200, \lp_rim], + \perc -> [\lp_perc, \tr808_cowbell, \sp1200, \akai_s, \do_strike], + \sub -> [\lp_sub, \sh101, \minimoog, \system100, \lp_reese], + \acid -> [\lp_acid, \tb303, \ms20, \system100, \lp_pluck], + \arp -> [\lp_pluck, \tb303, \supersaw, \juno, \do_strike], + \lead -> [\lp_pluck, \ms20, \supersaw, \do_plane, \sh101], + \stab -> [\lp_pluck, \ms20, \supersaw, \tb303, \lp_acid], + \pad -> [\lp_pad, \juno, \supersaw, \ms20, \lp_reese], \ride -> [\lp_ride, \tr909_ride, \lp_hat, \do_hat], - \rim -> [\lp_rim, \tr808_cowbell, \tr909_clap, \do_hat], - \tom -> [\lp_tom, \do_strike, \do_quake_sub], - \reese -> [\lp_reese, \ms20, \sh101, \lp_sub], - \bells -> [\lp_bells, \do_strike, \do_plane], - \sweep -> [\lp_sweep, \lp_pad, \lp_reese] + \rim -> [\lp_rim, \tr808_cowbell, \tr909_clap, \sp1200], + \tom -> [\lp_tom, \sp1200, \do_strike, \do_quake_sub], + \reese -> [\lp_reese, \ms20, \minimoog, \system100, \supersaw], + \bells -> [\lp_bells, \dx7, \akai_s, \do_plane], + \sweep -> [\lp_sweep, \akai_s, \lp_pad, \lp_reese] ]; // -- fixed kits: kitName -> (voiceName -> instrument). Unlisted voices = default -- diff --git a/web_realart/public/control/control.js b/web_realart/public/control/control.js index c312c47..101b259 100644 --- a/web_realart/public/control/control.js +++ b/web_realart/public/control/control.js @@ -312,15 +312,15 @@ const MATRIX_VOICES = [ // per-voice curated instrument options (must mirror SC ~matInstChoices) // note-terminating synths only — the continuous do_ drones never free per-note. const MATRIX_INST_CHOICES = { - kick:["lp_kick","tr909_kick","tr808_kick","do_kick","do_quake_sub"], hats:["lp_hat","tr909_hat","tr808_hat","do_hat"], - clap:["lp_clap","tr909_clap","tr808_clap","lp_rim","do_hat"], perc:["lp_perc","tr808_cowbell","do_strike","lp_tom"], - sub:["lp_sub","sh101","tr808_kick","lp_reese","do_quake_sub"], - acid:["lp_acid","tb303","ms20","lp_pluck"], arp:["lp_pluck","tb303","do_strike","lp_bells"], - lead:["lp_pluck","ms20","sh101","do_plane","do_strike"], stab:["lp_pluck","ms20","tb303","lp_acid"], - pad:["lp_pad","ms20","lp_reese","lp_sub"], ride:["lp_ride","tr909_ride","lp_hat","do_hat"], - rim:["lp_rim","tr808_cowbell","tr909_clap","do_hat"], tom:["lp_tom","do_strike","do_quake_sub"], - reese:["lp_reese","ms20","sh101","lp_sub"], - bells:["lp_bells","do_strike","do_plane"], sweep:["lp_sweep","lp_pad","lp_reese"] + kick:["lp_kick","tr909_kick","tr808_kick","sp1200","do_kick","do_quake_sub"], hats:["lp_hat","tr909_hat","tr808_hat","sp1200","do_hat"], + clap:["lp_clap","tr909_clap","tr808_clap","sp1200","lp_rim"], perc:["lp_perc","tr808_cowbell","sp1200","akai_s","do_strike"], + sub:["lp_sub","sh101","minimoog","system100","lp_reese"], + acid:["lp_acid","tb303","ms20","system100","lp_pluck"], arp:["lp_pluck","tb303","supersaw","juno","do_strike"], + lead:["lp_pluck","ms20","supersaw","do_plane","sh101"], stab:["lp_pluck","ms20","supersaw","tb303","lp_acid"], + pad:["lp_pad","juno","supersaw","ms20","lp_reese"], ride:["lp_ride","tr909_ride","lp_hat","do_hat"], + rim:["lp_rim","tr808_cowbell","tr909_clap","sp1200"], tom:["lp_tom","sp1200","do_strike","do_quake_sub"], + reese:["lp_reese","ms20","minimoog","system100","supersaw"], + bells:["lp_bells","dx7","akai_s","do_plane"], sweep:["lp_sweep","akai_s","lp_pad","lp_reese"] }; let matInst = new Array(16).fill("default"); // per-voice selection (persisted) const MATRIX_MOD_SOURCES = ["none","lHandY","rHandY","lHandX","rHandX","lOpen","rOpen","handSpeed","handDist","bodyX","bodyY","depth"];