Created
November 26, 2018 02:45
-
-
Save millxing/cf9fb2669367d7aa1e44265bb213180f to your computer and use it in GitHub Desktop.
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
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