From adb657e192e3396cd06de9da50e08d29d60b26d2 Mon Sep 17 00:00:00 2001
From: gretchen <gretchen@gnar.cool>
Date: Mon, 18 Nov 2019 22:47:46 -0800
Subject: fm rhodes for norns

---
 Makefile             |  9 +++++++
 README.org           |  7 +++++
 lib/Engine_Rhodes.sc | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 rhodes.lua           | 28 ++++++++++++++++++++
 4 files changed, 119 insertions(+)
 create mode 100644 Makefile
 create mode 100644 README.org
 create mode 100644 lib/Engine_Rhodes.sc
 create mode 100644 rhodes.lua

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:
-- 
cgit v1.2.1