| 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