diff options
-rw-r--r-- | README.org | 2 | ||||
-rw-r--r-- | lib/Engine_Rhodes.sc | 61 | ||||
-rw-r--r-- | rhodes.lua | 99 |
3 files changed, 155 insertions, 7 deletions
@@ -4,4 +4,4 @@ midi 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, some parameters (tremolo and vibrato? chorus? brightness?), maybe mod wheel and pitch bend support. Email patches to [[mailto:gretchen@gnar.cool][<gretchen@gnar.cool>]]. +Needs some visualization and more parameters (chorus? brightness? tube amp?). Email patches to [[mailto:gretchen@gnar.cool][<gretchen@gnar.cool>]]. diff --git a/lib/Engine_Rhodes.sc b/lib/Engine_Rhodes.sc index ad55cd3..a644c96 100644 --- a/lib/Engine_Rhodes.sc +++ b/lib/Engine_Rhodes.sc @@ -4,6 +4,10 @@ Engine_Rhodes : CroneEngine { var <notes; + var <lfoSpeed; + var <lfoDepth; + var <modIndex; + var <mix; *new { arg context, doneCallback; ^super.new(context, doneCallback); @@ -22,7 +26,6 @@ Engine_Rhodes : CroneEngine { | 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)); @@ -37,7 +40,7 @@ Engine_Rhodes : CroneEngine { 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 = snd * EnvGen.ar(Env.asr(0, 1, 0.25), gate); snd = Pan2.ar(snd, pan, amp); DetectSilence.ar(snd, doneAction: Done.freeSelf); Out.ar(out, snd); @@ -45,6 +48,10 @@ Engine_Rhodes : CroneEngine { }); } alloc { + lfoSpeed = 2.0; + lfoDepth = 0.1; + modIndex = 0.1; + mix = 0.2; notes = nil!128; this.addCommand("note_on", "ii", { arg msg; @@ -55,7 +62,11 @@ Engine_Rhodes : CroneEngine { [ \out, context.out_b, \freq, note.midicps, - \vel, vel/127.0 + \vel, vel/127.0, + \lfoSpeed, lfoSpeed, + \modIndex, modIndex, + \lfoDepth, lfoDepth, + \mix, mix ], target: context.xg); } @@ -69,6 +80,50 @@ Engine_Rhodes : CroneEngine { notes[note] = nil; } }); + this.addCommand("set_lfo_speed", "f", { + arg msg; + lfoSpeed = msg[1]; + // there has to be a better way to do this + for (0, 128, { + arg i; + if (notes[i].notNil) { + notes[i].set(\lfoSpeed, msg[1]); + } + }) + }); + this.addCommand("set_lfo_depth", "f", { + arg msg; + lfoDepth = msg[1]; + // there has to be a better way to do this + for (0, 128, { + arg i; + if (notes[i].notNil) { + notes[i].set(\lfoDepth, msg[1]); + } + }) + }); + this.addCommand("set_mod_index", "f", { + arg msg; + modIndex = msg[1]; + // there has to be a better way to do this + for (0, 128, { + arg i; + if (notes[i].notNil) { + notes[i].set(\modIndex, msg[1]); + } + }) + }); + this.addCommand("set_mix", "f", { + arg msg; + mix = msg[1]; + // there has to be a better way to do this + for (0, 128, { + arg i; + if (notes[i].notNil) { + notes[i].set(\mix, msg[1]); + } + }) + }); } free { } @@ -5,6 +5,7 @@ engine.name = "Rhodes" local MusicUtil = require "musicutil" +local ControlSpec = require "controlspec" function redraw() screen.clear() @@ -14,16 +15,108 @@ function redraw() end function init () - redraw() - m = midi.connect(1) - m.event = function (a) + local mod_wheel_controls = 2 + local base_mod_index = 0.1 + local base_trem_speed = 2.0 + local base_trem_depth = 0.1 + local operator_mix = 0.2 + + local m = nil + local midi_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 + -- mod wheel + if b.type == "cc" and b.ch == 1 then + if mod_wheel_controls == 1 then + engine.set_mod_index(base_mod_index + (b.val / 127) * (1.0 - base_mod_index)) + elseif mod_wheel_controls == 2 then + engine.set_lfo_speed(base_trem_speed + (b.val / 127) * (60.0 - base_trem_speed)) + elseif mod_wheel_controls == 3 then + engine.set_lfo_depth(base_trem_depth + (b.val / 127) * (1.0 - base_trem_depth)) + end + end end + + -- midi settings + params:add({ + type = "option", + id = "mod_controls", + name = "Mod Wheel", + default = mod_wheel_controls, + options={"Mod Index", "Tremolo Speed", "Tremolo Depth"}, + action = function(value) do + mod_wheel_controls = value; + engine.set_mod_index(base_mod_index); + engine.set_lfo_speed(base_trem_speed); + engine.set_lfo_depth(base_trem_depth); + end + end}) + params:add({ + type = "control", + id = "midi_in_vport", + name = "Midi In", + default=60, + controlspec = ControlSpec.new(1, 4, 'lin', 1, 1), + action = function(value) + -- clear the old connection + if m then + m.event = nil + end + m = midi.connect(value) + m.event = midi_event + end}) + params:add_separator() + + -- modulation settings + params:add({ + type = "control", + id = "base_mod_index", + name = "Mod Index", + controlspec = ControlSpec.new(0.0, 100.0, 'lin', 0.5, base_mod_index * 100.0, "%"), + action = function(value) do + base_mod_index = value/100.0; + engine.set_mod_index(value/100.0); + end + end}) + params:add({ + type = "control", + id = "operator_mix", + name = "Operator Mix", + controlspec = ControlSpec.new(0.0, 100.0, 'lin', 0.1, operator_mix * 100.0, "%"), + action = function(value) do + operator_mix = value/100.0; + engine.set_mix(value/100.0); + end + end}) + params:add_separator() + + -- tremolo + params:add({ + type = "control", + id = "base_trem_speed", + name = "Tremolo Speed", + controlspec = ControlSpec.new(0.001, 60.0, 'lin', 0.1, base_trem_speed, "hz"), + action = function(value) do + base_trem_speed = value; + engine.set_lfo_speed(value); + end + end}) + params:add({ + type = "control", + id = "base_trem_depth", + name = "Tremolo Depth", + controlspec = ControlSpec.new(0.00, 100.0, 'lin', 0.1, base_trem_depth * 100.0, "%"), + action = function(value) do + base_trem_depth = value/100.0; + engine.set_lfo_depth(value/100.00); + end + end}) + params:default() + redraw() end function key (n, x) |