Skip to content

Instantly share code, notes, and snippets.

@millxing
Last active December 12, 2022 21:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save millxing/8630412f07a8ac162a9b08908af7c536 to your computer and use it in GitHub Desktop.
Save millxing/8630412f07a8ac162a9b08908af7c536 to your computer and use it in GitHub Desktop.
ISOKB is a 2-channel MIDI keyboard for Norns and Grid. It uses an isomorphic "in key" note layout similar to Ableton Push.
-- ISOKB
--
-- 2-channel MIDI keyboard for Norns and Grid
-- Isomorphic "in key" note layout (similar to Ableton Push)
-- Select a tonic note and one of nine 7-note scales (default C Major)
-- Two independent keyboards, each sends MIDI to a different MIDI channel
-- Left hand notes go to MIDI 1
-- Right hand notes go to MIDI 2
-- There will be multiple buttons for each note and all buttons corresponding to a pressed note will light up
--
-- Rob Schoen (email: millxing at gmail dot com)
music = require 'mark_eats/musicutil' -- Thanks Mark!
function init()
-- change these paramters to send the MIDI to channels other than 1 & 2
midi_channel_left = 1
midi_channel_right = 2
math.randomseed(os.time())
opening_animation()
mode = 1
toniclist = music.NOTE_NAMES
tonicnum = 1
new_scale()
redraw()
redraw_grid()
end
function new_scale()
-- fill scal with notes from selected scale
scale = music.generate_scale_of_length(36+(tonicnum-1),music.SCALES[mode].name,80)
base = scale[1] % 12
-- initialize keyboard
pressed = {}
notes = {}
root = {}
local c
local r
for c = 1,16 do
pressed[c] = {}
notes[c] = {}
root[c] = {}
for r = 8,1,-1 do
pressed[c][r] = 0
notes[c][r] = 0
root[c][r] = 0
end
end
-- fill notes and root table
local temp
local i
for r = 8,1,-1 do
for c = 1,7 do
if r==8 then
notes[c][8] = scale[c]
notes[c+9][8] = scale[c]
if notes[c][8] % 12 == base then root[c][8] = 1; end
if notes[c+9][8] % 12 == base then root[c+9][8] = 1; end
else
for i = 1,#scale do
if notes[c][r+1] == scale[i] then temp = i; end
end
notes[c][r] = scale[temp+3]
notes[c+9][r] = scale[temp+3]
if notes[c][r] % 12 == base then
root[c][r] = 1;
root[c+9][r] = 1;
end
end
end
end
clearAllNotes()
end
g = grid.connect()
m = midi.connect()
g.event = function(x,y,z)
-- events for button press
if z==1 then
pressed[x][y] = 1
if x > 8 then cplus = 9 else cplus = 0 end
for c = 1,7 do
for r = 1,8 do
if notes[c+cplus][r] == notes[x][y] then
pressed[c+cplus][r] = 1
end
end
end
print(x,y,notes[x][y], music.note_num_to_name(notes[x][y]))
if x<9 then
m.note_on(notes[x][y],90,1)
else
m.note_on(notes[x][y],90,2)
end
end
--events for button release
if z==0 then
pressed[x][y] = 0
if x > 8 then cplus = 9 else cplus = 0 end
for c = 1,7 do
for r = 1,8 do
if notes[c+cplus][r] == notes[x][y] then
pressed[c+cplus][r] = 0
end
end
end
if x<9 then
m.note_off(notes[x][y],0,1)
else
m.note_off(notes[x][y],0,2)
end
end
redraw_grid()
end
function redraw_grid()
g.all(2)
local r
local c
for r = 8,1,-1 do
for c = 1,7 do
g.led(c,r,pressed[c][r]==1 and 15 or (root[c][r]==1 and 10 or 5))
g.led(c+9,r, pressed[c+9][r]==1 and 15 or (root[c+9][r]==1 and 10 or 5))
end
end
g.refresh()
end
function redraw()
screen.clear()
screen.font_size(8)
screen.level(15)
screen.move(0,10)
screen.text("left side is MIDI ch 1")
screen.move(0,20)
screen.text("right side is MIDI ch 2")
screen.move(0,30)
screen.text("encoder 2 changes root note")
screen.move(0,40)
screen.text("encoder 3 changes scale")
screen.move(0,60)
screen.text("scale: " .. toniclist[tonicnum] .. " " .. music.SCALES[mode].name)
screen.update()
end
function enc(n,d)
-- select tonic note
if n==2 then
tonicnum = tonicnum + d
if tonicnum>12 then tonicnum = 1; end
if tonicnum<1 then tonicnum = 12; end
new_scale()
end
-- select scale
if n==3 then
mode = mode + d
if mode>9 then mode = 1; end
if mode<1 then mode = 9; end
new_scale()
end
redraw()
end
-- gratuitous opening animation
function opening_animation()
local a; local i; local j
local speed = .05
for a = 8,1,-1 do
g.all(0)
for i = 1,8 do
for j = 1+(a-1),16-(a-1) do
g.led(j,i,math.random(0,15))
end
end
g.refresh()
sleep(speed)
end
for a = 1,8 do
g.all(0)
for i = 1,8 do
for j = 1+(a-1),16-(a-1) do
g.led(j,i,math.random(0,15))
end
end
g.refresh()
sleep(speed)
end
g.all(0)
g.refresh()
end
-- pause or delay lua code
function sleep(s)
local ntime = os.clock() + s
repeat until os.clock() > ntime
end
-- clear midi buffer in case of stuck notes
function clearAllNotes()
local i; local j
for i = 1,2 do
for j = 1,127 do
m.note_off(j,0,i)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment