diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | README.org | 7 | ||||
-rw-r--r-- | lib/Engine_Rhodes.sc | 75 | ||||
-rw-r--r-- | rhodes.lua | 28 |
4 files changed, 119 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..874f48e --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ + +all: + +upload: rhodes.lua lib/Engine_Rhodes.sc + find . -not -path '*/\.*' -type f | xargs -P 5 -n 1 norns_upload gretchen/rhodes + norns_load gretchen/rhodes rhodes.lua + +.PHONY: all upload + diff --git a/README.org b/README.org new file mode 100644 index 0000000..d385e1b --- /dev/null +++ b/README.org @@ -0,0 +1,7 @@ +* rhodes + +FM rhodes simulation for norns. + +Adapted for norns from [[https://sccode.org/1-522][this supercollider snippet]] by user 'snapizz', which is adapted from [[https://github.com/thestk/stk/blob/master/include/Rhodey.h][STK's rhodey]], which Perry R. Cook and Gary P. Scavone adapted from a Yamaha TX81z algorithm. + +Needs some visualization, and some parameters (tremolo and vibrato? chorus? brightness?). Email patches to [[mailto:gretchen@gnar.cool][<gretchen@gnar.cool>]]. diff --git a/lib/Engine_Rhodes.sc b/lib/Engine_Rhodes.sc new file mode 100644 index 0000000..02da1af --- /dev/null +++ b/lib/Engine_Rhodes.sc @@ -0,0 +1,75 @@ +// Adapted from https://sccode.org/1-522, which is adapted in +// turn from STK's 'rhodey', which is adapted from a TX81z +// algorithm. + +Engine_Rhodes : CroneEngine { + var <notes; + + *new { arg context, doneCallback; + ^super.new(context, doneCallback); + } + + *initClass { + // TODO store it as a classvar? + StartUp.add({ + SynthDef("Rhodes", + { + | // standard meanings + out = 0, freq = 440, gate = 1, pan = 0, amp = 0.1, + // all of these range from 0 to 1 + vel = 0.8, modIndex = 0.2, mix = 0.2, + lfoSpeed = 0.4, lfoDepth = 0.1 + | + var env1, env2, env3, env4; + var osc1, osc2, osc3, osc4, snd; + lfoSpeed = lfoSpeed * 12; + freq = freq * 2; + + env1 = EnvGen.ar(Env.adsr(0.001, 1.25, 0.0, 0.04, curve: \lin)); + env2 = EnvGen.ar(Env.adsr(0.001, 1.00, 0.0, 0.04, curve: \lin)); + env3 = EnvGen.ar(Env.adsr(0.001, 1.50, 0.0, 0.04, curve: \lin)); + env4 = EnvGen.ar(Env.adsr(0.001, 1.50, 0.0, 0.04, curve: \lin)); + + osc4 = SinOsc.ar(freq * 0.5) * 2pi * 2 * 0.535887 * modIndex * env4 * vel; + osc3 = SinOsc.ar(freq, osc4) * env3 * vel; + osc2 = SinOsc.ar(freq * 15) * 2pi * 0.108819 * env2 * vel; + osc1 = SinOsc.ar(freq, osc2) * env1 * vel; + snd = Mix((osc3 * (1 - mix)) + (osc1 * mix)); + snd = snd * (SinOsc.ar(lfoSpeed) * lfoDepth + 1); + + snd = snd * EnvGen.ar(Env.asr(0, 1, 0.2)); + snd = Pan2.ar(snd, pan, amp); + DetectSilence.ar(snd, doneAction: Done.freeSelf); + Out.ar(out, snd); + }).add; + }); + } + alloc { + notes = nil!128; + this.addCommand("note_on", "ii", { + arg msg; + var note = msg[1]; + var vel = msg[2]; + if(notes[note].isNil) { + notes[note] = Synth("Rhodes", + [ + \out, context.out_b, + \freq, note.midicps, + \vel, vel/127.0 + ], + target: context.xg); + } + }); + + this.addCommand("note_off", "f", { + arg msg; + var note = msg[1]; + if(notes[note].notNil) { + notes[note].release; + notes[note] = nil; + } + }); + } + free { + } +}
\ No newline at end of file diff --git a/rhodes.lua b/rhodes.lua new file mode 100644 index 0000000..d9a1ee1 --- /dev/null +++ b/rhodes.lua @@ -0,0 +1,28 @@ +engine.name = "Rhodes" + +local MusicUtil = require "musicutil" + +function redraw() + screen.clear() + screen.move(64, 32) + screen.text_center("rhodes.") + screen.update() +end + +function init () + redraw() + m = midi.connect(1) + m.event = function (a) + b = midi.to_msg(a) + if b.type == "note_on" then + engine.note_on(b.note, b.vel or 60) + elseif b.type == "note_off" then + engine.note_off(b.note) + end + end +end + + +-- Local Variables: +-- compile-command: "make upload" +-- End: |