This is the latest version (as of 11/4/2018) of QUENCE, a probabilistic MIDI sequencer for norns and grid (monome.org)
| -- Q * U * E * N * C * E | |
| -- | |
| -- a probababilistic | |
| -- 4-track MIDI sequencer | |
| -- for norns and grid | |
| -- | |
| -- Rob Schoen | |
| -- millxing at gmail | |
| -- inspired by Turing Machine, Fugue Machine, and Physical (Norns Study #4) | |
| -- random tips: | |
| -- bottom row is always the toolbar - pause, mutes for tracks 1-4, lock all, clear all, select tracks 1-4, and select settings page | |
| -- hold encoder 3 to see the midi notes in the sequence for the current track | |
| -- on the settings page, the eight buttons in rows 5-6, cols 13-16 are currently unassigned, | |
| -- but for now you can hit any of them to resync all the sequences | |
| music = require 'mark_eats/musicutil' -- super useful. Thanks Mark! | |
| beatclock = require 'beatclock' | |
| function init() | |
| opening_animation() | |
| math.randomseed(os.time()) | |
| -- initalize variables | |
| position = {} | |
| tempomod = {} | |
| seqlen = {} | |
| dispersion = {} | |
| for j = 1,4 do | |
| position[j] = 16 | |
| tempomod[j] = 1 | |
| seqlen[j] = 16 | |
| dispersion[j] = 5 | |
| end | |
| press = 0 | |
| xy = 0 | |
| transpose = 0 | |
| page = 1 | |
| tpage = -99 | |
| pagecopy = 1 | |
| lock = 0 | |
| pause = 1 | |
| ct = 0 | |
| toniclist = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'} | |
| tonicnum = 1 | |
| -- probabilities for randomness and rest frequencies | |
| changelist = {0, (1/6)^1.5, (2/6)^1.5, (3/6)^1.5, (4/6)^1.5, (5/6)^1.5, 1.00} | |
| rflist = {0, (1/6)^1.5, (2/6)^1.5, (3/6)^1.5, (4/6)^1.5, (5/6)^1.5, 1} | |
| -- set defaults | |
| change = {7,1,1,1} | |
| restfreq = {1,1,1,1} | |
| mnote = {0,0,0,0} | |
| mute = {0,0,0,0} | |
| -- norns and grid parameters | |
| maxscreen = 6 | |
| maxgrid = 10 | |
| inact = 4 | |
| -- set up musical scale | |
| mode = 12 -- default = major pentatonic | |
| center = 48+(tonicnum-1) -- tonic center | |
| scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,24) | |
| tempo = 60 -- default tempo | |
| -- initalize sequences | |
| steps = {} | |
| rests = {} | |
| steps_copy = {} | |
| rests_copy = {} | |
| for j=1,4 do | |
| steps[j] = {} | |
| rests[j] = {} | |
| for i=1,seqlen[j] do | |
| steps[j][i] = 15 | |
| rests[j][i] = 1 | |
| steps_copy[i] = steps[1][i] | |
| rests_copy[i] = rests[1][i] | |
| end | |
| end | |
| -- 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) | |
| redraw() | |
| grid_redraw() | |
| end | |
| -- redraw screen | |
| function redraw() | |
| screen.clear() | |
| screen.level(maxscreen) | |
| screen.move(0,10) | |
| screen.text("global bpm : "..params:get("bpm")) | |
| screen.move(0,20) | |
| screen.text("tonic and scale : " .. toniclist[tonicnum] .. " " .. music.SCALES[mode].name) | |
| screen.move(0,30) | |
| screen.text("sequence lengths : " .. seqlen[1] .. " " .. seqlen[2] .. " " .. seqlen[3] .. " " .. seqlen[4]) | |
| screen.move(0,40) | |
| screen.text("tempo modifiers : " .. tempomod[1] .. " " .. tempomod[2] .. " " .. tempomod[3] .. " " .. tempomod[4]) | |
| screen.move(0,50) | |
| screen.text("dispersions : " .. dispersion[1] .. " " .. dispersion[2] .. " " .. dispersion[3] .. " " .. dispersion[4]) | |
| screen.update() | |
| end | |
| -- redraw grid | |
| function grid_redraw() | |
| if tpage~=page then | |
| g.all(0) | |
| grid_redraw_ctrl() | |
| end | |
| if page == 0 and tpage~=page then | |
| grid_redraw_home() | |
| end | |
| if page > 0 then | |
| grid_redraw_page() | |
| end | |
| g.refresh() | |
| tpage = page | |
| end | |
| -- redraw toolbar at bottom row of grid | |
| function grid_redraw_ctrl() | |
| g.led(1,8,pause==1 and maxgrid or inact) --pause | |
| for i = 3,6 do | |
| g.led(i,8,mute[i-2]==1 and maxgrid or inact) --mutes(1-4) | |
| end | |
| g.led(8,8,lock==1 and maxgrid or inact) --lock all | |
| g.led(9,8,inact) --randomize all | |
| for i = 11,14 do | |
| g.led(i,8,(page==(i-10)) and maxgrid or inact) --select track pages (1-4) | |
| end | |
| g.led(16,8,page==0 and maxgrid or inact) --select home page | |
| end | |
| -- redraw home page of grid | |
| function grid_redraw_home() | |
| g.led(1,2,inact) g.led(1,3,inact) -- seqlen 1 | |
| g.led(2,2,inact) g.led(2,3,inact) -- seqlen 2 | |
| g.led(3,2,inact) g.led(3,3,inact) -- seqlen 3 | |
| g.led(4,2,inact) g.led(4,3,inact) -- seqlen 4 | |
| g.led(6,2,inact) g.led(6,3,inact) -- bpm coarse | |
| g.led(7,2,inact) g.led(7,3,inact) -- bpm fi | |
| g.led(10,2,inact) g.led(10,3,inact) -- tonic | |
| g.led(11,2,inact) g.led(11,3,inact) -- scale | |
| g.led(13,2,inact) g.led(13,3,inact) -- tempo mod 1 | |
| g.led(14,2,inact) g.led(14,3,inact) -- tempo mod 2 | |
| g.led(15,2,inact) g.led(15,3,inact) -- tempo mod 3 | |
| g.led(16,2,inact) g.led(16,3,inact) -- tempo mod 4 | |
| g.led(1,5,inact) g.led(1,6,inact) -- dispersion 1 | |
| g.led(2,5,inact) g.led(2,6,inact) -- dispersion 2 | |
| g.led(3,5,inact) g.led(3,6,inact) -- dispersiom 3 | |
| g.led(4,5,inact) g.led(4,6,inact) -- dispersion 4 | |
| g.led(13,5,inact) g.led(13,6,inact) -- unassigned | |
| g.led(14,5,inact) g.led(14,6,inact) -- unassigned | |
| g.led(15,5,inact) g.led(15,6,inact) -- unassigned | |
| g.led(16,5,inact) g.led(16,6,inact) -- unassigned | |
| end | |
| -- redraw the track view for the selected track | |
| function grid_redraw_page() | |
| -- turn off all leds on the top 2 rows | |
| for i = 1,seqlen[page] do | |
| g.led(i,1,0) | |
| g.led(i,2,0) | |
| end | |
| -- draw top 2 rows (sequence position and scale degrees) | |
| for i = 1,seqlen[page] do | |
| g.led(i,1,i==position[page] and maxgrid or inact) | |
| if rests[page][i]==0 and steps[page][i]>0 then | |
| val = math.floor((steps[page][i]/#scale)*maxgrid)+1 | |
| if val > maxgrid then val = maxgrid end | |
| if val < 0 then val = 1 end | |
| g.led(i,2,val) | |
| else | |
| g.led(i,2,0) | |
| end | |
| end | |
| -- draw randomness selector and rest frequency selector on row 4 | |
| for j = 1,7 do | |
| g.led(j,4,j==change[page] and maxgrid or inact) | |
| g.led(j+9,4,j==restfreq[page] and maxgrid or inact) | |
| end | |
| -- draw various buttons on rows 6 and 7 | |
| g.led(1,6,(press==106) and maxgrid or inact) --copy | |
| g.led(2,6,(press==206) and maxgrid or inact) --paste | |
| g.led(4,6,(press==406) and maxgrid or inact) --shift -> | |
| g.led(5,6,(press==506) and maxgrid or inact) --shift <- | |
| g.led(12,6,(press==1206) and maxgrid or inact) -- transpose up | |
| g.led(13,6,(press==1306) and maxgrid or inact) -- transpose down | |
| g.led(15,6,(press==1506) and maxgrid or inact) -- invert | |
| g.led(16,6,(press==1606) and maxgrid or inact) -- reverse | |
| g.led(8,7,(press==807) and maxgrid or inact) --track lock | |
| g.led(9,7,(press==907) and maxgrid or inact) --track clear | |
| end | |
| -- connect grid | |
| g = grid.connect() | |
| -- midi code | |
| m = midi.connect() | |
| m.event = function(data) | |
| local d = midi.to_msg(data) | |
| if d.type == "note_on" then | |
| transpose = d.note - 60 | |
| end | |
| end | |
| -- grid events | |
| g.event = function(x,y,z) | |
| xy = x*100+y -- integer code for coordinates of grid event | |
| -- settings page button press events --------------------------------------------------- | |
| if z == 1 and page == 0 then | |
| press = 0 | |
| -- coarse global tempo up | |
| if xy == 602 then | |
| tempo = tempo + 10 | |
| params:set("bpm",tempo) | |
| press = xy | |
| end | |
| -- coarse global tempo down | |
| if xy == 603 then | |
| tempo = tempo - 10 | |
| if tempo<1 then tempo = 10 end | |
| params:set("bpm",tempo) | |
| press = xy | |
| end | |
| -- fine global tempo up | |
| if xy == 702 then | |
| tempo = tempo + 1 | |
| params:set("bpm",tempo) | |
| press = xy | |
| end | |
| -- fine global tempo down | |
| if xy == 703 then | |
| tempo = tempo - 1 | |
| if tempo<1 then tempo = 1 end | |
| params:set("bpm",tempo) | |
| press = xy | |
| end | |
| -- tonic up | |
| if xy == 1002 then | |
| tonicnum = tonicnum + 1 | |
| if tonicnum>12 then tonicnum = 1 end | |
| center = 48+(tonicnum-1) -- tonic center | |
| scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,24) | |
| press = xy | |
| end | |
| -- tonic down | |
| if xy == 1003 then | |
| tonicnum = tonicnum - 1 | |
| if tonicnum<1 then tonicnum = 12 end | |
| center = 48+(tonicnum-1) -- tonic center | |
| scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,24) | |
| press = xy | |
| end | |
| -- scale up | |
| if xy == 1102 then | |
| mode = mode + 1 | |
| if mode>47 then mode = 1 end | |
| center = 48+(tonicnum-1) -- tonic center | |
| scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,24) | |
| press = xy | |
| end | |
| -- scale down | |
| if xy == 1103 then | |
| mode = mode - 1 | |
| if mode<1 then mode = 47 end | |
| center = 48+(tonicnum-1) -- tonic center | |
| scale = music.generate_scale_of_length(center-12,music.SCALES[mode].name,24) | |
| press = xy | |
| end | |
| -- sequence lengths for tracks 1-4 | |
| if x>0 and x<5 and (y==2 or y==3) then | |
| i = x | |
| if y==2 then | |
| seqlen[i] = seqlen[i] + 1 | |
| if seqlen[i]>16 then seqlen[i] = 16 end | |
| else | |
| seqlen[i] = seqlen[i] - 1 | |
| if seqlen[i]<1 then seqlen[i] = 16 end | |
| end | |
| position[i] = 16 | |
| press = xy | |
| end | |
| -- dispersion parameter for tracks 1-4 | |
| if x>0 and x<5 and (y==5 or y==6) then | |
| i = x | |
| if y==5 then | |
| dispersion[i] = dispersion[i] + 1 | |
| if dispersion[i]>10 then dispersion[i] = 10 end | |
| else | |
| dispersion[i] = dispersion[i] - 1 | |
| if dispersion[i]<0 then dispersion[i] = 0 end | |
| end | |
| press = xy | |
| end | |
| -- tempo modifier for tracks 1-4 | |
| if x>12 and (y==2 or y==3) then | |
| i = x-12 | |
| if y==2 then | |
| tempomod[i] = tempomod[i] + 1 | |
| if tempomod[i]>8 then tempomod[i] = 8 end | |
| else | |
| tempomod[i] = tempomod[i] - 1 | |
| if tempomod[i]<1 then tempomod[i] = 1 end | |
| end | |
| press = xy | |
| end | |
| -- re-sync all sequences | |
| if x>12 and (y==5 or y==6) then | |
| syncTracks() | |
| press = xy | |
| end | |
| end | |
| -- track page button press events --------------------------------------------------- | |
| if z == 1 and page > 0 then | |
| -- change probability (row 4, left side) | |
| if y == 4 and x < 8 then | |
| change[page] = x | |
| end | |
| -- rest frequency (row 4, right side) | |
| if y == 4 and x > 9 then | |
| restfreq[page] = x-9 | |
| end | |
| -- sequence position (top row) | |
| if y == 1 then | |
| position[page] = x | |
| end | |
| -- toggle rest (2nd row from top) | |
| if y == 2 then | |
| rests[page][x] = 1-rests[page][x] | |
| end | |
| -- copy sequence (row 6, col 1) | |
| if y==6 and x==1 then | |
| for i = 1,16 do | |
| steps_copy[i] = steps[page][i] | |
| rests_copy[i] = rests[page][i] | |
| end | |
| pagecopy = page | |
| press = xy | |
| end | |
| -- paste sequence (row 6, col 2) | |
| if y==6 and x==2 then | |
| for i = 1,16 do | |
| steps[page][i] = steps_copy[i] | |
| rests[page][i] = rests_copy[i] | |
| end | |
| seqlen[page] = seqlen[pagecopy] | |
| change[page] = 1 | |
| press = xy | |
| end | |
| -- shift left (row 6, col 4) | |
| if xy == 406 then | |
| shift_left() | |
| press = xy | |
| end | |
| -- shift right (row 6, col 5) | |
| if xy == 506 then | |
| shift_right() | |
| press = xy | |
| end | |
| -- scalar transpose down (row 6, col 12) | |
| if xy == 1206 then | |
| for i = 1,seqlen[page] do | |
| steps[page][i] = steps[page][i] - 1 | |
| if steps[page][i] < 1 then steps[page][i] = #scale end | |
| end | |
| press = xy | |
| end | |
| -- scalar transpose up (row 6 col 13) | |
| if xy == 1306 then | |
| for i = 1,seqlen[page] do | |
| steps[page][i] = steps[page][i] + 1 | |
| if steps[page][i] > #scale then steps[page][i] = 1 end | |
| end | |
| press = xy | |
| end | |
| -- invert (row 6 col 15) | |
| if xy == 1506 then | |
| for i = 1,seqlen[page] do | |
| steps[page][i] = (12-steps[page][i]) + 12 | |
| end | |
| press = xy | |
| end | |
| -- reverse (row 6 col 16) | |
| if xy == 1606 then | |
| temp_steps = deepcopy(steps[page]) | |
| temp_rests = deepcopy(rests[page]) | |
| for i = 1,seqlen[page] do | |
| steps[page][i] = temp_steps[seqlen[page]+1-i] | |
| rests[page][i] = temp_rests[seqlen[page]+1-i] | |
| end | |
| press = xy | |
| end | |
| -- lock track (row 7 col 8 -- sets randomness for track to 0) | |
| if xy == 807 then | |
| change[page] = 1 | |
| press = xy | |
| end | |
| -- clear track (row 7 col 9) | |
| if xy == 907 then | |
| change[page] = 1 | |
| restfreq[page] = 1 | |
| for j=1,16 do | |
| steps[page][j] = 15 | |
| rests[page][j] = 1 | |
| end | |
| press = xy | |
| end | |
| end | |
| -- toolbar button press events --------------------------------------------------- | |
| if z == 1 and y == 8 then | |
| -- pause all sequences (row 8, col 1) | |
| if xy == 108 then | |
| pause = 1 - pause | |
| if pause == 0 then clk:start() else clk:stop() end | |
| -- clear all note ons | |
| if pause == 1 then | |
| for p = 1,4 do | |
| if mnote[p] > 0 then | |
| m.note_off(mnote[p],0,p) | |
| end | |
| end | |
| end | |
| end | |
| -- mute tracks (row 8 cols 3-6) | |
| if y==8 and (x>=3 and x<=6) then | |
| mute[x-2] = 1-mute[x-2] | |
| end | |
| -- lock all tracks (row 8 col 8) | |
| if y==8 and x==8 then | |
| lock = 1 - lock | |
| for i = 1,4 do | |
| change[i] = 1 | |
| end | |
| g.led(x,y,maxgrid) | |
| g.refresh() | |
| end | |
| -- clear all tracks (row 8 col 9) | |
| if y==8 and x==9 then | |
| for i = 1,4 do | |
| change[i] = 1 | |
| restfreq[i] = 1 | |
| for j=1,seqlen[i] do | |
| steps[i][j] = 15 | |
| rests[i][j] = 1 | |
| end | |
| end | |
| press = xy | |
| end | |
| -- select track page (row 8 cols 11-14) | |
| if y==8 and (x>=11 and x<=14) then page = x-10 end | |
| -- select settings page (row 8 col 16) | |
| if y==8 and x==16 then page = 0 end | |
| -- light up a pressed button | |
| if press>0 then | |
| g.led(x,y,maxgrid) | |
| g.refresh() | |
| end | |
| end | |
| -- button unpressed events | |
| if z == 0 then | |
| if press>0 then | |
| g.led(x,y,inact) | |
| press = 0 | |
| else | |
| g.led(x,y,0) | |
| g.refresh() | |
| end | |
| end | |
| -- pause led | |
| if pause == 1 then g.led(1,8,maxgrid) else g.led(1,8,inact) end | |
| -- mute leds | |
| for i = 1,4 do | |
| if mute[i] == 1 then g.led(2+i,8,maxgrid) else g.led(2+i,8,inact) end | |
| end | |
| -- track select leds | |
| for i = 1,4 do | |
| if page == i then g.led(10+i,8,maxgrid) else g.led(10+i,8,inact) end | |
| end | |
| -- settings page led | |
| if page == 0 then g.led(16,8,maxgrid) else g.led(16,8,inact) end | |
| -- turn off lock led if any track is unlocked | |
| for i = 1,4 do | |
| if change[i]>1 then lock = 0 end | |
| end | |
| if lock == 1 then g.led(8,8,maxgrid) else g.led(8,8,inact) end | |
| -- redraw | |
| redraw() | |
| grid_redraw() | |
| end | |
| function count() | |
| ct = ct + 1 | |
| -- moves the sequence ahead by one step and turns on/off notes | |
| for p = 1,4 do | |
| -- advance the sequence position, depending on the tempo modifier | |
| if ct % tempomod[p] == 0 then | |
| position[p] = (position[p] % seqlen[p]) + 1 | |
| -- update the sequence | |
| update_sequence(p) | |
| -- turn off the last note | |
| if mnote[p] > 0 or mute[p]==1 then | |
| m.note_off(mnote[p],0,p) | |
| end | |
| note = scale[steps[p][position[p]]] | |
| -- turn on a note unless there is a rest | |
| if rests[p][position[p]] == 0 and note~=nil then | |
| note = note + transpose | |
| if note>0 and mute[p]==0 then | |
| m.note_on(note,90,p) | |
| mnote[p] = note | |
| end | |
| end | |
| end | |
| end | |
| grid_redraw() | |
| end | |
| function update_sequence(p) | |
| -- updates each sequence in a probabilistic manner | |
| -- the dispersion parameter controls how big of a jump the sequence can make | |
| chg = changelist[change[p]] | |
| rfq = rflist[restfreq[p]] | |
| if (math.random()<chg) then | |
| tposition = position[p]-1 | |
| if tposition<1 then tposition = seqlen[p] end | |
| delta = round(box_muller()*(dispersion[p]/5)) | |
| delta = delta + round((15-steps[p][position[p]])*.05) | |
| steps[p][position[p]] = steps[p][tposition] + delta | |
| if steps[p][position[p]]>#scale then steps[p][position[p]] = #scale - (steps[p][position[p]]-#scale) end | |
| if steps[p][position[p]]<1 then steps[p][position[p]] = 1 - (steps[p][position[p]]) end | |
| if (math.random()<rfq) then | |
| rests[p][position[p]] = 1 | |
| else | |
| rests[p][position[p]] = 0 | |
| end | |
| end | |
| end | |
| function key(n,z) | |
| -- while held, button 3 displays the midi notes of the sequence on the selected track | |
| if n == 3 and z == 1 then | |
| screen.clear() | |
| bb = " " | |
| for i = 1,seqlen[page] do | |
| aa = scale[steps[page][i]] | |
| if rests[page][i]==1 then aa = 0 end | |
| if aa<100 then | |
| if aa==0 then | |
| aa = (" " .. aa) | |
| else | |
| aa = (" " .. aa) | |
| end | |
| end | |
| bb = (bb .. aa .. " ") | |
| end | |
| screen.move(0,10) | |
| screen.text("Track #" .. page) | |
| for k = 1,4 do | |
| screen.move(0,20+10*k) | |
| cc = string.sub(bb,(k-1)*16+1,(k-1)*16+16) | |
| screen.text(cc) | |
| end | |
| screen.update() | |
| end | |
| if n == 3 and z == 0 then | |
| redraw() | |
| end | |
| end | |
| function shift_left() | |
| -- shifts the sequence to the left, wrapping the first note to the end of the sequence | |
| -- rewrite this using deepcopy | |
| local stp = steps[page][1] | |
| local rst = rests[page][1] | |
| for i = 1,(seqlen[page]-1) do | |
| steps[page][i] = steps[page][i+1] | |
| rests[page][i] = rests[page][i+1] | |
| end | |
| steps[page][seqlen[page]] = stp | |
| rests[page][seqlen[page]] = rst | |
| end | |
| function shift_right() | |
| -- shifts the sequence to the right, wrapping the last note to the start of the sequence | |
| -- rewrite this using deepcopy | |
| local stp = steps[page][seqlen[page]] | |
| local rst = rests[page][seqlen[page]] | |
| for i = seqlen[page],2,-1 do | |
| steps[page][i] = steps[page][i-1] | |
| rests[page][i] = rests[page][i-1] | |
| end | |
| steps[page][1] = stp | |
| rests[page][1] = rst | |
| end | |
| function max(a) | |
| --return the max of a numeric table | |
| local values = {} | |
| for k,v in pairs(a) do | |
| values[#values+1] = v | |
| end | |
| table.sort(values) | |
| return values[#values] | |
| end | |
| function min(a) | |
| --return the max of a numeric table | |
| local values = {} | |
| for k,v in pairs(a) do | |
| values[#values+1] = v | |
| end | |
| table.sort(values) | |
| return values[1] | |
| end | |
| function calcScaleDegrees() | |
| -- returns the number of scale degrees in given scale | |
| -- needs improvement | |
| if scale[8]-scale[1] == 12 then | |
| scaleDegrees = 7 | |
| else | |
| if scale[6]-scale[1] == 12 then | |
| scaleDegrees = 5 | |
| else | |
| if scale[7]-scale[1] == 12 then | |
| scaleDegrees = 6 | |
| else | |
| if scale[9]-scale[1] == 12 then | |
| scaleDegrees = 8 | |
| end | |
| end | |
| end | |
| end | |
| if scaleDegrees==0 then | |
| print("scale degrees unknown") | |
| scaleDegrees = 7 | |
| end | |
| return scaleDegrees | |
| 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 | |
| function reverse(orig) | |
| -- reverse a numeric table | |
| local i, j = 1, #orig | |
| while i < j do | |
| orig[i], orig[j] = orig[j], orig[i] | |
| i = i + 1 | |
| j = j - 1 | |
| end | |
| end | |
| function syncTracks() | |
| for i = 1,4 do | |
| position[i] = 0 | |
| end | |
| end | |
| function clearAllNotes() | |
| for i = 1,4 do | |
| for j = 1,#scale do | |
| m.note_off(scale[j],0,i) | |
| end | |
| end | |
| end | |
| -- 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 | |
| -- gratuitous opening animation | |
| function opening_animation() | |
| for a = 8,1,-1 do | |
| g.all(0) | |
| for i = 1,8 do | |
| for j = 1+(a-1),16-(a-1) do | |
| g.led(j,i,math.random(0,15)) | |
| end | |
| end | |
| g.refresh() | |
| sleep(.1) | |
| end | |
| for a = 1,8 do | |
| g.all(0) | |
| for i = 1,8 do | |
| for j = 1+(a-1),16-(a-1) do | |
| g.led(j,i,math.random(0,15)) | |
| end | |
| end | |
| g.refresh() | |
| sleep(.1) | |
| end | |
| g.all(0) | |
| g.refresh() | |
| end | |
| -- pause lua code | |
| function sleep(s) | |
| local ntime = os.clock() + s | |
| repeat until os.clock() > ntime | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment