diff --git a/.vscode/settings.json b/.vscode/settings.json index 76cf3f9..8f08f84 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,5 +30,5 @@ "files.associations": { "*.conf": "bitbake", "*.inc": "bitbake" - } -} \ No newline at end of file + }, +} diff --git a/inc/audio/audio.generator_model.lua b/inc/audio/audio.generator_model.lua new file mode 100644 index 0000000..11afe01 --- /dev/null +++ b/inc/audio/audio.generator_model.lua @@ -0,0 +1,642 @@ +{ + model = { + ["...|..."] = { + next = { + ["..."] = 0.71111111111111, + ["A-4"] = 0.0074074074074074, + ["B-4"] = 0.0037037037037037, + ["C-3"] = 0.011111111111111, + ["C-4"] = 0.037037037037037, + ["C-5"] = 0.11111111111111, + ["C-6"] = 0.0037037037037037, + ["D-4"] = 0.011111111111111, + ["D-5"] = 0.018518518518519, + ["E-4"] = 0.0074074074074074, + ["E-5"] = 0.025925925925926, + ["F-5"] = 0.0074074074074074, + ["G-3"] = 0.0037037037037037, + ["G-4"] = 0.022222222222222, + ["G-5"] = 0.018518518518519 + }, + total = 270 + }, + ["...|A-4"] = { + next = { + ["..."] = 0.9, + ["C-5"] = 0.1 + }, + total = 10 + }, + ["...|A-5"] = { + next = { + ["..."] = 1 + }, + total = 8 + }, + ["...|B-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["...|B-5"] = { + next = { + ["..."] = 1 + }, + total = 5 + }, + ["...|C-3"] = { + next = { + ["..."] = 0.66666666666667, + ["C-5"] = 0.33333333333333 + }, + total = 3 + }, + ["...|C-4"] = { + next = { + ["..."] = 0.875, + ["D-4"] = 0.083333333333333, + ["E-4"] = 0.041666666666667 + }, + total = 24 + }, + ["...|C-5"] = { + next = { + ["..."] = 0.73333333333333, + ["B-4"] = 0.033333333333333, + ["C-5"] = 0.066666666666667, + ["D-5"] = 0.13333333333333, + ["E-5"] = 0.033333333333333 + }, + total = 60 + }, + ["...|C-6"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["...|D-4"] = { + next = { + ["..."] = 0.92857142857143, + ["D-4"] = 0.071428571428571 + }, + total = 14 + }, + ["...|D-5"] = { + next = { + ["..."] = 0.80645161290323, + ["C-5"] = 0.032258064516129, + ["D-5"] = 0.032258064516129, + ["E-5"] = 0.12903225806452 + }, + total = 31 + }, + ["...|D-6"] = { + next = { + ["..."] = 1 + }, + total = 3 + }, + ["...|E-4"] = { + next = { + ["..."] = 1 + }, + total = 19 + }, + ["...|E-5"] = { + next = { + ["..."] = 0.77777777777778, + ["C-5"] = 0.022222222222222, + ["D-5"] = 0.13333333333333, + ["F-5"] = 0.066666666666667 + }, + total = 45 + }, + ["...|F-3"] = { + next = { + ["..."] = 1 + }, + total = 3 + }, + ["...|F-4"] = { + next = { + ["..."] = 0.8, + ["D-4"] = 0.1, + ["F-4"] = 0.1 + }, + total = 10 + }, + ["...|F-5"] = { + next = { + ["..."] = 0.66666666666667, + ["D-5"] = 0.066666666666667, + ["E-5"] = 0.066666666666667, + ["G-5"] = 0.2 + }, + total = 15 + }, + ["...|G-3"] = { + next = { + ["..."] = 0.8, + ["G-5"] = 0.2 + }, + total = 5 + }, + ["...|G-4"] = { + next = { + ["..."] = 0.95652173913043, + ["E-4"] = 0.043478260869565 + }, + total = 23 + }, + ["...|G-5"] = { + next = { + ["..."] = 0.875, + ["A-5"] = 0.0625, + ["E-5"] = 0.0625 + }, + total = 16 + }, + ["...|G-6"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["A-4|..."] = { + next = { + ["..."] = 0.55555555555556, + ["C-5"] = 0.33333333333333, + ["D-5"] = 0.11111111111111 + }, + total = 9 + }, + ["A-4|B-4"] = { + next = { + ["C-5"] = 1 + }, + total = 2 + }, + ["A-4|C-5"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["A-4|G-4"] = { + next = { + ["F-4"] = 1 + }, + total = 2 + }, + ["A-5|..."] = { + next = { + ["..."] = 0.2, + ["B-5"] = 0.1, + ["E-4"] = 0.1, + ["E-5"] = 0.4, + ["F-5"] = 0.1, + ["G-5"] = 0.1 + }, + total = 10 + }, + ["A-5|G-5"] = { + next = { + ["..."] = 0.33333333333333, + ["A-5"] = 0.66666666666667 + }, + total = 3 + }, + ["B-4|..."] = { + next = { + ["A-4"] = 1 + }, + total = 1 + }, + ["B-4|A-4"] = { + next = { + ["G-4"] = 1 + }, + total = 2 + }, + ["B-4|C-5"] = { + next = { + ["..."] = 1 + }, + total = 2 + }, + ["B-5|..."] = { + next = { + ["..."] = 0.2, + ["A-5"] = 0.2, + ["D-6"] = 0.4, + ["G-5"] = 0.2 + }, + total = 5 + }, + ["C-3|..."] = { + next = { + ["C-4"] = 1 + }, + total = 2 + }, + ["C-3|C-5"] = { + next = { + ["..."] = 0.33333333333333, + ["C-3"] = 0.66666666666667 + }, + total = 3 + }, + ["C-4|..."] = { + next = { + ["..."] = 0.5, + ["D-4"] = 0.125, + ["E-4"] = 0.041666666666667, + ["F-3"] = 0.041666666666667, + ["G-3"] = 0.16666666666667, + ["G-4"] = 0.083333333333333, + ["G-5"] = 0.041666666666667 + }, + total = 24 + }, + ["C-4|D-4"] = { + next = { + ["E-4"] = 1 + }, + total = 2 + }, + ["C-4|E-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["C-5|..."] = { + next = { + ["..."] = 0.40677966101695, + ["A-4"] = 0.067796610169492, + ["C-5"] = 0.050847457627119, + ["D-5"] = 0.20338983050847, + ["E-5"] = 0.15254237288136, + ["F-5"] = 0.016949152542373, + ["G-4"] = 0.10169491525424 + }, + total = 59 + }, + ["C-5|B-4"] = { + next = { + ["A-4"] = 1 + }, + total = 2 + }, + ["C-5|C-3"] = { + next = { + ["C-5"] = 1 + }, + total = 2 + }, + ["C-5|C-5"] = { + next = { + ["..."] = 0.8, + ["C-5"] = 0.2 + }, + total = 5 + }, + ["C-5|D-5"] = { + next = { + ["..."] = 0.3, + ["C-5"] = 0.2, + ["D-5"] = 0.1, + ["E-5"] = 0.4 + }, + total = 10 + }, + ["C-5|E-5"] = { + next = { + ["..."] = 0.33333333333333, + ["D-5"] = 0.33333333333333, + ["G-5"] = 0.33333333333333 + }, + total = 3 + }, + ["C-6|..."] = { + next = { + ["A-5"] = 1 + }, + total = 1 + }, + ["D-4|..."] = { + next = { + ["..."] = 0.26666666666667, + ["A-5"] = 0.066666666666667, + ["C-4"] = 0.2, + ["E-4"] = 0.4, + ["F-3"] = 0.066666666666667 + }, + total = 15 + }, + ["D-4|C-4"] = { + next = { + ["..."] = 1 + }, + total = 2 + }, + ["D-4|D-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["D-4|E-4"] = { + next = { + ["F-4"] = 1 + }, + total = 2 + }, + ["D-5|..."] = { + next = { + ["..."] = 0.2258064516129, + ["A-4"] = 0.032258064516129, + ["A-5"] = 0.032258064516129, + ["C-5"] = 0.2258064516129, + ["E-5"] = 0.29032258064516, + ["F-5"] = 0.096774193548387, + ["G-5"] = 0.096774193548387 + }, + total = 31 + }, + ["D-5|C-5"] = { + next = { + ["..."] = 0.77777777777778, + ["D-5"] = 0.22222222222222 + }, + total = 9 + }, + ["D-5|D-5"] = { + next = { + ["..."] = 0.5, + ["E-5"] = 0.5 + }, + total = 2 + }, + ["D-5|E-5"] = { + next = { + ["..."] = 0.33333333333333, + ["E-5"] = 0.11111111111111, + ["F-5"] = 0.55555555555556 + }, + total = 9 + }, + ["D-5|F-5"] = { + next = { + ["E-5"] = 1 + }, + total = 1 + }, + ["D-6|..."] = { + next = { + ["B-5"] = 0.66666666666667, + ["G-6"] = 0.33333333333333 + }, + total = 3 + }, + ["E-4|..."] = { + next = { + ["..."] = 0.19047619047619, + ["B-5"] = 0.047619047619048, + ["C-4"] = 0.14285714285714, + ["D-4"] = 0.23809523809524, + ["F-4"] = 0.19047619047619, + ["G-4"] = 0.19047619047619 + }, + total = 21 + }, + ["E-4|D-4"] = { + next = { + ["C-4"] = 1 + }, + total = 2 + }, + ["E-4|F-4"] = { + next = { + ["G-4"] = 1 + }, + total = 2 + }, + ["E-5|..."] = { + next = { + ["..."] = 0.18604651162791, + ["A-5"] = 0.046511627906977, + ["C-5"] = 0.27906976744186, + ["D-5"] = 0.2093023255814, + ["E-5"] = 0.069767441860465, + ["F-5"] = 0.093023255813953, + ["G-4"] = 0.023255813953488, + ["G-5"] = 0.093023255813953 + }, + total = 43 + }, + ["E-5|C-5"] = { + next = { + ["E-5"] = 1 + }, + total = 1 + }, + ["E-5|D-5"] = { + next = { + ["..."] = 0.125, + ["C-5"] = 0.75, + ["F-5"] = 0.125 + }, + total = 8 + }, + ["E-5|E-5"] = { + next = { + ["F-5"] = 1 + }, + total = 1 + }, + ["E-5|F-5"] = { + next = { + ["..."] = 0.5, + ["E-5"] = 0.4, + ["G-5"] = 0.1 + }, + total = 10 + }, + ["E-5|G-5"] = { + next = { + ["..."] = 0.5, + ["F-5"] = 0.5 + }, + total = 2 + }, + ["F-3|..."] = { + next = { + ["C-4"] = 1 + }, + total = 3 + }, + ["F-4|..."] = { + next = { + ["D-4"] = 0.11111111111111, + ["E-4"] = 0.44444444444444, + ["G-4"] = 0.44444444444444 + }, + total = 9 + }, + ["F-4|D-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["F-4|E-4"] = { + next = { + ["D-4"] = 1 + }, + total = 2 + }, + ["F-4|F-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["F-4|G-4"] = { + next = { + ["A-4"] = 1 + }, + total = 2 + }, + ["F-5|..."] = { + next = { + ["..."] = 0.2, + ["A-5"] = 0.066666666666667, + ["D-5"] = 0.2, + ["E-5"] = 0.46666666666667, + ["G-5"] = 0.066666666666667 + }, + total = 15 + }, + ["F-5|A-5"] = { + next = { + ["G-5"] = 1 + }, + total = 1 + }, + ["F-5|D-5"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["F-5|E-5"] = { + next = { + ["..."] = 0.5, + ["D-5"] = 0.16666666666667, + ["F-5"] = 0.16666666666667, + ["G-5"] = 0.16666666666667 + }, + total = 6 + }, + ["F-5|G-5"] = { + next = { + ["..."] = 0.75, + ["A-5"] = 0.25 + }, + total = 4 + }, + ["G-3|..."] = { + next = { + ["C-4"] = 0.5, + ["D-4"] = 0.25, + ["F-3"] = 0.25 + }, + total = 4 + }, + ["G-3|G-5"] = { + next = { + ["..."] = 0.5, + ["G-3"] = 0.5 + }, + total = 2 + }, + ["G-4|..."] = { + next = { + ["..."] = 0.090909090909091, + ["A-4"] = 0.090909090909091, + ["C-4"] = 0.045454545454545, + ["C-5"] = 0.18181818181818, + ["D-5"] = 0.045454545454545, + ["E-4"] = 0.22727272727273, + ["E-5"] = 0.045454545454545, + ["F-4"] = 0.27272727272727 + }, + total = 22 + }, + ["G-4|A-4"] = { + next = { + ["B-4"] = 1 + }, + total = 2 + }, + ["G-4|E-4"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["G-4|F-4"] = { + next = { + ["E-4"] = 1 + }, + total = 2 + }, + ["G-5|..."] = { + next = { + ["..."] = 0.35, + ["A-5"] = 0.05, + ["B-5"] = 0.05, + ["C-5"] = 0.05, + ["D-4"] = 0.05, + ["E-5"] = 0.25, + ["F-5"] = 0.2 + }, + total = 20 + }, + ["G-5|A-5"] = { + next = { + ["..."] = 0.5, + ["G-5"] = 0.5 + }, + total = 4 + }, + ["G-5|E-5"] = { + next = { + ["..."] = 1 + }, + total = 1 + }, + ["G-5|F-5"] = { + next = { + ["A-5"] = 1 + }, + total = 1 + }, + ["G-5|G-3"] = { + next = { + ["G-5"] = 1 + }, + total = 1 + }, + ["G-6|..."] = { + next = { + ["D-6"] = 1 + }, + total = 1 + } + }, + order = 2 +} diff --git a/inc/audio/audio.songs.lua b/inc/audio/audio.songs.lua index 322ef0a..67065f5 100644 --- a/inc/audio/audio.songs.lua +++ b/inc/audio/audio.songs.lua @@ -162,3 +162,43 @@ Songs.custom_song = { }, 130) } ]] + +function generate_sequence(model_data, length) + local order = model.order + local model_data = model_data.model + + -- random start key + local model_keys = {} + for k,_ in pairs(model) do + model_keys[#model_keys + 1] = k + end + local start_key = model_keys[math.ceil(math.random() * #model_keys)] + + -- sequence starts with the start key + local seq = unmake_key(start_key) + + -- generation loop + while #seq < length do + local current_key = table.concat({unpack(seq, #seq - order + 1, #seq)}, "|") + + local chosen = "..." + + local key_data = model[current_key] + if key_data then + local r = math.random() + local prob_sum = 0.0 + for new_note, new_prob in pairs(key_data.next) do + prob_sum = prob_sum + new_prob + if prob_sum < r then + chosen = new_note + end + end + end + +-- print(current_key .. " --> " .. chosen) + + seq[#seq+1] = chosen + end + + return seq +end diff --git a/tools/musicator/midi_converter.py b/tools/musicator/midi_converter.py new file mode 100644 index 0000000..c3e2a81 --- /dev/null +++ b/tools/musicator/midi_converter.py @@ -0,0 +1,46 @@ +from mido import MidiFile + +MIDI_FILE = "/tmp/teletype_impostor_musicator/maestro-v3.0.0/2018/MIDI-Unprocessed_Schubert7-9_MID--AUDIO_16_R2_2018_wav.midi" + +# resolution: rows per beat (e.g. 4 = 16th notes) +ROWS_PER_BEAT = 4 + +names = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"] + +def note_name(n): + octave = n // 12 - 1 + return f"{names[n % 12]}-{octave}" + +mid = MidiFile(MIDI_FILE) +tpb = mid.ticks_per_beat + +row_ticks = tpb // ROWS_PER_BEAT + +time = 0 +rows = {} + +for msg in mid: + time += msg.time + if msg.type == "note_on" and msg.velocity > 0: + row = int(time // row_ticks) + rows.setdefault(row, []).append(msg.note) + +# build monophonic sequence (highest note wins) +max_row = max(rows.keys()) +sequence = [] + +for r in range(max_row + 1): + if r in rows: + n = max(rows[r]) + sequence.append(note_name(n)) + else: + sequence.append("...") + +# trim (optional) +sequence = sequence[:512] + +# output as Lua +print("sequence = {") +for n in sequence: + print(f' "{n}",') +print("}") diff --git a/tools/musicator/musicator.lua b/tools/musicator/musicator.lua index 8da1ea4..d9927e4 100644 --- a/tools/musicator/musicator.lua +++ b/tools/musicator/musicator.lua @@ -1,130 +1,127 @@ -unpack = unpack or table.unpack +-- key separator: | +-- empty note: "..." -function build_markov_model(sequence, order) - local function make_key(tbl) - return table.concat(tbl, "|") +math.randomseed(os.time()) + +local unpack = unpack or table.unpack + +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 - local function unmake_key(k) - local result = {} - for t in string.gmatch(k, "[^|]+") do - result[#result + 1] = t + 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 function has_value (tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end end - return result - end + return false +end - local function add_key(str, value) - return str .. "|" .. value +-- 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 - local function split_last(full) - local i = full:match(".*()|") - return full:sub(1, i-1), full:sub(i+1) - end +function build_markov_model(sequence, order) + -- TODO: add {"..." x order} to beginning? - local counts = {} - local totals = {} + local model = { } -- 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 key = make_key({unpack(sequence, i, i + order - 1)}) + local next_note = sequence[i + order] - local notes_full = add_key(notes, sequence[i + order]) - counts[notes_full] = (counts[notes_full] or 0) + 1 + local data = model[key] or { next={}, total=0 } + + data.next[next_note] = (data.next[next_note] or 0) + 1 + data.total = data.total + 1 + + model[key] = data 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] + -- normalize + for temp_key,temp_data in pairs(model) do + for temp_note, temp_count in pairs(temp_data.next) do + model[temp_key].next[temp_note] = temp_count / temp_data.total + end end +--[[ + for k,v in pairs(model) do + print("-----" .. k) + for k2,v2 in pairs(v.next) do + print(k2, v2) + end + end +--]] + return { order = order, - model = model, - counts = counts -- keep raw counts (useful!) + model = model } end function generate_sequence(model_data, length) - local model = model_data.model - local order = model_data.order + local order = model.order + local model_data = model_data.model - -- 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 + -- random start key + local model_keys = {} for k,_ in pairs(model) do - start_key = k - break + model_keys[#model_keys + 1] = k end + local start_key = model_keys[math.ceil(math.random() * #model_keys)] - -- (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 + -- sequence starts with the start key + local seq = unmake_key(start_key) -- generation loop while #seq < length do - -- build current state key - local state = table.concat({unpack(seq, #seq - order + 1, #seq)}, "|") + local current_key = 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} + local chosen = "..." + + local key_data = model[current_key] + if key_data then + local r = math.random() + local prob_sum = 0.0 + for new_note, new_prob in pairs(key_data.next) do + prob_sum = prob_sum + new_prob + if prob_sum < r then + chosen = new_note + end end end - if #matches == 0 then break end +-- print(current_key .. " --> " .. chosen) - -- 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 + seq[#seq+1] = chosen end return seq end - --- todo: feed samples diff --git a/tools/musicator/sample_1.lua b/tools/musicator/sample_1.lua deleted file mode 100644 index b5b909e..0000000 --- a/tools/musicator/sample_1.lua +++ /dev/null @@ -1 +0,0 @@ --- todo diff --git a/tools/musicator/sequence_to_strudel.py b/tools/musicator/sequence_to_strudel.py new file mode 100644 index 0000000..d74dd11 --- /dev/null +++ b/tools/musicator/sequence_to_strudel.py @@ -0,0 +1,42 @@ +import sys +import re + +ROWS_PER_BEAT = 4 # keep consistent with your MIDI extraction +SOUND = "piano" + +def parse_sequence(text): + return re.findall(r'"([^"]+)"', text) + +def to_strudel_notes(seq): + out = [] + for n in seq: + if n == "..." or n == "---": + out.append("~") + else: + # C-5 → c5, C#5 → c#5 + note = n.replace("-", "") + out.append(note.lower()) + return out + +def chunk(seq, size): + for i in range(0, len(seq), size): + yield seq[i:i+size] + +# read from stdin +text = sys.stdin.read() + +sequence = parse_sequence(text) +notes = to_strudel_notes(sequence) + +# group into musical lines (4 beats) +lines = [] +for group in chunk(notes, ROWS_PER_BEAT * 4): + lines.append(" ".join(group)) + +pattern = "\n".join(lines) + +print("note(`") +print(pattern) +print(f"`).sound(\"{SOUND}\")") + +# npm install -g strudel-cli diff --git a/tools/musicator/teach.lua b/tools/musicator/teach.lua new file mode 100644 index 0000000..6e9af7e --- /dev/null +++ b/tools/musicator/teach.lua @@ -0,0 +1,414 @@ +-- teach the musicator +-- uses samples from: https://magenta.tensorflow.org/datasets/maestro#v300 + +--require("luarocks.loader") +--require("luamidi") + +require("./musicator") +local inspect = require("inspect") + +math.randomseed(os.time()) + +function flatten(v) + local res = {} + local function flatten(v) + if type(v) ~= "table" then + table.insert(res, v) + return + end + for _, v in ipairs(v) do + flatten(v) + end + end + flatten(v) + return res +end + + +local training_data = { + +-- simple ascending phrase +{ + "C-4","...","D-4","...","E-4","...","G-4","...", + "E-4","...","D-4","...","C-4","...","...","..." +}, + +-- descending answer +{ + "G-4","...","F-4","...","E-4","...","D-4","...", + "C-4","...","D-4","...","E-4","...","...","..." +}, + +-- arpeggio major +{ + "C-4","...","E-4","...","G-4","...","C-5","...", + "G-4","...","E-4","...","C-4","...","...","..." +}, + +-- arpeggio minor +{ + "A-4","...","C-5","...","E-5","...","A-5","...", + "E-5","...","C-5","...","A-4","...","...","..." +}, + +-- stepwise melody (folk-like) +{ + "D-4","...","E-4","...","F-4","...","G-4","...", + "F-4","...","E-4","...","D-4","...","...","..." +}, + +-- repeated note rhythm +{ + "C-5","C-5","...","C-5","...","C-5","C-5","...", + "D-5","...","E-5","...","...","...","...","..." +}, + +-- bounce pattern +{ + "C-5","...","G-4","...","C-5","...","G-4","...", + "D-5","...","A-4","...","D-5","...","...","..." +}, + +-- scale run up +{ + "C-4","D-4","E-4","F-4","G-4","A-4","B-4","C-5", + "...","...","...","...","...","...","...","..." +}, + +-- scale run down +{ + "C-5","B-4","A-4","G-4","F-4","E-4","D-4","C-4", + "...","...","...","...","...","...","...","..." +}, + +-- syncopated feel +{ + "C-5","...","...","D-5","...","...","E-5","...", + "C-5","...","...","G-4","...","...","...","..." +}, + +-- triplet-ish feel (simulated) +{ + "E-5","D-5","C-5","...","E-5","D-5","C-5","...", + "G-4","...","...","...","...","...","...","..." +}, + +-- small jumps +{ + "C-5","...","E-5","...","D-5","...","F-5","...", + "E-5","...","C-5","...","...","...","...","..." +}, + +-- call +{ + "G-4","...","A-4","...","C-5","...","A-4","...", + "...","...","...","...","...","...","...","..." +}, + +-- response +{ + "E-4","...","F-4","...","G-4","...","F-4","...", + "D-4","...","...","...","...","...","...","..." +}, + +-- denser pattern (DDR-like) +{ + "C-5","D-5","E-5","...","D-5","E-5","F-5","...", + "E-5","D-5","C-5","...","...","...","...","..." +}, + +-- alternating pattern (good for gameplay) +{ + "C-5","...","E-5","...","C-5","...","E-5","...", + "D-5","...","F-5","...","D-5","...","...","..." +}, + +-- higher register variant +{ + "G-5","...","A-5","...","B-5","...","D-6","...", + "B-5","...","A-5","...","G-5","...","...","..." +}, + +-- low register grounding +{ + "C-4","...","G-3","...","C-4","...","G-3","...", + "F-3","...","C-4","...","...","...","...","..." +}, + +-- variant of ascending with offset +{ + "...","C-4","...","D-4","...","E-4","...","G-4", + "...","E-4","...","D-4","...","C-4","...","..." +}, + +-- staggered rhythm +{ + "C-4","...","...","D-4","...","E-4","...","...", + "G-4","...","E-4","...","D-4","...","...","..." +}, + +-- broken arpeggio (different spacing) +{ + "C-4","E-4","...","G-4","...","C-5","...", + "G-4","E-4","...","C-4","...","...","...","..." +}, + +-- minor variation (shifted) +{ + "...","A-4","C-5","...","E-5","...","A-5","...", + "E-5","...","C-5","...","A-4","...","...","..." +}, + +-- repeated + variation +{ + "D-4","D-4","...","E-4","...","F-4","F-4","...", + "G-4","...","F-4","...","E-4","...","...","..." +}, + +-- zig-zag motion +{ + "C-5","...","E-5","...","D-5","...","F-5","...", + "E-5","...","G-5","...","F-5","...","...","..." +}, + +-- alternating step/jump +{ + "C-5","...","D-5","...","G-5","...","F-5","...", + "E-5","...","C-5","...","D-5","...","...","..." +}, + +-- denser burst pattern +{ + "C-5","D-5","E-5","F-5","...","E-5","D-5","C-5", + "...","...","...","...","...","...","...","..." +}, + +-- rolling pattern +{ + "E-5","...","D-5","...","C-5","...","D-5","...", + "E-5","...","G-5","...","E-5","...","...","..." +}, + +-- syncopation variant +{ + "...","C-5","...","...","E-5","...","...","G-5", + "...","E-5","...","C-5","...","...","...","..." +}, + +-- low-high interplay +{ + "C-4","...","G-4","...","C-5","...","G-4","...", + "E-4","...","C-4","...","...","...","...","..." +}, + +-- descending but staggered +{ + "C-5","...","...","B-4","...","A-4","...","...", + "G-4","...","F-4","...","E-4","...","...","..." +}, + +-- small trill-like feel +{ + "E-5","F-5","E-5","...","E-5","F-5","E-5","...", + "D-5","...","C-5","...","...","...","...","..." +}, + +-- call variant (shifted timing) +{ + "...","G-4","...","A-4","...","C-5","...","A-4", + "...","...","...","...","...","...","...","..." +}, + +-- response variant +{ + "...","E-4","...","F-4","...","G-4","...","F-4", + "D-4","...","...","...","...","...","...","..." +}, + +-- dense DDR-ish alternating +{ + "C-5","...","D-5","...","C-5","...","D-5","...", + "E-5","...","F-5","...","E-5","...","...","..." +}, + +-- higher variation arpeggio +{ + "G-5","...","B-5","...","D-6","...","G-6","...", + "D-6","...","B-5","...","G-5","...","...","..." +}, + +-- low groove pattern +{ + "C-3","...","C-4","...","G-3","...","C-4","...", + "F-3","...","C-4","...","...","...","...","..." +}, + +-- slightly chaotic (good for branching) +{ + "C-5","...","E-5","D-5","...","G-5","...","F-5", + "...","D-5","...","C-5","...","...","...","..." +}, + +-- mixed density +{ + "C-5","D-5","...","E-5","...","F-5","G-5","...", + "E-5","...","D-5","C-5","...","...","...","..." +}, + +-- offset staircase up +{ + "...","C-4","D-4","E-4","F-4","G-4","A-4","B-4", + "C-5","...","...","...","...","...","...","..." +}, + +-- offset staircase down +{ + "...","C-5","B-4","A-4","G-4","F-4","E-4","D-4", + "C-4","...","...","...","...","...","...","..." +}, + +-- dense zigzag +{ + "C-5","E-5","D-5","F-5","E-5","G-5","F-5","A-5", + "G-5","...","...","...","...","...","...","..." +}, + +-- jack pattern (DDR classic) +{ + "C-5","C-5","C-5","...","C-5","C-5","...","...", + "D-5","D-5","...","...","...","...","...","..." +}, + +-- alternating two-note burst +{ + "C-5","D-5","C-5","D-5","C-5","D-5","...","...", + "E-5","F-5","E-5","F-5","...","...","...","..." +}, + +-- wide jumps +{ + "C-4","...","G-5","...","D-4","...","A-5","...", + "E-4","...","B-5","...","...","...","...","..." +}, + +-- rolling triplet-ish +{ + "C-5","E-5","G-5","...","E-5","C-5","E-5","...", + "G-5","...","...","...","...","...","...","..." +}, + +-- syncopated dense +{ + "...","C-5","D-5","...","E-5","...","F-5","G-5", + "...","E-5","...","C-5","...","...","...","..." +}, + +-- mirrored pattern +{ + "C-5","D-5","E-5","F-5","E-5","D-5","C-5","...", + "...","...","...","...","...","...","...","..." +}, + +-- chord-outline arpeggio feel +{ + "C-4","...","G-4","...","E-5","...","G-4","...", + "C-4","...","...","...","...","...","...","..." +}, + +-- broken rhythm variant +{ + "C-5","...","D-5","E-5","...","F-5","...","G-5", + "E-5","...","D-5","...","C-5","...","...","..." +}, + +-- fast burst then rest +{ + "C-5","D-5","E-5","F-5","G-5","A-5","...","...", + "...","...","...","...","...","...","...","..." +}, + +-- low-high bounce fast +{ + "C-3","C-5","C-3","C-5","C-3","C-5","...","...", + "G-3","G-5","G-3","G-5","...","...","...","..." +}, + +-- repeated with shift +{ + "...","E-5","...","E-5","...","E-5","...","...", + "D-5","...","C-5","...","...","...","...","..." +}, + +-- clustered mid +{ + "D-5","E-5","F-5","...","E-5","D-5","C-5","...", + "D-5","...","...","...","...","...","...","..." +}, + +-- broken descending +{ + "C-6","...","A-5","...","F-5","...","D-5","...", + "C-5","...","...","...","...","...","...","..." +}, + +-- chaotic jumpy (important for branching) +{ + "C-5","...","F-5","D-5","...","A-5","...","E-5", + "...","G-5","...","C-5","...","...","...","..." +}, + +-- double-step pattern +{ + "C-5","D-5","D-5","E-5","E-5","F-5","...","...", + "G-5","...","...","...","...","...","...","..." +}, + +-- uneven spacing +{ + "C-5","...","...","D-5","E-5","...","...","F-5", + "G-5","...","...","...","...","...","...","..." +}, + +-- fast alternating high +{ + "G-5","A-5","G-5","A-5","G-5","A-5","...","...", + "F-5","E-5","...","...","...","...","...","..." +}, + +-- low groove with variation +{ + "C-3","...","C-4","...","G-3","...","D-4","...", + "F-3","...","C-4","...","...","...","...","..." +}, + +-- semi-random filler (very useful) +{ + "C-5","...","E-5","...","D-5","...","G-5","...", + "F-5","...","A-5","...","E-5","...","...","..." +}, + +-- near-repetition (state collision booster) +{ + "C-5","...","D-5","...","E-5","...","C-5","...", + "D-5","...","E-5","...","C-5","...","...","..." +}, + +-- same but shifted (VERY important) +{ + "...","C-5","...","D-5","...","E-5","...","C-5", + "...","D-5","...","E-5","...","C-5","...","..." +}, + +} + +local model = build_markov_model(flatten(training_data), 2) + +print(inspect(model)) + +--[[ +local generated = generate_sequence(model, 100) + +for i,v in ipairs(generated) do + print(v) +end +--]]