Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
479 lines (409 sloc) 10.6 KB
--
-- MIDI MONITOR
-- 0.5.2 - @okyeron
--
--
-- E1 - select MIDI device
-- E3 - scrollback through MIDI messages
--
-- K2 - toggle unmute/mute internal synth
-- K3 - clear screen / buffer
--
engine.name = 'PolyPerc'
local devicepos = 1
local device_name = ""
local device_name_display = ""
local mdevs = {}
local midi_device
local msg = {}
--local t = 0 -- last tap time
--local dt = 1 -- last tapped delta
local default_bpm = 90
local tempo
local running = false
local clocking = false
local blinkers = {false}
local mute = true
local midi_buffer = {}
local midi_buffer_display = {}
local midi_buffer_len = 256
local buff_start = 1
-- display grid setup
local line_height = 8
local line_offset = 16
local col1 = 0
local col2 = 11
local col3 = 29
local col4 = col3 + 18
local col5 = col4 + 44
local col6 = 128
-- scrolling labels setup
local scr_tempo = 90
local scr_step = 1
local scr_step_increment = 1
local scr_max_length_device_name = 24 -- reduce value to force scrolling of device names
local scr_max_length_col4 = 9 -- reduce value to force scrolling of midi type values
local scr_step_metro
-- utilities
function copy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
return res
end
function truncate(str, trunc_len, with_ellipses)
if (str ~= nil) then
if (with_ellipses and #str > trunc_len) then
trunc_str = string.sub(str, 0, trunc_len-1) .. "..."
else
trunc_str = string.sub(str, 0, trunc_len)
end
return trunc_str
end
end
-- scroll the device name and midi type
-- scrolling occurs based on the length of the fields as set by the two variables:
-- scr_max_length_device_name
-- scr_max_length_col4
local function scroll_text()
scr_step = scr_step + scr_step_increment
--scroll device name
scr_text = ""
scr_current_cut = ""
scr_head = ""
scr_tail = ""
scr_text = ""
if (devicepos and mdevs[devicepos]) then
device_name_display = devicepos .. ": ".. mdevs[devicepos]
if (#device_name_display > scr_max_length_device_name) then
scr_text = device_name_display
scr_current_cut = (scr_step) % #scr_text + 1
-- print(scr_current_cut)
scr_head = string.sub(scr_text,0, scr_current_cut)
scr_tail = string.sub(scr_text,scr_current_cut, #scr_text)
scr_text = scr_tail .. " " .. scr_head
device_name_display = scr_text
end
redraw()
end
--scroll text at top of midi_buffer list
scr_text = ""
scr_current_cut = ""
scr_head = ""
scr_tail = ""
scr_text = ""
if (midi_buffer[buff_start][4]) then
if (#midi_buffer[buff_start][4] > scr_max_length_col4) then
scr_text = midi_buffer[buff_start][4] and midi_buffer[buff_start][4] or ""
scr_current_cut = (scr_step) % #scr_text + 1
-- print(scr_current_cut)
scr_head = string.sub(scr_text,0, scr_current_cut)
scr_tail = string.sub(scr_text,scr_current_cut, #scr_text)
scr_text = scr_tail .. " " .. scr_head
midi_buffer_display[buff_start][4] = string.sub(scr_text,0,scr_max_length)
-- print(midi_buffer_display[buff_start][4])
-- print("scroll: ".. scr_tail .." / " .. scr_head)
else
midi_buffer_display[buff_start][4] = midi_buffer[buff_start][4]
end
redraw()
end
end
local function scr_start_stop_metro()
if scr_step_metro.is_running then
scr_step_metro:stop()
else
scr_step = 0
scr_step_metro:start(60 / scr_tempo / 4) -- 16ths
end
end
--
function blink_generator(x)
while true do
--print (clock.get_beats() )
clock.sync(1/2)
--print (clock.get_beats() )
blinkers[x] = not blinkers[x]
redraw()
end
end
--
-- INIT
--
function init()
clear_midi_buffer()
engine.amp(0)
--_norns.rev_off()
connect()
get_midi_names()
print_midi_names()
-- setup params
params:add{type = "option", id = "midi_device", name = "MIDI-device", options = mdevs , default = 1,
action = function(value)
midi_device.event = nil
--grid.cleanup()
midi_device = midi.connect(value)
midi_device.event = midi_event
midi.update_devices()
mdevs = {}
get_midi_names()
params.params[1].options = mdevs
--tab.print(params.params[1].options)
devicepos = value
if clocking then
clock.cancel(blink_id)
clocking = false
end
print ("midi ".. devicepos .." selected: " .. mdevs[devicepos])
end}
--clock.set_source(1)
params:set("clock_source", 2)
params:set("clock_tempo", default_bpm)
--tempo = util.round (clock.get_tempo(), 1)
tempo = util.round (params:get("clock_tempo"),1)
--print(tempo)
--print(tempo2)
-- Render Style
screen.level(15)
screen.aa(0)
screen.line_width(1)
--startup scrolling metro
scr_step_metro = metro.init()
scr_step_metro.event = scroll_text
scr_start_stop_metro()
end
-- END INIT
function get_midi_names()
-- Get a list of grid devices
for id,device in pairs(midi.vports) do
mdevs[id] = device.name
end
end
function print_midi_names()
-- Get a list of grid devices
print ("MIDI Devices:")
for id,device in pairs(midi.vports) do
mdevs[id] = device.name
print(id, mdevs[id])
end
end
function connect()
midi.update_devices()
midi_device = midi.connect(devicepos)
midi_device.event = midi_event
--midi_device.add = on_midi_add
--midi_device.remove = on_midi_remove
end
function clear_midi_buffer()
midi_buffer = {}
for z=1,midi_buffer_len do
table.insert(midi_buffer, {})
end
buff_start = 1
end
function midi_event(data)
msg = midi.to_msg(data)
if msg.type == "start" then
clock.transport.reset()
clock.transport.start()
elseif msg.type == "continue" then
if running then
clock.transport.stop()
else
clock.transport.start()
end
end
if msg.type == "stop" then
clock.transport.stop()
end
if msg.type == "clock" then
tempo = util.round (clock.get_tempo(), 1)
--print(tempo)
if not clocking then
clocking = true
blink_id = clock.run(blink_generator, 1)
else
end
else
-- {msg.ch, msg.note , msg.vel, msg.type, msg.val}
temp_msg = {}
if msg.ch then temp_msg[1] = msg.ch else temp_msg[1] = "" end
if msg.note then temp_msg[2] = msg.note else temp_msg[2] = "" end
if msg.vel then temp_msg[3] = msg.vel else temp_msg[3] = "" end
if msg.type and msg.type ~= "clock" then
if msg.cc then
temp_msg[4] = msg.type ..": ".. msg.cc
else
temp_msg[4] = msg.type
end
else
temp_msg[4] = ""
end
if msg.val then temp_msg[5] = msg.val else temp_msg[5] = "" end
table.insert (midi_buffer, 1, temp_msg)
midi_buffer_display = copy(midi_buffer)
if not mute then
play(msg) -- play notes with default engine
end
redraw()
end
end
--
-- CLOCK coroutines
--
function pulse()
while true do
--clock.sync(1/8)
--step_event()
end
end
function clock.transport.start()
print("transport.start")
running = true
--id = clock.run(pulse)
--running = true
end
function clock.transport.stop()
print("transport.stop")
running = false
end
function clock.transport.reset()
--print("transport.reset")
end
--
-- play notes
--
function play(msg)
if msg.type == 'note_on' then
hz = note_to_hz(msg.note)
--print(hz)
engine.amp(msg.vel / 127)
engine.hz(hz)
end
if msg.type == 'note_off' then
engine.amp(0)
end
end
--
-- Interaction
--
function key(n, z)
if n==2 and z == 1 then
mute = not mute
end
if n == 3 and z == 1 then
clear_midi_buffer()
end
redraw()
end
function enc(id,delta)
if id == 1 then
--print(params:get("midi_device"))
params:set("midi_device", util.clamp(devicepos+delta, 1,4))
if clocking then
clock.cancel(blink_id)
clocking = false
end
end
if id == 2 then
end
if id == 3 then
buff_start = util.clamp(buff_start + delta, 1, midi_buffer_len-5)
end
redraw()
end
--
-- Screen Render
--
function draw_labels()
screen.level(1)
screen.move(col1,(line_height * 2))
screen.text('ch')
screen.move(col2,(line_height * 2))
screen.text('num')
screen.move(col3,(line_height * 2))
screen.text('vel')
screen.move(col4,(line_height * 2))
screen.text('type')
screen.move(col5,(line_height * 2))
screen.text('value')
screen.move(col6,(line_height * 2))
screen.text_right('ln')
end
function draw_event()
for i=1,6 do
--print("i:",i)
buf_idx = buff_start + i - 1
if (midi_buffer[buf_idx][1] ~= nil) then
--truncate the type column if: (1) length > 8 and (2) it is not the first line of scrolling text
type_text = ""
if (i==1) then
type_text = truncate(midi_buffer_display[buf_idx][4],scr_max_length_col4,false)
else
type_text = truncate(midi_buffer[buf_idx][4],scr_max_length_col4,true)
end
screen.level(12)
screen.move(col1+1,(line_offset + line_height * i))
screen.text(midi_buffer_display[buf_idx][1])
screen.move(col2+1,(line_offset + line_height * i))
screen.text(midi_buffer_display[buf_idx][2])
screen.move(col3+2,(line_offset + line_height * i))
screen.text(midi_buffer_display[buf_idx][3])
screen.move(col4,(line_offset + line_height * i))
-- screen.text(midi_buffer_display[buf_idx][4])
screen.text(type_text)
screen.move(col5,(line_offset + line_height * i))
screen.text(midi_buffer_display[buf_idx][5])
screen.stroke()
screen.level(3)
screen.move(col6,(line_offset + line_height * i))
screen.text_right(buf_idx)
screen.stroke()
end
end
end
function redraw()
tempo_disp = util.round (clock.get_tempo(), 1)
screen.clear()
draw_labels()
if msg then
draw_event()
end
screen.level(3)
screen.move(90,7)
screen.text('bpm')
screen.move(110,7)
screen.text(tempo_disp)
screen.stroke()
if blinkers[1] and clocking then
screen.level(12)
screen.rect(124, 3, 4, 4)
screen.fill()
-- screen.pixel(112, 6)
else
screen.level(1)
screen.rect(124, 3, 4, 4)
screen.fill()
end
screen.level(1)
screen.line_width (1.5)
screen.move(0, 18)
screen.line (128, 18)
screen.stroke()
--screen.line_width (1)
--screen.move(116, 10)
--screen.line (116, 64)
--screen.stroke()
screen.level(15)
screen.move(0, 7)
device_name = truncate(device_name_display,scr_max_length_device_name,false)
screen.text(device_name_display)
-- screen.text(devicepos .. ": ".. mdevs[devicepos])
screen.update()
end
--
-- Utils
--
function note_to_hz(note)
return (440 / 32) * (2 ^ ((note - 9) / 12))
end