grid
control
Syntax | Description |
---|---|
my_grid = grid.connect(n) | Assign this connected grid to a script, defaults to port 1 unless n is specified |
my_grid:led(x, y, val) | Set state of single LED (x is horizontal, y is vertical, 1-indexed) of this connected grid, accepts val 0 (off) - 15 (full bright) |
my_grid:all(val) | Set state of all LEDs of this connected grid, expects val 0 (off) - 15 (full bright) |
my_grid:refresh() | Update any dirty LEDs on this connected grid |
my_grid:intensity(i) | Set LED intensity, expects 0 (off) - 15 (full bright) |
grid.add() | User script callback when any grid is connected (do not use my_grid or custom variable, must be grid ) |
grid.remove() | User script callback when any grid is disconnected (do not use my_grid or custom variable, must be grid ) |
query
Syntax | Description |
---|---|
my_grid.name | Returns name of this grid : string |
my_grid.device | If there’s a physical grid present at the connected port, this will not be nil : table |
my_grid.device.id | Returns this grid’s id : number |
my_grid.device.cols | Returns this grid’s column count : number |
my_grid.device.rows | Returns this grid’s row count : number |
my_grid.device.port | Returns this grid’s port : number |
my_grid.device.name | Returns this grid’s name : string |
my_grid.device.serial | Returns this grid’s serial : string |
example
This example is a bit long, but it demonstrates how an entire performance script can be defined in less than 150 lines of code.
We recommend testing it out + interacting with the mechanics before reading:
-
run the script + connect a grid – you’ll see a step sequencer set up as 8 unique lanes, each with their own unique pitch
-
mash some grid pads to add notes
-
use E2 on norns to change the column count (try 5)
-
press K2 on norns to randomize each lane’s playback
-
LEDs too bright? use E3 to turn down the intensity
-- // grid reference example \\
-- 8 playheads move across grid
-- press a key to add a step
--
-- K2: randomize the
-- playback rate
-- E2: clamp the column count
-- E3: change LED intensity
g = grid.connect() -- if no argument is provided, defaults to port 1
engine.name = 'PolyPerc'
MU = require 'musicutil'
function init()
engine.release(0.2)
engine.cutoff(1200)
grid_connected = g.device~= nil and true or false -- ternary operator, eg. http://lua-users.org/wiki/TernaryOperator
columns = grid_connected and g.device.cols or 16 -- keep track of device columns
rows = grid_connected and g.device.rows or 8 -- keep track of device rows
led_intensity = 15 -- scales LED intensity
notes = {60,61,51,65,48,68,70,77} -- some notes to play
playheads = {} -- table for playheads
for i = 1,rows do -- for each row...
playheads[i] = {} -- create a playhead!
playheads[i].pos = 1 -- track the position
playheads[i].rate = 1 -- start rate at 1 beat per step
playheads[i].data = {} -- for 1's and 0's
for j = 1,columns do
playheads[i].data[j] = 0 -- start with 0's in every step
end
playheads[i].clock = clock.run(
function()
while true do
clock.sync(playheads[i].rate) -- sync to playhead rate
playheads[i].pos = util.wrap(playheads[i].pos+1,1,columns) -- advance playhead position
if playheads[i].data[playheads[i].pos] == 1 then -- if data at this position equals 1...
engine.hz(MU.note_num_to_freq(notes[i])) -- play note!
end
grid_dirty = true -- redraw grid!
end
end
)
end
-- common redraw function:
redraw_clock = clock.run(
function()
while true do
clock.sleep(1/15)
if screen_dirty then
redraw()
end
if grid_dirty then
grid_redraw()
end
end
end
)
grid_dirty = true -- use flags to keep track of whether hardware needs to be redrawn
screen_dirty = true
end
function grid_redraw()
if grid_connected then -- only redraw if there's a grid connected
g:all(0) -- turn all the LEDs off
for x = 1,columns do -- for each column...
for y = 1,rows do -- for each row...
-- g:led(x,y,playheads[y].data[x] == 1 and (playheads[y].pos == x and 15 or 12) or (playheads[y].pos == x and 8 or 0))
-- ^^ this one line summarizes the next 12 lines!!! wild!!
if playheads[y].pos == x then -- if the playhead crosses...
if playheads[y].data[x] == 1 then -- an active step...
g:led(x,y,15) -- full-bright
else -- if step is non-active
g:led(x,y,8) -- semi-bright
end
else -- if not current playhead step...
if playheads[y].data[x] == 1 then-- an active step...
g:led(x,y,12) -- near-full
else -- not an active step
g:led(x,y,2) -- low
end
end
end
end
g:intensity(led_intensity) -- change intensity
g:refresh() -- refresh the LEDs
end
grid_dirty = false -- reset the flag because changes have been committed
end
function g.key(x,y,z)
if z == 1 then -- if we press any grid key
playheads[y].data[x] = playheads[y].data[x] == 0 and 1 or 0 -- ternary operator again! if data is 0, flip to 1 + vice versa
grid_dirty = true -- enable flag to redraw grid, because data has changed
end
end
function enc(n,d)
if n == 3 then -- if encoder 3, then...
led_intensity = util.clamp(led_intensity + d,0,15) -- change LED intensity (within 0 to 15 range)
elseif n == 2 then -- if encoder 2 then
columns = util.clamp(columns+d,1,16) -- change our column count (within 1 and 16)
end
grid_dirty = true -- enable flag to redraw grid, because data has changed
screen_dirty = true -- enable flag to redraw screen, because data has changed
end
function key(n,z)
if n == 2 and z == 1 then -- if key 2 is pressed down then...
for i = 1,#playheads do -- for each playhead (we don't know how many, because it's based on the connected grid)
playheads[i].rate = math.random(i*2) / math.random(i*3) -- randomize the playback rate for each
end
engine.pw(math.random(90)/100) -- change the pulse width
elseif n == 3 and z == 1 then -- if key 3 is pressed down then...
for i = 1,#playheads do -- for each playhead (we don't know how many, because it's based on the connected grid)
playheads[i].pos = 1 -- reset to step 1
end
end
grid_dirty = true -- enable flag to redraw grid, because data has changed
end
function redraw()
screen.clear() -- clear screen
screen.move(0,10)
screen.text("K2: RANDOMIZE RATES")
screen.move(0,20)
screen.text("K3: RESET TO STEP 1")
screen.move(128,54)
screen.text_right("E2: # OF COLUMNS ("..columns..")")
screen.move(128,64)
screen.text_right("E3: INTENSITY ("..led_intensity..")")
screen.update()
screen_dirty = false
end
function grid.add(new_grid) -- must be grid.add, not g.add (this is a function of the grid class)
print(new_grid.name.." says 'hello!'")
-- each grid added can be queried for device information:
print("new grid found at port: "..new_grid.port)
g = grid.connect(new_grid.port) -- connect script to the new grid
grid_connected = true -- a grid has been connected!
grid_dirty = true -- enable flag to redraw grid, because data has changed
end
function grid.remove(g) -- must be grid.remove, not g.remove (this is a function of the grid class)
print(g.name.." says 'goodbye!'")
end
description
Connects a script to a monome grid, adding a matrix of re-definable buttons and LEDs to control and display components of your script.