Skip to content

Instantly share code, notes, and snippets.

@millxing
Created November 26, 2018 02:45
Show Gist options
  • Save millxing/cf9fb2669367d7aa1e44265bb213180f to your computer and use it in GitHub Desktop.
Save millxing/cf9fb2669367d7aa1e44265bb213180f to your computer and use it in GitHub Desktop.
music = require 'mark_eats/musicutil' -- super useful. Thanks Mark!
beatclock = require 'beatclock'
-- midi code
m = midi.connect(1)
function init()
math.randomseed(os.time())
-- initalize variables
glob = {}
glob.pause = 0
glob.ct = 0
glob.tracks = 1
local i; local j
seqs = {}
for i = 1,8 do
seqs[i] = {}
seqs[i].position = 0
seqs[i].seqlen = 16
seqs[i].change = 100
seqs[i].restfreq = 0
seqs[i].mnote = {0,0,0,0}
seqs[i].mute = 1
seqs[i].mixer = 90
seqs[i].shift = 0
seqs[i].transpose = 0
seqs[i].velmod = 75
seqs[i].tempomod = 1
seqs[i].octave = 0;
seqs[i].dispersion = 15
seqs[i].isorig = 0
seqs[i].steps = {}
seqs[i].rests = {}
seqs[i].velos = {}
seqs[i].chord = {}
for j = 1,32 do
seqs[i].steps[j] = 15
seqs[i].rests[j] = 0
seqs[i].velos[j] = 0
seqs[i].chord[j] = 0
end
end
glob.tempo = math.random(30,90)
glob.tonicnum = math.random(1,12)
glob.mode = math.random(1,9)
--glob.tonicnum = 1
--glob.mode = 1
glob.toniclist = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}
glob.center = 48 + (glob.tonicnum-1) -- tonic center
glob.scalenum = 50
glob.scale = music.generate_scale_of_length(glob.center-12, music.SCALES[glob.mode].name, glob.scalenum)
if glob.tempo>80 then
seqs[1].seqlen = math.random(1,8) * 4
else
if glob.tempo>50 then
seqs[1].seqlen = math.random(1,6) * 4
else
seqs[1].seqlen = math.random(1,4) * 4
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",glob.tempo)
if glob.pause == 0 then
clk:start()
end
redraw()
end
-- redraw screen
function redraw()
screen.clear()
screen.font_size(8);
screen.level(15)
local g = 0
g = g + 1;
screen.move(0,10+9*(g-1)); screen.text("scale : " .. glob.toniclist[glob.tonicnum] .. " " .. music.SCALES[glob.mode].name)
g = g + 1;
screen.move(0,10+9*(g-1)); screen.text("bpm : "..params:get("bpm"))
screen.update()
end
function count()
glob.ct = glob.ct + 1
songscript()
-- moves the sequence ahead by one step and turns on/off notes
local p
for p = 1,4 do
if ((glob.ct % seqs[p].tempomod) == 0) then
local tpos = seqs[p].position
seqs[p].position = (tpos % seqs[p].seqlen) + 1
local pos = ((tpos + seqs[p].shift) % seqs[p].seqlen) + 1
-- update sequence
update_sequence(p,pos)
-- turn off the last note
if (seqs[p].mnote[1] > 0) or (seqs[p].mute == 1) or (seqs[p].mixer == 0) then
m.note_off(seqs[p].mnote[1],0,p)
m.note_off(seqs[p].mnote[2],0,p)
m.note_off(seqs[p].mnote[3],0,p)
m.note_off(seqs[p].mnote[4],0,p)
end
local temp = (seqs[p].steps[pos] + seqs[p].transpose);
if temp > glob.scalenum then; temp = glob.scalenum; end
if temp < 1 then; temp = 1; end
local note = {0,0,0,0}
note[1] = glob.scale[temp] + (seqs[p].octave*12)
if seqs[p].chord == 1 then; note[2] = note[1] + 12; end
if seqs[p].chord == 2 then; note[2] = glob.scale[temp+2] + (seqs[p].octave*12); end
if seqs[p].chord == 3 then; note[2] = glob.scale[temp+4] + (seqs[p].octave*12); end
if seqs[p].chord == 4 then; note[2] = glob.scale[temp+2] + (seqs[p].octave*12); note[3] = glob.scale[temp+4] + (seqs[p].octave*12); end
if seqs[p].chord == 5 then; note[2] = glob.scale[temp+2] + (seqs[p].octave*12); note[3] = glob.scale[temp+4] + (seqs[p].octave*12); note[4] = glob.scale[temp+6] + (seqs[p].octave*12); end
-- turn on a note unless there is a rest
if seqs[p].rests[pos] == 0 and note[1]~=nil and pos~=nil then
if note[1]>0 and seqs[p].mute==0 then
local volum = seqs[p].mixer + seqs[p].velos[pos]
--local volum = mixer[p]
if volum>127 then; volum = 127; end
if volum<0 then; volum = 0; end
m.note_on(note[1],volum,p)
if note[2] > 0 then; m.note_on(note[2],volum,p); end
if note[3] > 0 then; m.note_on(note[3],volum,p); end
if note[4] > 0 then; m.note_on(note[4],volum,p); end
seqs[p].mnote = note
local temp = music.note_nums_to_names(note,1)
if note[2]==0 then temp[2] = ''; end
if note[3]==0 then temp[3] = ''; end
if note[4]==0 then temp[4] = ''; end
print(glob.ct, p, seqs[p].position, pos, temp[1], temp[2], temp[3], temp[4])
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 = seqs[p].change*.01
rfq = seqs[p].restfreq*.01
if (math.random()<chg) then
tpos = pos-1
if tpos<1 then tpos = seqs[p].seqlen end
delta = round(box_muller()*(seqs[p].dispersion/50)*6)
seqs[p].steps[pos] = 15 + delta
--print(p,pos,15+delta)
if seqs[p].steps[pos]>#glob.scale then seqs[p].steps[pos] = #glob.scale - (seqs[p].steps[pos]-#glob.scale) end
if seqs[p].steps[pos]<1 then seqs[p].steps[pos] = 1 - (seqs[p].steps[pos]) end
if (math.random()<rfq) then
seqs[p].rests[pos] = 1
else
seqs[p].rests[pos] = 0
end
seqs[p].velos[pos] = round(box_muller()*(seqs[p].velmod/50)*7)
end
end
function clearAllNotes()
for i = 1,4 do
for j = 1,#glob.scale do
m1.note_off(glob.scale[j],0,i)
end
end
end
function mute_track(t);
seqs[t].mute = 1 - seqs[t].mute
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
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 round(val, decimal)
if (decimal) then
return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
else
return math.floor(val+0.5)
end
end
function format_num(amount, decimal, prefix, neg_prefix)
local str_amount, formatted, famount, remain
decimal = decimal or 2 -- default 2 decimal places
neg_prefix = neg_prefix or "-" -- default negative sign
famount = math.abs(round(amount,decimal))
famount = math.floor(famount)
remain = round(math.abs(amount) - famount, decimal)
-- comma to separate the thousands
formatted = comma_value(famount)
-- attach the decimal portion
if (decimal > 0) then
remain = string.sub(tostring(remain),3)
formatted = formatted .. "." .. remain ..
string.rep("0", decimal - string.len(remain))
end
-- attach prefix string e.g '$'
formatted = (prefix or "") .. formatted
-- if value is negative then format accordingly
if (amount<0) then
if (neg_prefix=="()") then
formatted = "("..formatted ..")"
else
formatted = neg_prefix .. formatted
end
end
return formatted
end
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
function songscript()
if (glob.ct-1) == 0 then
seqs[1].mute = 0
seqs[1].octave = 0
seqs[1].change = 100
seqs[1].restfreq = math.random(0,50)
seqs[1].velmod = math.random(0,100)
seqs[1].dispersion = math.random(10,30)
seqs[1].tempomod = 1
end
if (glob.ct-1) == (seqs[1].seqlen) then
seqs[1].change = 0
orig_seq = deepcopy(seqs[1])
seqs[1].isorig = 1
print("lock original sequence")
--clk:stop()
end
if (((glob.ct-1) % (seqs[1].seqlen*1)) == 0) and (glob.ct>31) then
if (math.random()<.8) or (glob.tracks==3) then
local t = math.random(1,glob.tracks)
op = math.random(1,7)
if op == 1 and seqs[t].mute==0 then
if seqs[t].change==0 then
seqs[t].change = math.random(10,40)
seqs[t].isorig = 0
print("sequence " .. t .. " change = " .. seqs[t].change)
else
seqs[t].change = 0
print("sequence " .. t .. " locked")
end
end
if op == 2 and seqs[t].isorig == 0 and seqs[t].mute==0 then
seqs[t] = deepcopy(orig_seq)
seqs[t].isorig = 1
print("reset sequence " .. t .. " to original sequence")
end
if op == 3 and t>1 and seqs[t].mute==0 then
seqs[t].shift = math.random(-8,8)
seqs[t].isorig = 0
print("sequence " .. t .. " shifted by " .. seqs[t].shift)
end
if op == 4 and seqs[t].mute==0 then
seqs[t].transpose = math.random(-8,8)
seqs[t].isorig = 0
print("sequence " .. t .. " transposed by " .. seqs[t].transpose)
end
if op == 5 and t>1 and seqs[t].mute==0 then
seqs[t].tempomod = 2 ^ math.random(0,2)
seqs[t].isorig = 0
print("sequence " .. t .. " tempomod = " .. seqs[t].tempomod)
end
if op == 6 and seqs[t].mute==0 then
seqs[t].chord = math.random(0,5)
seqs[t].isorig = 0
print("sequence " .. t .. " chord = " .. seqs[t].chord)
end
if op == 7 and t>1 and seqs[t].mute==0 then
seqs[t].mute = 1 - seqs[t].mute
if seqs[t].mute == 1 then
print("sequence " .. t .. " muted ")
else
print("sequence " .. t .. " un-muted ")
end
end
else
glob.tracks = glob.tracks + 1
local t = glob.tracks
seqs[t] = deepcopy(orig_seq)
--seqs[glob.tracks].octave = (math.floor(math.random()*5)+1) - 3
seqs[t].isorig = 1
local done = 0
while done == 0 do
seqs[t].octave = math.random(-2,2)
done = 1
for i = 1,t-1 do
if seqs[t].octave == seqs[i].octave then done = 0; end
end
end
print("start track " .. t)
end
end
--seqs[1].octave = 0; seqs[2].octave = -2; seqs[3].octave = -1; seqs[4].octave = 1;
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment