Last active
March 24, 2019 04:30
-
-
Save tlubke/b170fbce7c0a233102a814536e142067 to your computer and use it in GitHub Desktop.
roda (2.0)
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
-- roda | |
-- @tyler | |
-- a microrhythmic sampler | |
-- | |
-- inspired by the microrhythms | |
-- of Samba de Roda. | |
-- | |
-- load samples via parameters | |
-- | |
-- DISPLAY | |
-- ------------------------------------------- | |
-- bpm: | ratio to beat | | |
-- track 1 # divisions | |
-- track 2 # divisions | |
-- track 3 # divisions | |
-- track 4 # divisions | |
-- pause/play | |
-- | |
-- CONTROLS | |
-- -------------------------------------------- | |
-- key1: ALT | |
-- key2: change track | |
-- key3: play/pause | |
-- *ALT: mute track | |
-- | |
-- enc1: select division | |
-- *ALT: change bpm | |
-- enc2: shift selected division | |
-- enc3: change # of divisions | |
-- in selected track [1-9] | |
-- | |
-- PARAMETERS | |
-- -------------------------------------------- | |
-- standard 'ack' parameters | |
-- | |
-- RANDOM MODE | |
-- --------------------------------------------- | |
-- randomly change a handful | |
-- of parameters on each | |
-- sample triggered. | |
-- | |
-- In the parameter menu... | |
-- | |
-- mode 0: none/off | |
-- mode 1: total random | |
-- mode 2: step-based random | |
-- (like Drunk Obj. in Max/MSP) | |
-- | |
-- each track can be muted | |
-- so as not to be altered by | |
-- random mode. | |
-- | |
-- 0 = affectable | |
-- 1 = unaffectable | |
engine.name = 'Ack' | |
local ack = require 'we/lib/ack' | |
------------ | |
-- variables | |
------------ | |
-- clocking variables | |
position = 0 | |
q_position = 0 | |
running = false | |
ppq = 192 -- pulses per quarter | |
track = {{},{},{},{}, divs = {1,2,3,4}} | |
track_select = 0 | |
div_select = 0 | |
alt = 0 | |
-- init tracks | |
for i=1, #track do | |
for j=1, track.divs[i] do | |
table.insert(track[i], j, (ppq//track.divs[i])*j) | |
end | |
end | |
---------------- | |
-- initilization | |
---------------- | |
-- FIX ME! set_action with key press? | |
function init() | |
-- parameters | |
params:add_number("preset", "preset:", 1, 128, 1) | |
params:add_option("file", "save-load", {"save","S<->L","load"}, 2) | |
params:set_action("file", function(x) if x == 1 then save() elseif x == 3 then load() end end) | |
params:add_separator() | |
params:add_number("bpm", "bpm", 15, 400, 60) | |
params:add_option("ppq", "resolution", {96,192,288,384}, 2) | |
params:set_action("ppq", function(x) ppq = params:get("ppq") * 96 end) | |
params:add_number("random_mode", "random mode:", 0, 2, 0) | |
params:add_number("drunk_step", "mode 2 step size:", 1, 10, 1) | |
params:add_number("1_mute", "mute track 1:", 0, 1, 0) | |
params:add_number("2_mute", "mute track 2:", 0, 1, 0) | |
params:add_number("3_mute", "mute track 3:", 0, 1, 0) | |
params:add_number("4_mute", "mute track 4:", 0, 1, 0) | |
params:set_action("1_mute", function(x) mute_groups(1,x) end ) | |
params:set_action("2_mute", function(x) mute_groups(2,x) end ) | |
params:set_action("3_mute", function(x) mute_groups(3,x) end ) | |
params:set_action("4_mute", function(x) mute_groups(4,x) end ) | |
params:add_separator() | |
ack.add_effects_params() | |
params:add_separator() | |
for channel=1, 4 do | |
ack.add_channel_params(channel) | |
params:add_separator() | |
end | |
params:read("roda.pset") | |
-- metronome setup | |
clk = metro.init(count, 60 / (params:get("bpm") * ppq), -1) | |
random_mute_groups("1-4",0) | |
redraw() | |
end | |
mute = {0,0,0,0} | |
random_mute = {} | |
function random_mute_groups(track,x) | |
if x == 1 then | |
print("track "..track.." muted") | |
else | |
print("track "..track.." unmuted") | |
end | |
for i=1, 4 do | |
if params:get(i.."_mute") == 1 then | |
random_mute[i] = 1 | |
else | |
random_mute[i] = 0 | |
end | |
end | |
tab.print(random_mute) | |
end | |
--------------------------- | |
-- norns control functions | |
--------------------------- | |
function enc(n,d) | |
if n == 1 then | |
div_select = (div_select + d) % track.divs[track_select+1] | |
if alt == 1 then | |
params:delta("bpm", d) | |
end | |
end | |
if n == 2 then | |
track[track_select+1][div_select+1] = util.clamp(track[track_select+1][div_select+1] + d, 1, ppq - 1) | |
print(track[track_select+1][div_select+1]) | |
end | |
if n == 3 then | |
refresh_track( | |
track_select+1, | |
util.clamp(track.divs[track_select+1] + d, 1, 9) | |
) | |
end | |
redraw() | |
end | |
function key(n,z) | |
if z == 1 then | |
if n == 1 then | |
alt = 1 | |
end | |
if n == 2 then | |
track_select = (track_select + 1) % #track | |
div_select = 0 | |
end | |
if n == 3 then | |
if alt == 1 then | |
mute[track_select+1] = (mute[track_select+1] + 1) % 2 | |
else | |
if running then | |
clk:stop() | |
running = false | |
else | |
clk:start() | |
running = true | |
end | |
end | |
end | |
else | |
if n == 1 then | |
alt = 0 | |
end | |
end | |
redraw() | |
end | |
------------------ | |
-- active functions | |
------------------- | |
function refresh_track(i, divs) | |
div_select = divs - 1 | |
track[i] = {} | |
track.divs[i] = divs | |
for j = 1, track.divs[i] do | |
table.insert(track[i], j, (ppq//track.divs[i]) * j) | |
end | |
end | |
function simplify(n,d) | |
while n ~= 1 do | |
if n/2 == n//2 and d/2 == d//2 then | |
n = n//2 | |
d = d//2 -- don't need to check d, typically a power of 2. | |
elseif n/3 == 1 then | |
n = n//3 | |
d = d//3 | |
return({n,d}) | |
else | |
return({n,d}) | |
end | |
end | |
return({n,d}) | |
end | |
function count(c) | |
position = (position + 1) % (ppq + 1) | |
if position == 0 then position = 1 end | |
clk.time = 60 / (params:get("bpm") * ppq) | |
for i=1, #track do | |
for j=1, #track[i] do | |
if position / track[i][j] == 1 then | |
if mute[i] == 0 then | |
engine.trig(i-1) | |
end -- random modes affect after trigger | |
if params:get("random_mode") == 1 then -- mode 1:total random | |
if params:get(i.."_mute") == 0 then | |
params:set(i.."_start_pos", math.random()) | |
--params:set(i.."_speed", math.random()) | |
params:set(i.."_pan", math.random(-1,1)*math.random()) -- -1 or 1 * random float, to fit -1 through 1 panning range | |
params:set(i.."_filter_cutoff", math.random(20,20000)) | |
params:set(i.."_filter_res", math.random()) | |
params:set(i.."_filter_env_atk", math.random()) | |
params:set(i.."_filter_env_rel", math.random()) | |
params:set(i.."_filter_env_mod", math.random()) | |
params:set(i.."_dist", math.random()) | |
end | |
elseif params:get("random_mode") == 2 then -- mode 2: step-based (like drunk from Max) random | |
if params:get(i.."_mute") == 0 then | |
size = params:get("drunk_step") | |
params:delta(i.."_start_pos", (math.random(-10,10)/100)*size) | |
--params:delta(i.."_speed", (math.random(-10,10)/100)*size) | |
params:delta(i.."_pan", (math.random(-10,10)/100)*size) -- -1 or 1 * random float, to fit -1 through 1 panning range | |
params:delta(i.."_filter_cutoff", math.random(-100*size,100*size)) | |
params:delta(i.."_filter_res", (math.random(-10,10)/100)*size) | |
params:delta(i.."_filter_env_atk", (math.random(-10,10)/100)*size) | |
params:delta(i.."_filter_env_rel", (math.random(-10,10)/100)*size) | |
params:delta(i.."_filter_env_mod", (math.random(-10,10)/100)*size) | |
params:delta(i.."_dist", (math.random(-10,10)/100)*size) | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------------- | |
-- refresh/redraw functions | |
--------------------------- | |
function redraw() | |
screen.clear() | |
screen.aa(0) | |
screen.level(15) | |
-- track_select cursor | |
screen.rect(0, track_select*10 + 15, 1, 3) | |
screen.fill() | |
-- divisions | |
screen.aa(1) | |
for i=1, #track do | |
for j=1, track.divs[i] do | |
if track[i][j] ~= ppq then | |
screen.move(track[i][j]/(ppq/120), i*10 + 5) | |
else | |
screen.move(3, i*10 + 5) | |
end | |
screen.line_rel(0,3) | |
if j == div_select+1 and i == track_select+1 then | |
screen.line_rel(0,-6) | |
end | |
screen.stroke() | |
-- division length display | |
screen.move(123, i*10 + 9) | |
screen.text(track.divs[i]) | |
end | |
end | |
screen.aa(0) | |
-- param display | |
screen.move(1,5) | |
screen.text("bpm:"..params:get("bpm")) | |
-- pause/play icon | |
if not running then | |
screen.rect(123,57,2,6) | |
screen.rect(126,57,2,6) | |
screen.fill() | |
else | |
screen.move(123,57) | |
screen.line_rel(6,3) | |
screen.line_rel(-6,3) | |
screen.fill() | |
end | |
-- track mute toggle | |
for i=1, #track do | |
screen.move(4 + (i-1)*20, 60) | |
screen.text_right(i) | |
screen.rect(8 + (i-1)*20, 54, 8, 8) | |
screen.move(9 + (i-1)*20, 60) | |
if mute[i] == 0 then screen.level(1) end | |
screen.text("M") | |
screen.level(15) | |
screen.stroke() | |
end | |
screen.level(12) | |
-- tracks | |
for i=1, 4 do | |
screen.rect(2, i*10+5, 118,4) | |
screen.stroke() | |
end | |
screen.level(1) | |
-- ratio display | |
screen.move(128,5) | |
screen.text_right( | |
simplify(track[track_select+1][div_select+1],ppq)[1] | |
.."/".. | |
simplify(track[track_select+1][div_select+1],ppq)[2] | |
) | |
screen.stroke() | |
screen.update() | |
end | |
function save() | |
tab.save(track, '/home/we/dust/we/data/roda-'.. params:get("preset") ..'.data') | |
print("SAVE COMPLETE", params:get("preset")) | |
end | |
function load() | |
track = tab.load('/home/we/dust/we/data/roda-'.. params:get("preset") ..'.data') | |
print("LOAD COMPLETE", params:get("preset")) | |
redraw() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can't get this version playing. Probably my own fault. However I do get an error when turning on mute track: