- added ddr test to main menu\n- tweaked sounds\n- musicator tool base code, needs samples
This commit is contained in:
130
tools/musicator/musicator.lua
Normal file
130
tools/musicator/musicator.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
unpack = unpack or table.unpack
|
||||
|
||||
function build_markov_model(sequence, order)
|
||||
local function make_key(tbl)
|
||||
return table.concat(tbl, "|")
|
||||
end
|
||||
|
||||
local function unmake_key(k)
|
||||
local result = {}
|
||||
for t in string.gmatch(k, "[^|]+") do
|
||||
result[#result + 1] = t
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local function add_key(str, value)
|
||||
return str .. "|" .. value
|
||||
end
|
||||
|
||||
local function split_last(full)
|
||||
local i = full:match(".*()|")
|
||||
return full:sub(1, i-1), full:sub(i+1)
|
||||
end
|
||||
|
||||
local counts = {}
|
||||
local totals = {}
|
||||
|
||||
-- count
|
||||
for i = 1, #sequence - order do
|
||||
local notes = make_key({unpack(sequence, i, i + order - 1)})
|
||||
totals[notes] = (totals[notes] or 0) + 1
|
||||
|
||||
local notes_full = add_key(notes, sequence[i + order])
|
||||
counts[notes_full] = (counts[notes_full] or 0) + 1
|
||||
end
|
||||
|
||||
-- build model
|
||||
local model = {}
|
||||
|
||||
for notes_full,count in pairs(counts) do
|
||||
local notes, _ = split_last(notes_full)
|
||||
|
||||
model[notes_full] = count[notes_full] / total[notes]
|
||||
end
|
||||
|
||||
return {
|
||||
order = order,
|
||||
model = model,
|
||||
counts = counts -- keep raw counts (useful!)
|
||||
}
|
||||
end
|
||||
|
||||
function generate_sequence(model_data, length)
|
||||
local model = model_data.model
|
||||
local order = model_data.order
|
||||
|
||||
-- helper: split key into parts
|
||||
local function split(k)
|
||||
local t = {}
|
||||
for part in string.gmatch(k, "[^|]+") do
|
||||
t[#t+1] = part
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- pick random starting state
|
||||
local start_key
|
||||
for k,_ in pairs(model) do
|
||||
start_key = k
|
||||
break
|
||||
end
|
||||
|
||||
-- (optional: better random start)
|
||||
for k,_ in pairs(model) do
|
||||
if math.random() < 0.1 then
|
||||
start_key = k
|
||||
end
|
||||
end
|
||||
|
||||
local parts = split(start_key)
|
||||
|
||||
-- initial sequence = first `order` items
|
||||
local seq = {}
|
||||
for i = 1, order do
|
||||
seq[i] = parts[i]
|
||||
end
|
||||
|
||||
-- generation loop
|
||||
while #seq < length do
|
||||
-- build current state key
|
||||
local state = table.concat({unpack(seq, #seq - order + 1, #seq)}, "|")
|
||||
|
||||
-- collect matching transitions
|
||||
local matches = {}
|
||||
for full,prob in pairs(model) do
|
||||
if full:sub(1, #state) == state and full:sub(#state+1, #state+1) == "|" then
|
||||
matches[#matches+1] = {key=full, prob=prob}
|
||||
end
|
||||
end
|
||||
|
||||
if #matches == 0 then break end
|
||||
|
||||
-- weighted pick
|
||||
local r = math.random()
|
||||
local sum = 0
|
||||
|
||||
local chosen
|
||||
for _,m in ipairs(matches) do
|
||||
sum = sum + m.prob
|
||||
if r <= sum then
|
||||
chosen = m.key
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not chosen then
|
||||
chosen = matches[#matches].key
|
||||
end
|
||||
|
||||
-- extract next symbol (after last '|')
|
||||
local next_symbol = chosen:match("|([^|]+)$")
|
||||
|
||||
seq[#seq+1] = next_symbol
|
||||
end
|
||||
|
||||
return seq
|
||||
end
|
||||
|
||||
-- todo: feed samples
|
||||
1
tools/musicator/sample_1.lua
Normal file
1
tools/musicator/sample_1.lua
Normal file
@@ -0,0 +1 @@
|
||||
-- todo
|
||||
Reference in New Issue
Block a user