Last active
January 5, 2019 21:53
-
-
Save ypxk/c7261783a06bf6b56cc064ef53c017c1 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
-- LA Drivers | |
-- thanks to mark | |
--enc 1: move key center by 5th | |
--enc 2: move key center by 4th | |
--key 2: walk through current scale | |
--enc 3: add random/remove last | |
--key 2: parallel modulation | |
--key 3: random scale | |
MusicUtil = require "mark_eats/musicutil" | |
local UI = require "mark_eats/ui" | |
local BeatClock = require "beatclock" | |
local MollyThePoly = require "mark_eats/mollythepoly" | |
engine.name = "MollyThePoly" | |
local options = {} | |
options.OUTPUT = {"Audio", "MIDI", "Audio + MIDI"} | |
options.STEP_LENGTH_NAMES = {"1 bar", "1/2", "1/3", "1/4", "1/6", "1/8", "1/12", "1/16", "1/24", "1/32", "1/48", "1/64"} | |
options.STEP_LENGTH_DIVIDERS = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64} | |
local DATA_FILE_PATH = data_dir .. "mark_eats/loom.data" | |
local SCREEN_FRAMERATE = 15 | |
local screen_dirty = true | |
local GRID_FRAMERATE = 30 | |
local grid_dirty = true | |
local grid_leds = {} | |
local grid_w, grid_h = 16, 8 | |
local beat_clock | |
local grid_device | |
local midi_in_device | |
local midi_out_device | |
local midi_out_channel | |
local notes = {} | |
local triggers = {} | |
local custom_scale = false | |
local scale_edit_id = 1 | |
local scale_notes = {} | |
local SCALES_LEN = #MusicUtil.SCALES | |
local active_notes = {} | |
local down_marks = {} | |
local down_keys = {} | |
local trails = {} | |
local remove_animations = {} | |
local DOWN_ANI_LENGTH = 0.2 | |
local REMOVE_ANI_LENGTH = 0.4 | |
local TRAIL_ANI_LENGTH = 6.0 | |
local pages | |
local tabs | |
local playback_icon | |
local add_remove_animations = {} | |
local ADD_REMOVE_ANI_LENGTH = 0.2 | |
local notes_changed_timeout = 0 | |
local triggers_changed_timeout = 0 | |
local NOTES_TRIGGERS_TIMEOUT_LENGTH = 0.2 | |
local nextScale | |
rootNote = math.random(12) | |
gridScale = {} | |
minorTonality = true | |
tonality = 'Natural Minor' | |
octave = 4 | |
timeLast = util.time() | |
transposeAmount = 0 | |
function tonality_status () | |
if minor_tonality then | |
tonality = 'Natural Minor' | |
elseif not minor_tonality then | |
tonality = 'Major' | |
end | |
return tonality | |
end | |
function get_incoming_scale(s, t) --semitones to transpose, tonality, | |
local transposeBy = s | |
local scaleType = t | |
local incomingScale = {} | |
rootNote = (rootNote + transposeBy) % 12 | |
incomingScale = MusicUtil.generate_scale_of_length(rootNote, scaleType, 128) | |
return incomingScale | |
end | |
function update_grid_scale(s) --incoming scale | |
local incomingScale = s | |
local notesToKeep = {} | |
local incomingGridScale = {} | |
--get tone map of new pitches | |
for k,v in pairs(masterScale) do | |
local randomDir = math.random(2) | |
for n_k, n_v in pairs(incomingScale) do | |
if n_v == v then | |
notesToKeep[k] = v | |
--print('same', notesToKeep[k]) | |
break | |
elseif n_v ~= v then | |
if randomDir > 1 then | |
if n_v - 1 == v then | |
notesToKeep[k] = v + 1 | |
-- print('diff1', notesToKeep[k]) | |
break | |
end | |
else | |
if n_v + 1 == v then | |
notesToKeep[k] = v - 1 | |
-- print('diff2', notesToKeep[k]) | |
break | |
end | |
end | |
end | |
end | |
end | |
--map scale to grid swap with place scale function | |
for k,v in pairs(notesToKeep) do | |
--print(k,v) | |
if #incomingGridScale < 16 then | |
if v >= gridScale[1] then | |
incomingGridScale[#incomingGridScale + 1] = v | |
end | |
end | |
end | |
incomingGridScaledeDupe = duplicateCheck(incomingGridScale, incomingScale) | |
-- | |
masterScale = incomingScale | |
return incomingGridScaledeDupe, incomingScale | |
end | |
function duplicateCheck(s,i_s) --scale, incoming scale | |
local incomingGridScale = s | |
local incomingScale = i_s | |
local attemptCounter = 1 | |
for k,v in pairs(incomingGridScale) do | |
if incomingGridScale[k] ==incomingGridScale[k+1] then | |
duplicate = true | |
--print(k+1, incomingGridScale[k+1], incomingGridScale[k+2]) | |
for a,b in pairs(incomingScale) do | |
if incomingGridScale[k+1] == incomingScale[a] then | |
incomingGridScale[k+1] = incomingScale[a+1] | |
break | |
end | |
end | |
end | |
end | |
--print('--------------') | |
return incomingGridScale | |
end | |
function place_scale_on_grid(s) --scale | |
local scaleBuilder = {} | |
for k,v in pairs(s) do | |
if #scaleBuilder < 16 then | |
if v >= (12*octave) then | |
scaleBuilder[#scaleBuilder + 1] = v | |
end | |
end | |
end | |
--for k,v in pairs(scaleBuilder) do print(k,v) end | |
return scaleBuilder | |
end | |
function pivot_scale(s, p) --scale, pivot amount | |
local toPiv = s | |
local pivedScale = {} | |
local pivAmount = p | |
for k,v in pairs(toPiv) do | |
for n_k, n_v in pairs(masterScale) do | |
if v == n_v then | |
if masterScale[n_k+p] then | |
pivedScale[k] = masterScale[n_k+p] | |
break | |
else | |
return toPiv | |
end | |
end | |
end | |
end | |
return pivedScale | |
end | |
function enc(n, delta) | |
local newScale | |
if n == 1 then | |
if delta > 0 then | |
transposeAmount = 7 | |
elseif delta < 0 then | |
transposeAmount = -7 | |
end | |
if util.time() - timeLast > .1 then | |
newScale, MasterScale = update_grid_scale(get_incoming_scale(transposeAmount, tonality)) | |
gridScale = place_scale_on_grid(newScale) | |
--print(MusicUtil.note_num_to_name(masterScale[1], true)) | |
end | |
elseif n == 2 then | |
if not shiftA then | |
if delta > 0 then | |
transposeAmount = 5 | |
elseif delta < 0 then | |
transposeAmount = -5 | |
end | |
if util.time() - timeLast > .1 then | |
newScale = update_grid_scale(get_incoming_scale(transposeAmount, tonality)) | |
gridScale = place_scale_on_grid(newScale) | |
end | |
elseif shiftA then | |
if delta > 0 then | |
gridScale = pivot_scale(gridScale, 1) | |
elseif delta < 0 then | |
gridScale = pivot_scale(gridScale, -1) | |
end | |
end | |
elseif n == 3 then | |
if delta > 1 then | |
add_random() | |
elseif delta < 1 then | |
remove_last() | |
end | |
end | |
timeLast = util.time() | |
redraw() | |
end | |
function key(n, z) | |
if z == 1 then | |
if n == 2 then | |
shiftA = true | |
aTime = util.time() | |
elseif n == 3 then | |
transposeAmount = math.random(12) - 6 | |
newScale = update_grid_scale(get_incoming_scale(transposeAmount, tonality)) | |
gridScale = place_scale_on_grid(newScale) | |
end | |
elseif z == 0 then | |
if n == 2 then | |
shiftA = false | |
if util.time() - aTime < .5 then | |
if tonality == 'Natural Minor' then | |
tonality = 'Major' | |
else | |
tonality = 'Natural Minor' | |
end | |
transposeAmount = 0 | |
newScale = update_grid_scale(get_incoming_scale(transposeAmount, tonality)) | |
gridScale = place_scale_on_grid(newScale) | |
end | |
end | |
end | |
end | |
--print(circle_position, key_name) | |
----------------- | |
--[[ ]] | |
local function note_on(note_num) | |
local min_vel, max_vel = params:get("min_velocity"), params:get("max_velocity") | |
if min_vel > max_vel then | |
max_vel = min_vel | |
end | |
local note_midi_vel = math.random(min_vel, max_vel) | |
-- print("note_on", note_num, note_midi_vel) | |
-- Audio engine out | |
if params:get("output") == 1 or params:get("output") == 3 then | |
engine.noteOn(note_num, MusicUtil.note_num_to_freq(note_num), note_midi_vel / 127) | |
end | |
-- MIDI out | |
if (params:get("output") == 2 or params:get("output") == 3) then | |
midi_out_device.note_on(note_num, note_midi_vel, midi_out_channel) | |
end | |
end | |
local function note_off(note_num) | |
-- print("note_off", note_num) | |
-- Audio engine out | |
if params:get("output") == 1 or params:get("output") == 3 then | |
engine.noteOff(note_num) | |
end | |
-- MIDI out | |
if (params:get("output") == 2 or params:get("output") == 3) then | |
midi_out_device.note_off(note_num, nil, midi_out_channel) | |
end | |
end | |
local function all_notes_off() | |
-- Audio engine out | |
engine.noteKillAll() | |
-- MIDI out | |
if (params:get("output") == 2 or params:get("output") == 3) then | |
for _, a in pairs(active_notes) do | |
midi_out_device.note_off(a, 96, midi_out_channel) | |
end | |
end | |
active_notes = {} | |
end | |
local function add_note(position, head, length, direction, show_screen_animation) | |
length = length or 1 | |
direction = direction or 1 | |
local note = {position = position, head = head, length = length, advance_countdown = length, direction = direction, active = false} | |
table.insert(notes, note) | |
end | |
local function add_trigger(position, head, length, direction, show_screen_animation) | |
length = length or 1 | |
direction = direction or 1 | |
local trigger = {position = position, head = head, length = length, advance_countdown = length, direction = direction, active = false} | |
table.insert(triggers, trigger) | |
end | |
local function remove_note(position, show_screen_animation, silent) | |
local note | |
if position then | |
for k, v in pairs(notes) do | |
if v.position == position then | |
note = table.remove(notes, k) | |
break | |
end | |
end | |
else | |
note = table.remove(notes) | |
end | |
if note and not silent then | |
if show_screen_animation then | |
else | |
notes_changed_timeout = NOTES_TRIGGERS_TIMEOUT_LENGTH | |
end | |
grid_dirty = true | |
end | |
end | |
local function remove_trigger(position, show_screen_animation, silent) | |
local trigger | |
if position then | |
for k, v in pairs(triggers) do | |
if v.position == position then | |
trigger = table.remove(triggers, k) | |
break | |
end | |
end | |
else | |
trigger = table.remove(triggers) | |
end | |
if trigger and not silent then | |
if show_screen_animation then | |
else | |
triggers_changed_timeout = NOTES_TRIGGERS_TIMEOUT_LENGTH | |
end | |
grid_dirty = true | |
end | |
end | |
function add_random() | |
if #notes >= grid_w and #triggers >= grid_h then return end | |
-- Note | |
if math.random() >= 0.5 then | |
local available_positions = {} | |
for i = 1, grid_w do | |
local available = true | |
for _, vn in pairs(notes) do | |
if vn.position == i then | |
available = false | |
break | |
end | |
end | |
if available then | |
table.insert(available_positions, i) | |
end | |
end | |
if #available_positions > 0 then | |
local length = util.round(math.pow(math.random(), 4) * (grid_h - 2) + 1) | |
add_note(available_positions[math.random(#available_positions)], math.random(grid_h), length, (math.random() >= 0.5 and 1 or -1), true) | |
end | |
-- Trigger | |
else | |
local available_positions = {} | |
for i = 1, grid_h do | |
local available = true | |
for _, vt in pairs(triggers) do | |
if vt.position == i then | |
available = false | |
break | |
end | |
end | |
if available then | |
table.insert(available_positions, i) | |
end | |
end | |
if #available_positions > 0 then | |
local length = util.round(math.pow(math.random(), 4) * (grid_w - 2) + 1) | |
add_trigger(available_positions[math.random(#available_positions)], math.random(grid_w), length, (math.random() >= 0.5 and 1 or -1), true) | |
end | |
end | |
end | |
function remove_last() | |
if #notes == 0 and #triggers == 0 then return end | |
if math.random() >= 0.5 then | |
remove_note(nil, true) | |
else | |
remove_trigger(nil, true) | |
end | |
end | |
local function advance_step() | |
if grid_device then | |
grid_w = grid_device.cols() | |
grid_h = grid_device.rows() | |
if grid_w ~= 8 and grid_w ~= 16 then grid_w = 16 end | |
if grid_h ~= 8 and grid_h ~= 16 then grid_h = 8 end | |
end | |
for _, n in pairs(notes) do | |
n.advance_countdown = n.advance_countdown - 1 | |
if n.advance_countdown == 0 then | |
n.advance_countdown = n.length | |
if n.direction > 0 then n.head = n.head % params:get("pattern_height") + 1 | |
else n.head = (n.head + params:get("pattern_height") - 2) % params:get("pattern_height") + 1 end | |
end | |
n.active = false | |
end | |
local active_notes_this_step = {} | |
for _, t in pairs(triggers) do | |
-- Progress | |
t.advance_countdown = t.advance_countdown - 1 | |
if t.advance_countdown == 0 then | |
t.advance_countdown = t.length | |
if t.direction > 0 then t.head = t.head % params:get("pattern_width") + 1 | |
else t.head = (t.head + params:get("pattern_width") - 2) % params:get("pattern_width") + 1 end | |
end | |
t.active = false | |
-- Check for intersections and generate trails | |
local tx | |
for ti = 0, t.length - 1 do | |
tx = t.head + (ti * t.direction * -1) | |
tx = (tx - 1) % params:get("pattern_width") + 1 | |
if tx <= grid_w then trails[tx][t.position] = TRAIL_ANI_LENGTH end | |
for _, n in pairs(notes) do | |
local ny | |
for ni = 0, n.length - 1 do | |
ny = n.head + (ni * n.direction * -1) | |
ny = (ny - 1) % params:get("pattern_height") + 1 | |
if ny <= grid_h then trails[n.position][ny] = TRAIL_ANI_LENGTH end | |
if tx == n.position and t.position == ny then | |
if not n.active then | |
table.insert(active_notes_this_step, gridScale[n.position]) | |
end | |
n.active = true | |
t.active = true | |
break | |
end | |
end | |
end | |
end | |
end | |
-- Generate trails for notes if need be | |
if #triggers == 0 then | |
for _, n in pairs(notes) do | |
local ny | |
for ni = 0, n.length - 1 do | |
ny = n.head + (ni * n.direction * -1) | |
ny = (ny - 1) % params:get("pattern_height") + 1 | |
if ny <= grid_h then trails[n.position][ny] = TRAIL_ANI_LENGTH end | |
end | |
end | |
end | |
-- Work out which need noteOffs | |
for i = #active_notes, 1, -1 do | |
local still_active = false | |
for sk, sa in pairs(active_notes_this_step) do | |
if sa == active_notes[i] then | |
still_active = true | |
table.remove(active_notes_this_step, sk) | |
break | |
end | |
end | |
if not still_active then | |
note_off(active_notes[i]) | |
table.remove(active_notes, i) | |
end | |
end | |
-- Add remaining, the new notes | |
for _, sa in pairs(active_notes_this_step) do | |
if #active_notes < params:get("max_active_notes") then | |
note_on(sa) | |
table.insert(active_notes, sa) | |
end | |
end | |
screen_dirty = true | |
grid_dirty = true | |
end | |
local function reset_step() | |
beat_clock:reset() | |
end | |
local function grid_update() | |
if #down_marks > 0 or #remove_animations > 0 then grid_dirty = true end | |
local time_increment = 1 / GRID_FRAMERATE | |
-- Trails | |
for x = 1, grid_w do | |
for y = 1, grid_h do | |
trails[x][y] = util.clamp(trails[x][y] - time_increment, 0, TRAIL_ANI_LENGTH) | |
if trails[x][y] > 0 then grid_dirty = true end | |
end | |
end | |
-- Down marks | |
for i = #down_marks, 1, -1 do | |
if not down_marks[i].active then | |
down_marks[i].time_remaining = down_marks[i].time_remaining - time_increment | |
if down_marks[i].time_remaining <= 0 then | |
table.remove(down_marks, i) | |
end | |
end | |
end | |
-- Remove animations | |
for i = #remove_animations, 1, -1 do | |
remove_animations[i].time_remaining = remove_animations[i].time_remaining - time_increment | |
if remove_animations[i].time_remaining <= 0 then | |
table.remove(remove_animations, i) | |
end | |
end | |
end | |
function grid_event(x, y, z) | |
if z == 1 then | |
-- Is there a relevant down mark? | |
local relevant_down_mark = nil | |
for k, v in pairs(down_marks) do | |
-- Re-activate fading down mark | |
if v.x == x and v.y == y then | |
v.active = true | |
v.time_remaining = DOWN_ANI_LENGTH | |
relevant_down_mark = v | |
break | |
-- Note | |
elseif v.x == x and v.active then | |
local three_keys = false | |
for _, kv in pairs(down_keys) do | |
if kv.x == x then | |
three_keys = true | |
break | |
end | |
end | |
if three_keys then | |
remove_note(x, true, false) | |
else | |
remove_note(x, false, true) | |
add_note(x, v.y, math.abs(v.y - y), (y < v.y and 1 or -1), false) | |
end | |
relevant_down_mark = v | |
-- Trigger | |
elseif v.y == y and v.active then | |
local three_keys = false | |
for _, kv in pairs(down_keys) do | |
if kv.y == y then | |
three_keys = true | |
break | |
end | |
end | |
if three_keys then | |
remove_trigger(y, true, false) | |
else | |
remove_trigger(y, false, true) | |
add_trigger(y, v.x, math.abs(v.x - x), (x < v.x and 1 or -1), false) | |
end | |
relevant_down_mark = v | |
end | |
end | |
-- Make it the down mark | |
if relevant_down_mark then | |
if relevant_down_mark.x ~= x or relevant_down_mark.y ~= y then | |
table.insert(down_keys, {x = x, y = y}) | |
end | |
else | |
table.insert(down_marks, {active = true, x = x, y = y, time_remaining = DOWN_ANI_LENGTH}) | |
end | |
else | |
for _, v in pairs(down_marks) do | |
if v.x == x and v.y == y then | |
v.active = false | |
break | |
end | |
end | |
for k, v in pairs(down_keys) do | |
if v.x == x and v.y == y then | |
table.remove(down_keys, k) | |
break | |
end | |
end | |
end | |
grid_dirty = true | |
end | |
function init() | |
--me | |
masterScale = MusicUtil.generate_scale_of_length(rootNote, tonality, 128) | |
gridScale = place_scale_on_grid(masterScale) | |
--og | |
for x = 1, 16 do | |
grid_leds[x] = {} | |
trails[x] = {} | |
for y = 1, 16 do | |
trails[x][y] = 0 | |
end | |
end | |
grid_device = grid.connect(1) | |
grid_device.event = grid_event | |
beat_clock = BeatClock.new() | |
beat_clock.on_step = advance_step | |
beat_clock.on_select_internal = function() | |
beat_clock:start() | |
screen_dirty = true | |
end | |
beat_clock.on_select_external = function() | |
reset_step() | |
screen_dirty = true | |
end | |
midi_in_device = midi.connect(1) | |
midi_in_device.event = function(data) | |
beat_clock:process_midi(data) | |
if not beat_clock.playing and playback_icon.status == 1 then | |
screen_dirty = true | |
end | |
end | |
midi_out_device = midi.connect(1) | |
midi_out_device.event = function() end | |
local screen_refresh_metro = metro.alloc() | |
screen_refresh_metro.callback = function() | |
if screen_dirty then | |
screen_dirty = false | |
redraw() | |
end | |
end | |
local grid_redraw_metro = metro.alloc() | |
grid_redraw_metro.callback = function() | |
grid_update() | |
if grid_dirty and grid_device.attached() then | |
grid_dirty = false | |
grid_redraw() | |
end | |
end | |
-- Add params | |
params:add{type = "number", id = "grid_device", name = "Grid Device", min = 1, max = 4, default = 1, action = function(value) | |
grid_device.all(0) | |
grid_device.refresh() | |
grid_device:reconnect(value) | |
end} | |
params:add{type = "option", id = "output", name = "Output", options = options.OUTPUT, action = all_notes_off} | |
params:add{type = "number", id = "midi_out_device", name = "MIDI Out Device", min = 1, max = 4, default = 1, action = function(value) | |
midi_out_device:reconnect(value) | |
end} | |
params:add{type = "number", id = "midi_out_channel", name = "MIDI Out Channel", min = 1, max = 16, default = 1, action = function(value) | |
all_notes_off() | |
midi_out_channel = value | |
end} | |
params:add{type = "number", id = "max_active_notes", name = "Max Active Notes", min = 1, max = 16, default = 16} | |
params:add{type = "option", id = "clock", name = "Clock", options = {"Internal", "External"}, default = beat_clock.external or 2 and 1, action = function(value) | |
beat_clock:clock_source_change(value) | |
end} | |
params:add{type = "number", id = "clock_midi_in_device", name = "Clock MIDI In Device", min = 1, max = 4, default = 1, action = function(value) | |
midi_in_device:reconnect(value) | |
end} | |
params:add{type = "option", id = "clock_out", name = "Clock Out", options = {"Off", "On"}, default = beat_clock.send or 2 and 1, action = function(value) | |
if value == 1 then beat_clock.send = false | |
else beat_clock.send = true end | |
end} | |
params:add_separator() | |
params:add{type = "number", id = "bpm", name = "BPM", min = 1, max = 480, default = 140, action = function(value) | |
beat_clock:bpm_change(value) | |
screen_dirty = true | |
end} | |
params:add{type = "option", id = "step_length", name = "Step Length", options = options.STEP_LENGTH_NAMES, default = 8, action = function(value) | |
beat_clock.steps_per_beat = options.STEP_LENGTH_DIVIDERS[value] / 4 | |
beat_clock:bpm_change(beat_clock.bpm) | |
end} | |
params:add{type = "number", id = "pattern_width", name = "Pattern Width", min = 8, max = 64, default = 16} | |
params:add{type = "number", id = "pattern_height", name = "Pattern Height", min = 8, max = 64, default = 8} | |
params:add{type = "number", id = "min_velocity", name = "Min Velocity", min = 1, max = 127, default = 80} | |
params:add{type = "number", id = "max_velocity", name = "Max Velocity", min = 1, max = 127, default = 100} | |
params:add_separator() | |
midi_out_channel = params:get("midi_out_channel") | |
-- Engine params | |
MollyThePoly.add_params() | |
-- UI | |
screen.aa(1) | |
screen_refresh_metro:start(1 / SCREEN_FRAMERATE) | |
grid_redraw_metro:start(1 / GRID_FRAMERATE) | |
beat_clock:start() | |
end | |
function grid_redraw() | |
local DOWN_BRIGHTNESS = 1 | |
local TRAIL_BRIGHTNESS = 1 | |
local OUTSIDE_BRIGHTNESS = 1 | |
local INACTIVE_BRIGHTNESS = 3 | |
local ACTIVE_BRIGHTNESS = 12 | |
local brightness | |
-- Draw trails | |
for x = 1, 16 do | |
for y = 1, 16 do | |
if trails[x][y] then grid_leds[x][y] = util.round(util.linlin(0, TRAIL_ANI_LENGTH, 0, TRAIL_BRIGHTNESS, trails[x][y])) | |
else grid_leds[x][y] = 0 end | |
if (x > params:get("pattern_width") or y > params:get("pattern_height")) and grid_leds[x][y] < OUTSIDE_BRIGHTNESS then grid_leds[x][y] = OUTSIDE_BRIGHTNESS end | |
end | |
end | |
-- Draw down marks | |
for k, v in pairs(down_marks) do | |
brightness = util.round(util.linlin(0, DOWN_ANI_LENGTH, 0, DOWN_BRIGHTNESS, v.time_remaining)) | |
for i = 1, grid_w do | |
if grid_leds[i][v.y] < brightness then grid_leds[i][v.y] = brightness end | |
end | |
for i = 1, grid_h do | |
if grid_leds[v.x][i] < brightness then grid_leds[v.x][i] = brightness end | |
end | |
if v.active and grid_leds[v.x][v.y] < INACTIVE_BRIGHTNESS then grid_leds[v.x][v.y] = INACTIVE_BRIGHTNESS end | |
end | |
-- Draw remove animations | |
for _, v in pairs(remove_animations) do | |
brightness = util.round(util.linlin(0, REMOVE_ANI_LENGTH, 0, 15, v.time_remaining)) | |
if v.orientation == "row" then | |
for i = 1, grid_w do | |
if grid_leds[i][v.position] < brightness then grid_leds[i][v.position] = brightness end | |
end | |
else | |
for i = 1, grid_h do | |
if grid_leds[v.position][i] < brightness then grid_leds[v.position][i] = brightness end | |
end | |
end | |
end | |
-- Draw notes | |
for _, n in pairs(notes) do | |
if n.active then brightness = ACTIVE_BRIGHTNESS | |
else brightness = INACTIVE_BRIGHTNESS end | |
if n.position <= grid_w then | |
local ny | |
for i = 0, n.length - 1 do | |
ny = n.head + (i * n.direction * -1) | |
ny = (ny - 1) % params:get("pattern_height") + 1 | |
if ny > 0 and ny <= grid_h then | |
grid_leds[n.position][ny] = brightness | |
end | |
end | |
end | |
end | |
-- Draw triggers | |
for _, t in pairs(triggers) do | |
if t.active then brightness = ACTIVE_BRIGHTNESS | |
else brightness = INACTIVE_BRIGHTNESS end | |
if t.position <= grid_h then | |
local tx | |
for i = 0, t.length - 1 do | |
tx = t.head + (i * t.direction * -1) | |
tx = (tx - 1) % params:get("pattern_width") + 1 | |
if tx > 0 and tx <= grid_w then | |
grid_leds[tx][t.position] = brightness | |
end | |
end | |
end | |
end | |
for x = 1, grid_w do | |
for y = 1, grid_h do | |
grid_device.led(x, y, grid_leds[x][y]) | |
end | |
end | |
grid_device.refresh() | |
end | |
function redraw() | |
screen.clear() | |
-- Scale name | |
screen.move(5, 10) | |
screen.level(15) | |
screen.text(MusicUtil.note_num_to_name(rootNote) .. " " .. tonality) | |
-- Scale notes | |
local x, y = 5, 14 | |
local scale_note_names = MusicUtil.note_nums_to_names(gridScale, true) | |
local COLS = 4 | |
if #scale_note_names ~= 16 then | |
--print (#scale_note_names) | |
else | |
for i = 1, grid_w do | |
if (i - 1) % COLS == 0 then x, y = 5, y + 11 end | |
local is_active = false | |
for _, n in pairs(notes) do | |
if n.position == i and n.active then | |
is_active = true | |
break | |
end | |
end | |
local underline_length = 10 | |
if string.len(scale_note_names[i]) > 3 then | |
underline_length = 18 | |
elseif string.len(scale_note_names[i]) > 2 then | |
underline_length = 16 | |
end | |
if custom_scale and i == scale_edit_id then | |
screen.level(15) | |
screen.move(x - 1, y + 2.5) | |
screen.line(x + underline_length, y + 2.5) | |
screen.stroke() | |
end | |
if is_active then screen.level(15) | |
else screen.level(3) end | |
screen.move(x, y) | |
screen.text(scale_note_names[i]) | |
x = x + 25 | |
end | |
end | |
screen.update() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment