Created
December 2, 2018 14:47
-
-
Save millxing/7803e3dec9dd28cab74f2ac8c06d0734 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
music = require 'mark_eats/musicutil' -- super useful. Thanks Mark! | |
beatclock = require 'beatclock' | |
-- midi code | |
m1 = midi.connect(1) | |
m2 = midi.connect(2) | |
--[[ | |
m1.event = function(data) | |
local d = midi.to_msg(data) | |
if d.type == "note_on" and d.ch == 16 then | |
knote = d.note; | |
mbcount = mbcount + 1; if mbcount>32 then mbcount = 1; end | |
mbuffer[mbcount] = knote | |
end | |
end | |
]]-- | |
m2.event = function(data) | |
local d = midi.to_msg(data) | |
channel = 99; cc = 99; value = 99 | |
if d.type=="cc" then | |
--print(" type = " .. d.type .. " ch = " .. d.ch .. " cc = " .. d.cc .. " val = " .. d.val) | |
channel = d.ch; cc = d.cc; value = d.val | |
cc = cc % 16 | |
if channel==1 and cc==0 then twist_tempo() end | |
if channel==1 and cc==1 then twist_tonic() end | |
if channel==1 and cc==2 then twist_octave(bank) end | |
if channel==1 and cc==4 then twist_seqlen(bank) end | |
if channel==1 and cc==5 then twist_random(bank) end | |
if channel==1 and cc==6 then twist_restfreq(bank) end | |
if channel==1 and cc==8 then twist_tempomod(bank) end | |
if channel==1 and cc==9 then twist_dispersion(bank) end | |
if channel==1 and cc==10 then twist_persist(bank) end | |
if channel==1 and cc==12 then twist_shift(bank) end | |
if channel==1 and cc==13 then twist_tpose(bank) end | |
if channel==1 and cc==14 then twist_velocity(bank) end | |
if channel==1 and cc==3 then twist_mix(1) end | |
if channel==1 and cc==7 then twist_mix(2) end | |
if channel==1 and cc==11 then twist_mix(3) end | |
if channel==1 and cc==15 then twist_mix(4) end | |
if channel==2 and cc==0 then twist_pause() end | |
if channel==2 and cc==3 then twist_mute(1) end | |
if channel==2 and cc==7 then twist_mute(2) end | |
if channel==2 and cc==11 then twist_mute(3) end | |
if channel==2 and cc==15 then twist_mute(4) end | |
if channel==2 and cc==5 then twist_lock(bank) end | |
if channel==4 and cc==0 then bank = 1; end | |
if channel==4 and cc==1 then bank = 2; end | |
if channel==4 and cc==2 then bank = 3; end | |
if channel==4 and cc==3 then bank = 4; end | |
if channel==5 and cc==1 then twist_scale() end | |
end | |
redraw_grid() | |
redraw_twister() | |
redraw() | |
end | |
-- connect grid | |
g = grid.connect() | |
-- redraw grid | |
function redraw_grid() | |
g.all(0) | |
g.led(1,1,bank==1 and 15 or 5) | |
g.led(1,3,bank==2 and 15 or 5) | |
g.led(1,5,bank==3 and 15 or 5) | |
g.led(1,7,bank==4 and 15 or 5) | |
for r = 1,8 do | |
for c = 0,2 do | |
g.led(9+c,r,((pressX==(9+c) or pressX==(13+c)) and pressY==r) and 15 or sBank[r][c+1]) | |
g.led(13+c,r,((pressX==(9+c) or pressX==(13+c)) and pressY==r) and 15 or sBank[r][c+1]) | |
end | |
end | |
g.refresh() | |
end | |
g.event = function(x,y,z) | |
if x == 1 and y == 1 then bank = 1; m2.cc(0,127,4); end | |
if x == 1 and y == 3 then bank = 2; m2.cc(1,127,4); end | |
if x == 1 and y == 5 then bank = 3; m2.cc(2,127,4); end | |
if x == 1 and y == 7 then bank = 4; m2.cc(3,127,4); end | |
if x>=9 and x<=11 then | |
--store | |
if z==1 then | |
pressX = x; pressY = y; | |
sBank[y][x-8] = 8 | |
sequenceBank[y][x-8] = deepcopy(steps[bank]) | |
sequenceBank2[y][x-8] = deepcopy(rests[bank]) | |
sLength[y][x-8] = deepcopy(seqlen[bank]) | |
rF[y][x-8] = deepcopy(restfreq[bank]) | |
end | |
if z==0 then pressX = 0; pressY = 0; end | |
end | |
if x>=13 and x<=15 then | |
--recall | |
if z==1 then | |
pressX = x; pressY = y; | |
steps[bank] = deepcopy(sequenceBank[y][x-12]) | |
rests[bank] = deepcopy(sequenceBank2[y][x-12]) | |
seqlen[bank] = deepcopy(sLength[y][x-12]) | |
restfreq[bank] = deepcopy(rF[y][x-12]) | |
change[bank] = 0 | |
waitforsync[bank] = 1 | |
--print(y,x,steps[bank][1],steps[bank][2]) | |
end | |
if z==0 then pressX = 0; pressY = 0; end | |
end | |
redraw_grid() | |
redraw_twister() | |
redraw() | |
end | |
function init() | |
math.randomseed(os.time()) | |
-- initalize variables | |
pause = 1 | |
ct = 0 | |
bank = 1 | |
midiX = 0 | |
-- set defaults | |
position = {0,0,0,0} | |
change = {100,0,0,0} | |
restfreq = {25,25,25,25} | |
mnote = {0,0,0,0} | |
mute = {0,1,1,1} | |
mixer = {90,90,90,90} | |
transpose = {0,0,0,0} | |
velmod = {75,75,75,75} | |
shift = {0,0,0,0} | |
octave = {0,0,0,0} | |
tempomod = {1,1,1,1} | |
seqlen = {16,16,16,16} | |
dispersion = {15,15,15,15} | |
persist = {50,50,50,50} | |
waitforsync = {0,0,0,0} | |
tempo = 60 | |
-- norns parameters | |
maxscreen = 12 | |
-- grid presses | |
pressX = 0 | |
pressY = 0 | |
sequenceBank = {} | |
sequenceBank2 = {} | |
sBank = {} | |
sLength = {} | |
rF = {} | |
for j = 1,8 do | |
sBank[j] = {} | |
sLength[j] = {} | |
rF[j] = {} | |
sequenceBank[j] = {} | |
sequenceBank2[j] = {} | |
for i = 1,3 do | |
sBank[j][i] = 3 | |
sequenceBank[j][i] = {} | |
sequenceBank2[j][i] = {} | |
sLength[j][i] = seqlen[bank] | |
rF[j][i] = restfreq[bank] | |
end | |
end | |
-- set up musical scale | |
toniclist = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'} | |
tonicnum = 6 -- default = C | |
mode = 12 -- default = ionian scake | |
center = 48+(tonicnum-1) -- tonic center | |
maxscale = 30 | |
scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,maxscale) | |
scale2 = music.generate_scale_of_length(48,music.SCALES[1].name,40) | |
mbuffer = {}; | |
mbcount = 0; | |
-- initalize sequences | |
steps = {}; rests = {}; velos = {} | |
steps_copy = {}; rests_copy = {}; velos_copy = {} | |
for j=1,4 do | |
steps[j] = {}; rests[j] = {}; velos[j] = {} | |
for i=1,32 do | |
steps[j][i] = 15 | |
rests[j][i] = 0 | |
velos[j][i] = 0 | |
steps_copy[i] = steps[1][i] | |
rests_copy[i] = rests[1][i] | |
velos_copy[i] = velos[1][i] | |
end | |
end | |
seqlen_copy = 1 | |
bank_copy = 1 | |
-- clock settings | |
clk = beatclock.new() | |
clk_midi = midi.connect() | |
clk_midi.event = clk.process_midi | |
clk.on_step = count | |
clk.on_select_internal = function() clk:start() end | |
clk.on_select_external = function() print("external") end | |
clk:add_clock_params() | |
params:set("bpm",tempo) | |
m2.cc(0,127,4) -- set twister to bank 1 | |
redraw_grid() | |
redraw_twister() | |
redraw() | |
end | |
function value_to_encoder(val,mn,mx) | |
pct = (val-mn) / (mx-mn) | |
return round(127*pct) | |
end | |
function encoder_to_value(enc,mn,mx) | |
pct = enc / 127 | |
return round(mn + (mx-mn) * pct) | |
end | |
--redraw twister | |
function redraw_twister() | |
for j = 0,3 do | |
m2.cc(0+(j*16),value_to_encoder(tempo,1,120),1) | |
m2.cc(1+(j*16),value_to_encoder(tonicnum,1,12),1) | |
m2.cc(1+(j*16),value_to_encoder(mode,1,47),5) | |
m2.cc(2+(j*16),value_to_encoder(octave[j+1],-3,3),1) | |
m2.cc(3+(j*16),value_to_encoder(mixer[1],0,127),1) | |
m2.cc(4+(j*16),value_to_encoder(seqlen[j+1],1,32),1) | |
m2.cc(5+(j*16),value_to_encoder(change[j+1],0,100),1) | |
m2.cc(6+(j*16),value_to_encoder(restfreq[j+1],0,100),1) | |
m2.cc(7+(j*16),value_to_encoder(mixer[2],0,127),1) | |
m2.cc(8+(j*16),value_to_encoder(tempomod[j+1],1,8),1) | |
m2.cc(9+(j*16),value_to_encoder(dispersion[j+1],0,100),1) | |
m2.cc(10+(j*16),value_to_encoder(persist[j+1],0,100),1) | |
m2.cc(11+(j*16),value_to_encoder(mixer[3],0,127),1) | |
m2.cc(12+(j*16),value_to_encoder(shift[j+1],-8,8),1) | |
m2.cc(13+(j*16),value_to_encoder(transpose[j+1],-7,7),1) | |
m2.cc(14+(j*16),value_to_encoder(velmod[j+1],0,100),1) | |
m2.cc(15+(j*16),value_to_encoder(mixer[4],0,127),1) | |
if pause==1 then m2.cc(0+(j*16),127,2) else m2.cc(0+(j*16),0,2) end | |
if change[j+1]==100 then m2.cc(5+(j*16),127,2) else m2.cc(5+(j*16),0,2) end | |
if mute[1]==1 then m2.cc(3+(j*16),127,2) else m2.cc(3+(j*16),0,2) end | |
if mute[2]==1 then m2.cc(7+(j*16),127,2) else m2.cc(7+(j*16),0,2) end | |
if mute[3]==1 then m2.cc(11+(j*16),127,2) else m2.cc(11+(j*16),0,2) end | |
if mute[4]==1 then m2.cc(15+(j*16),127,2) else m2.cc(15+(j*16),0,2) end | |
end | |
end | |
-- redraw screen | |
function redraw() | |
screen.clear() | |
screen.font_size(8); | |
screen.level(maxscreen) | |
local g = 0 | |
g = g + 1; | |
screen.move(0,10+9*(g-1)); screen.text("scale : " .. toniclist[tonicnum] .. " " .. music.SCALES[mode].name) | |
g = g + 1; | |
screen.move(0,10+9*(g-1)); screen.text("bpm : "..params:get("bpm")) | |
screen.move(64,10+9*(g-1)); screen.text("bank : " .. bank) | |
g = g + 1 | |
screen.move(0,10+9*(g-1)); screen.text("length : " .. seqlen[bank]) | |
screen.move(64,10+9*(g-1)); screen.text("bpm mod : " .. tempomod[bank]) | |
g = g + 1 | |
screen.move(0,10+9*(g-1)); screen.text("random : " .. change[bank]) | |
screen.move(64,10+9*(g-1)); screen.text("rest prob : " .. restfreq[bank]) | |
g = g + 1 | |
screen.move(0,10+9*(g-1)); screen.text("dispers : " .. dispersion[bank]) | |
screen.move(64,10+9*(g-1)); screen.text("octave : " .. octave[bank]) | |
g = g + 1 | |
screen.move(0,10+9*(g-1)); screen.text("shift : " .. shift[bank]) | |
screen.move(64,10+9*(g-1)); screen.text("tpose : " .. transpose[bank]) | |
screen.update() | |
end | |
function count() | |
ct = ct + 1 | |
-- moves the sequence ahead by one step and turns on/off notes | |
for p = 1,4 do | |
if waitforsync[p]==1 then | |
-- If sequence length is different from length of 1st sequence, just set position[p] to position[1] | |
-- otherwise wait for the sequences to sync before unmuting the sequence | |
if (ct % 32 == 1) or (seqlen[p]==seqlen[1]) then | |
waitforsync[p] = 0 | |
position[p] = position[1]-1 | |
end | |
end | |
-- advance the sequence position, depending on the tempo modifier | |
if ct % tempomod[p] == 0 and waitforsync[p]==0 then | |
local tpos = position[p] | |
position[p] = (tpos % seqlen[p]) + 1 | |
local pos = ((tpos + shift[p]) % seqlen[p]) + 1 | |
-- update the sequence | |
update_sequence(p,pos) | |
-- turn off the last note | |
if mnote[p] > 0 or mute[p]==1 then | |
m1.note_off(mnote[p],0,p) | |
end | |
temp = (steps[p][pos]+transpose[p]); | |
if temp>maxscale then; temp = maxscale; end | |
if temp<1 then; temp = 1; end | |
note = scale[temp] + (octave[p]*12) | |
-- turn on a note unless there is a rest | |
if rests[p][pos] == 0 and note~=nil and pos~=nil then | |
if note>0 and mute[p]==0 then | |
local volum = mixer[p]+velos[p][pos] | |
--local volum = mixer[p] | |
if volum>127 then; volum = 127; end | |
if volum<0 then; volum = 0; end | |
m1.note_on(note,volum,p) | |
mnote[p] = note | |
print(ct, p, position[p], pos, music.note_num_to_name(note, 1), volum) | |
end | |
end | |
end | |
end | |
end | |
function update_sequence(p,pos) | |
-- updates each sequence in a probabilistic manner | |
-- the dispersion parameter controls how big of a jump the sequence can make | |
chg = change[p]*.01 | |
rfq = restfreq[p]*.01 | |
if (math.random()<chg) then | |
if midiX == 0 then | |
tpos = pos-1 | |
if tpos<1 then tpos = seqlen[p] end | |
delta = round(box_muller()*(dispersion[p]/50)*6) | |
steps[p][pos] = 15 + delta | |
if steps[p][pos]>#scale then steps[p][pos] = #scale - (steps[p][pos]-#scale) end | |
if steps[p][pos]<1 then steps[p][pos] = 1 - (steps[p][pos]) end | |
velos[p][pos] = round(box_muller()*(velmod[p]/50)*7) | |
if (math.random()<rfq) then | |
rests[p][pos] = 1 | |
else | |
rests[p][pos] = 0 | |
end | |
else | |
end | |
end | |
end | |
function clearAllNotes() | |
for i = 1,4 do | |
for j = 1,#scale do | |
m1.note_off(scale[j],0,i) | |
end | |
end | |
end | |
-------------- Twister functions -------------- | |
function twist_tempo() | |
tempo = round(encoder_to_value(value,0,120)) | |
params:set("bpm",tempo) | |
end | |
function twist_pause() | |
pause = 1 - pause | |
if pause == 0 then | |
clk:start() | |
else | |
clk:stop() | |
clearAllNotes() | |
end | |
end | |
function twist_tonic() | |
tonicnum = encoder_to_value(value,1,12) | |
center = 48+(tonicnum-1) -- tonic center | |
scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,maxscale) | |
end | |
function twist_scale() | |
mode = encoder_to_value(value,1,47) | |
center = 48+(tonicnum-1) -- tonic center | |
scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,maxscale) | |
end | |
function twist_octave(t) | |
octave[t] = encoder_to_value(value,-3,3) | |
end | |
function twist_mute(t); | |
mute[t] = 1 - mute[t] | |
end | |
function twist_mix(t); mixer[t] = encoder_to_value(value,0,127); end | |
function twist_seqlen(t); seqlen[t] = encoder_to_value(value,1,32); end | |
function twist_restfreq(t); restfreq[t] = encoder_to_value(value,0,100) end | |
function twist_random(t); change[t] = encoder_to_value(value,1,100); end | |
function twist_tempomod(t); tempomod[t] = encoder_to_value(value,1,8); end | |
function twist_dispersion(t); dispersion[t] = encoder_to_value(value,0,100); end | |
function twist_persist(t); persist[t] = encoder_to_value(value,0,100); end | |
function twist_shift(t); shift[t] = encoder_to_value(value,-8,8); print("shift " .. shift[t]); end | |
function twist_tpose(t); transpose[t] = encoder_to_value(value,-7,7); print("tpose " .. transpose[t]); end | |
function twist_velocity(t); velmod[t] = encoder_to_value(value,0,100); end | |
function twist_copy(t) | |
steps_copy = deepcopy(steps[t]) | |
rests_copy = deepcopy(rests[t]) | |
velos_copy = deepcopy(velos[t]) | |
seqlen_copy = deepcopy(seqlen[t]) | |
bank_copy = t | |
print("copy") | |
end | |
function twist_paste(t) | |
steps[t] = deepcopy(steps_copy) | |
rests[t] = deepcopy(rests_copy) | |
velos[t] = deepcopy(velos_copy) | |
change[t] = 0 | |
restfreq[t] = deepcopy(restfreq[bank_copy]) | |
seqlen[t] = deepcopy(seqlen_copy) | |
position[t] = deepcopy(position[bank_copy]) | |
end | |
function twist_lock(t) | |
temp = change[t] | |
if temp>0 then change[t] = 0; end | |
if temp==100 then change[t] = 0; end | |
if temp==0 then change[t] = 100; end | |
end | |
-------------- Utility functions -------------- | |
-- box_muller simulates normally distributed random numbers from uniform random numbers | |
function box_muller() | |
return math.sqrt(-2 * math.log(math.random())) * math.cos(2 * math.pi * math.random()) | |
end | |
-- round to nearest integer | |
function round(num) | |
under = math.floor(num) | |
upper = math.floor(num) + 1 | |
underV = -(under - num) | |
upperV = upper - num | |
if (upperV > underV) then; return under; else return upper; end | |
end | |
function deepcopy(orig) | |
-- make a copy of a table instead of making a direct reference | |
local orig_type = type(orig) | |
local copy | |
if orig_type == 'table' then | |
copy = {} | |
for orig_key, orig_value in next, orig, nil do | |
copy[deepcopy(orig_key)] = deepcopy(orig_value) | |
end | |
setmetatable(copy, deepcopy(getmetatable(orig))) | |
else -- number, string, boolean, etc | |
copy = orig | |
end | |
return copy | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment