-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8616580
Showing
3 changed files
with
406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# grendy | ||
|
||
a simple drone synth, grendel drone commander inspired | ||
|
||
## Installation | ||
|
||
[Download latest release](https://github.com/cfdrake/grendy/archive/master.zip) and copy into `~/dust/code`. | ||
|
||
Or use Git: | ||
``` | ||
<ssh into your Norns> | ||
$ cd ~/dust/code | ||
$ git clone git@github.com:cfdrake/grendy.git | ||
``` | ||
|
||
Note that after installing you must `SYSTEM => RESET` your Norns before running this script, as it includes a new SuperCollider engine. | ||
|
||
## Norns Script | ||
|
||
The following controls are available for `grendy`: | ||
|
||
- Key 2 randomizes OSC/MIXER section | ||
- Key 3 randomizes FILTER/LFO section | ||
- Enc 1, Enc 2, and Enc 3 controls filter frequency and oscillator frequencies | ||
- Holding Key 1 + Enc 1, Enc 2, and Enc 3 controls filter resonance and LFO frequency/depth | ||
|
||
Tweak more parameters from the `[PARAMETERS] => [EDIT]` page. | ||
|
||
MIDI note input will round-robin set OSC1 and OSC2 frequency. | ||
|
||
## Synth Architecture | ||
|
||
- OSC: two oscillators, each crossfaded between square and triangle shapes | ||
- MIXER: balance control between OSC1 and OSC2 | ||
- FILTER: Moog-modeled filter with cutoff and resonance control | ||
- LFO: saw and click shapes, crossfaded, affecting filter cutoff with depth control | ||
- SAW: frequency control | ||
- CLICK: rate control (N times SAW frequency), pulse width control | ||
- AMP: final volume control | ||
|
||
## SuperCollider Engine | ||
|
||
This script makes a new SuperCollider engine available, `Grendy`. Please see `lib/engine_grendy.sc` for the latest parameter definitions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
-- ~~ grendy ~~ | ||
-- a simple drone synth | ||
-- grendel drone commander inspired | ||
-- v1.0.0 @cfd90 | ||
-- | ||
-- KEY2: randomize OSC/MIXER | ||
-- KEY3: randomize FILTER/LFO | ||
-- | ||
-- ENC1: FILTER freq | ||
-- ENC2: OSC1 freq | ||
-- ENC3: OSC2 freq | ||
-- | ||
-- KEY1+ENC1: FILTER res | ||
-- KEY1+ENC2: LFO freq | ||
-- KEY1+ENC3: LFO depth | ||
-- | ||
-- Further tweak parameters via [EDIT] page | ||
-- | ||
-- MIDI input round-robin sets OSC1 and OSC2 freq | ||
|
||
music = require 'musicutil' | ||
|
||
engine.name = "Grendy" | ||
|
||
------ | ||
------ STATE | ||
------ | ||
|
||
local shift = false | ||
local midiFirstOsc = true | ||
|
||
------ | ||
------ INITIALIZATION | ||
------ | ||
|
||
function init() | ||
print("unleashing grendy...") | ||
setup_params() | ||
setup_midi() | ||
end | ||
|
||
function setup_params() | ||
-- OSC1 | ||
params:add_separator() | ||
params:add_control("freq1", "osc1 freq", controlspec.new(20, 360, 'exp', 0.5, 220, 'hz')) | ||
params:set_action("freq1", function(x) engine.freq1(x) end) | ||
|
||
params:add_control("shape1", "osc1 shape [tri=>sqr]", controlspec.new(-1, 1, 'lin', 0.01, 1, '')) | ||
params:set_action("shape1", function(x) engine.shape1(x) end) | ||
|
||
-- OSC2 | ||
params:add_separator() | ||
params:add_control("freq2", "osc2 freq", controlspec.new(20, 360, 'exp', 0.5, 220, 'hz')) | ||
params:set_action("freq2", function(x) engine.freq2(x) end) | ||
|
||
params:add_control("shape2", "osc2 shape [tri=>sqr]", controlspec.new(-1, 1, 'lin', 0.01, 1, '')) | ||
params:set_action("shape2", function(x) engine.shape2(x) end) | ||
|
||
-- MIXER | ||
params:add_separator() | ||
params:add_control("mix", "osc mix [osc1=>osc2]", controlspec.new(-1, 1, 'lin', 0.1, 0, '')) | ||
params:set_action("mix", function(x) engine.mix(x) end) | ||
|
||
-- FILTER | ||
params:add_separator() | ||
params:add_control("ffreq", "filter frequency", controlspec.new(20, 2000, 'exp', 0.5, 440, 'hz')) | ||
params:set_action("ffreq", function(x) engine.ffreq(x) end) | ||
|
||
params:add_control("fres", "filter res", controlspec.new(0, 4, 'lin', 0.1, 1, '')) | ||
params:set_action("fres", function(x) engine.fres(x) end) | ||
|
||
-- LFO | ||
params:add_separator() | ||
params:add_control("lfreq", "lfo frequency", controlspec.new(0.1, 20, 'lin', 0.1, 0.1, 'hz')) | ||
params:set_action("lfreq", function(x) engine.lfreq(x) end) | ||
|
||
params:add_control("cspeed", "lfo click rate", controlspec.new(1, 16, 'lin', 1, 1, 'x')) | ||
params:set_action("cspeed", function(x) engine.cspeed(x) end) | ||
|
||
params:add_control("cwidth", "lfo click width", controlspec.new(0.1, 0.9, 'lin', 0.1, 0.2, '')) | ||
params:set_action("cwidth", function(x) engine.cwidth(x) end) | ||
|
||
params:add_control("lshape", "lfo shape [ramp=>click]", controlspec.new(-1, 1, 'lin', 0.1, -1, '')) | ||
params:set_action("lshape", function(x) engine.lshape(x) end) | ||
|
||
params:add_control("ldepth", "lfo depth", controlspec.new(0, 1000, 'lin', 0.1, 0, '')) | ||
params:set_action("ldepth", function(x) engine.ldepth(x) end) | ||
|
||
-- AMP | ||
params:add_separator() | ||
params:add_control("amp", "amp", controlspec.new(0, 1, 'lin', 0.01, 1, '')) | ||
params:set_action("amp", function(x) engine.amp(x) end) | ||
end | ||
|
||
function setup_midi() | ||
m = midi.connect() | ||
|
||
m.event = function(data) | ||
local d = midi.to_msg(data) | ||
|
||
if d.type == "note_on" then | ||
-- Round-robin set OSC1 and OSC2 | ||
hz = music.note_num_to_freq(d.note) | ||
|
||
if midiFirstOsc then | ||
params:set("freq1", hz) | ||
else | ||
params:set("freq2", hz) | ||
end | ||
|
||
midiFirstOsc = not midiFirstOsc | ||
end | ||
end | ||
end | ||
|
||
------ | ||
------ INPUT | ||
------ | ||
|
||
function key(n, z) | ||
-- Shift key | ||
if n == 1 then | ||
if z > 0 then | ||
shift = true | ||
else | ||
shift = false | ||
end | ||
|
||
redraw() | ||
end | ||
|
||
-- Parameter randomization | ||
if n == 2 and z == 1 then | ||
randomize_osc() | ||
elseif n == 3 and z == 1 then | ||
randomize_filter() | ||
end | ||
end | ||
|
||
function enc(n, d) | ||
if shift then | ||
-- SHIFT mode | ||
-- Control filter res, LFO freq, LFO depth | ||
if n == 1 then | ||
params:delta("fres", d) | ||
elseif n == 2 then | ||
params:delta("lfreq", d) | ||
elseif n == 3 then | ||
params:delta("ldepth", d) | ||
end | ||
else | ||
-- REGULAR mode | ||
-- Control filter freq, OSC1 freq, OSC2 freq | ||
if n == 1 then | ||
params:delta("ffreq", d) | ||
elseif n == 2 then | ||
params:delta("freq1", d) | ||
elseif n == 3 then | ||
params:delta("freq2", d) | ||
end | ||
end | ||
end | ||
|
||
------ | ||
------ DRAWING | ||
------ | ||
|
||
function redraw() | ||
screen.clear() | ||
|
||
-- Encoder documentation | ||
if shift then | ||
screen.move(0, 10) | ||
screen.level(1) | ||
screen.text("[ENC1] ") | ||
screen.level(5) | ||
screen.text("filter res") | ||
screen.move(0, 20) | ||
screen.level(1) | ||
screen.text("[ENC2] ") | ||
screen.level(5) | ||
screen.text("lfo freq") | ||
screen.move(0, 30) | ||
screen.level(1) | ||
screen.text("[ENC3] ") | ||
screen.level(5) | ||
screen.text("lfo depth") | ||
else | ||
screen.move(0, 10) | ||
screen.level(1) | ||
screen.text("[ENC1] ") | ||
screen.level(5) | ||
screen.text("filter freq") | ||
screen.move(0, 20) | ||
screen.level(1) | ||
screen.text("[ENC2] ") | ||
screen.level(5) | ||
screen.text("osc1 freq") | ||
screen.move(0, 30) | ||
screen.level(1) | ||
screen.text("[ENC3] ") | ||
screen.level(5) | ||
screen.text("osc2 freq") | ||
end | ||
|
||
-- Key documentation | ||
if not shift then | ||
screen.move(0, 40) | ||
screen.level(1) | ||
screen.text("[KEY1] ") | ||
screen.level(5) | ||
screen.text("held, toggle page 2") | ||
else | ||
screen.move(0, 40) | ||
screen.level(1) | ||
screen.text("see params page for more") | ||
end | ||
|
||
screen.move(0, 50) | ||
screen.level(1) | ||
screen.text("[KEY2] ") | ||
screen.level(5) | ||
screen.text("randomize OSC/MIXER") | ||
screen.move(0, 60) | ||
screen.level(1) | ||
screen.text("[KEY3] ") | ||
screen.level(5) | ||
screen.text("randomize FILTER/LFO") | ||
|
||
screen.update() | ||
end | ||
|
||
------ | ||
------ HELPERS | ||
------ | ||
|
||
function randomize_osc() | ||
params:set("freq1", 20 + (math.random() * (360 - 20))) | ||
params:set("shape1", (math.random() * 2) - 1) | ||
params:set("freq2", 20 + (math.random() * (360 - 20))) | ||
params:set("shape2", (math.random() * 2) - 1) | ||
params:set("mix", (math.random() * 2) - 1) | ||
end | ||
|
||
function randomize_filter() | ||
params:set("ffreq", 20 + (math.random() * (2000 - 20))) | ||
params:set("fres", math.random() * 3.5) -- Don't go to 4, don't want to blast the user's ear with self-oscillation... | ||
params:set("cspeed", 1 + (math.random() * (16 - 1))) | ||
params:set("cwidth", 0.1 + (math.random() * (1 - 0.1))) | ||
params:set("lshape", (math.random() * 2) - 1) | ||
params:set("ldepth", math.random() * 1000) | ||
end |
Oops, something went wrong.