Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
929 lines (871 sloc) 25.8 KB
-- euclidean sample instrument
-- with trigger conditions.
--
-- ----------
--
-- based on tehn/playfair,
-- with generous contributions
-- from junklight and okyeron.
--
-- ----------
--
-- samples can be loaded
-- via the parameter menu.
--
-- ----------
-- home
--
-- enc1 = cycle through
-- the tracks.
-- enc2 = set the number
-- of trigs.
-- enc3 = set the number
-- of steps.
-- key2 = start and stop the
-- clock.
--
-- on the home screen,
-- key3 is alt.
--
-- alt + enc1 = mix volume
-- alt + enc2 = rotation
-- alt + enc3 = bpm
--
-- ----------
-- holding key1 will bring up the
-- track edit screen. release to
-- return home.
-- ----------
-- track edit
--
-- encoders 1-3 map to
-- parameters 1-3.
--
-- key2 = advance to the
-- next track.
-- key3 = advance to the
-- next page.
--
-- ----------
-- grid
-- ----------
--
-- col 1 select track edit
-- col 2 provides mute toggles
--
-- the dimly lit 5x5 grid is
-- made up of memory cells.
-- memory cells hold both
-- pattern and pset data.
-- simply pressing a cell
-- will load the pattern
-- data.
--
-- button 4 on row 7 starts
-- and stops the clock.
-- while the clock is stopped
-- the button will blink.
--
-- button 5 on row 7 is
-- the phase reset button.
--
-- button 8 on row 7 is
-- the pset load button.
--
-- to load a pset, press
-- and hold the pset load
-- button while touching
-- the desired memory cell.
--
-- open track edit pages
-- with grid buttons 4-7 on
-- the bottom row.
--
-- button 8 on the bottom row
-- is the copy button.
--
-- to copy a pattern to a new
-- cell hold the copy button,
-- and press the cell you'd
-- like to copy.
-- the cell will blink. while
-- still holding copy, press the
-- destination cell.
-- release the copy button.
--
-- v1.2 @justmat
--
-- llllllll.co/t/21081
er = require 'er'
engine.name = 'Ack'
local ack = require 'ack/lib/ack'
local g = grid.connect()
local m = midi.connect()
local alt = 0
local reset = false
-- 0 == home, 1 == track edit
local view = 0
local page = 0
local track_edit = 1
local stopped = 1
local pset_load_mode = false
local current_pset = 0
-- for new clock system
local clock_id = 0
function pulse()
while true do
clock.sync(1/4)
step()
end
end
function clock.transport.stop()
clock.cancel(clock_id)
end
function clock.transport.start()
clock_id = clock.run(pulse)
end
-- a table of midi note on/off status i = 1/0
local note_off_queue = {}
for i = 1, 8 do
note_off_queue[i] = 0
end
-- added for grid support - junklight
local current_mem_cell = 1
local current_mem_cell_x = 4
local current_mem_cell_y = 1
local copy_mode = false
local blink = false
local copy_source_x = -1
local copy_source_y = -1
function simplecopy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do
res[simplecopy(k)] = simplecopy(v)
end
return res
end
local memory_cell = {}
for j = 1,25 do
memory_cell[j] = {}
for i=1, 8 do
memory_cell[j][i] = {
k = 0,
n = 16,
pos = 1,
s = {},
prob = 100,
trig_logic = 0,
logic_target = track_edit,
rotation = 0,
mute = 0
}
end
end
local function gettrack( cell , tracknum )
return memory_cell[cell][tracknum]
end
local function cellfromgrid( x , y )
return (((y - 1) * 5) + (x -4)) + 1
end
local function rotate_pattern(t, rot, n, r)
-- rotate_pattern comes to us via okyeron and stackexchange
n, r = n or #t, {}
rot = rot % n
for i = 1, rot do
r[i] = t[n - rot + i]
end
for i = rot + 1, n do
r[i] = t[i - rot]
end
return r
end
local function reer(i)
if gettrack(current_mem_cell,i).k == 0 then
for n=1,32 do gettrack(current_mem_cell,i).s[n] = false end
else
gettrack(current_mem_cell,i).s = rotate_pattern(er.gen(gettrack(current_mem_cell,i).k, gettrack(current_mem_cell,i).n), gettrack(current_mem_cell, i).rotation)
end
end
local function trig()
-- mute state is ignored for trigger logics
for i, t in ipairs(memory_cell[current_mem_cell]) do
-- no trigger logic
if t.trig_logic==0 and t.s[t.pos] then
if math.random(100) <= t.prob and t.mute == 0 then
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i .. "_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
end
else
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
-- logical and
if t.trig_logic == 1 then
if t.s[t.pos] and gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] then
if math.random(100) <= t.prob and t.mute == 0 then
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i .. "_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
else break end
else
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
-- logical or
elseif t.trig_logic == 2 then
if t.s[t.pos] or gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] then
if math.random(100) <= t.prob and t.mute == 0 then
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i .. "_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
else break end
else
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
-- logical nand
elseif t.trig_logic == 3 then
if t.s[t.pos] and gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] then
elseif t.s[t.pos] then
if math.random(100) <= t.prob and t.mute == 0 then
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i .. "_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
else break end
else
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
-- logical nor
elseif t.trig_logic == 4 then
if not t.s[t.pos] and math.random(100) <= t.prob then
if not gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] and t.mute == 0 then
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i.."_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
else break end
else
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
-- logical xor
elseif t.trig_logic == 5 then
if t.mute == 0 and math.random(100) <= t.prob then
if not t.s[t.pos] and not gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] then
elseif t.s[t.pos] and gettrack(current_mem_cell,t.logic_target).s[gettrack(current_mem_cell,t.logic_target).pos] then
else
if params:get(i.."_send_midi") == 1 then
engine.trig(i-1)
end
if i <= 4 and params:get(i .. "_send_crow") == 2 then
crow.output[i]()
end
if params:get(i .. "_send_midi") == 2 then
m:note_on(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 1
end
if note_off_queue[i] == 1 then
m:note_off(params:get(i.."_midi_note"), 100, params:get(i.."_midi_chan"))
note_off_queue[i] = 0
end
end
else break end
end
end
end
function init()
for i=1, 8 do reer(i) end
screen.line_width(1)
params:add_separator('tracks')
for i = 1, 8 do
params:add_group("track " .. i, 25)
ack.add_channel_params(i)
params:add_option(i.."_send_midi", i..": send midi", {"no", "yes"}, 1)
params:add_number(i.."_midi_chan", i..": midi chan", 1, 16, 1)
params:add_number(i.."_midi_note", i..": midi note", 0, 127, 0)
end
params:add_separator('crow sends')
for i = 1, 8 do
if i <= 4 then
params:add_option(i .. "_send_crow", i .. ": send crow", {"no", "yes"}, 1)
end
end
params:add_separator('effects')
ack.add_effects_params()
-- load default pset
params:read()
params:bang()
-- load pattern data
loadstate()
if stopped==1 then
clock.cancel(clock_id)
else
clock_id = clock.run(pulse)
end
-- grid refresh timer, 15 fps
metro_grid_redraw = metro.init(function(stage) grid_redraw() end, 1 / 15)
metro_grid_redraw:start()
-- blink for copy mode
metro_blink = metro.init(function(stage) blink = not blink end, 1 / 4)
metro_blink:start()
-- savestate timer
metro_save = metro.init(function(stage) savestate() end, 10)
metro_save:start()
-- crow triggers
for i = 1, 4 do
crow.output[i].action = "pulse(.1, 5, 1)"
end
end
function reset_pattern()
reset = true
end
function step()
if reset then
for i=1,8 do
gettrack(current_mem_cell,i).pos = 1
end
reset = false
else
for i=1,8 do
gettrack(current_mem_cell,i).pos = (gettrack(current_mem_cell,i).pos % gettrack(current_mem_cell,i).n) + 1
end
end
trig()
redraw()
end
function key(n,z)
-- home and track edit views
if n==1 then view = z end
-- track edit view
if view==1 then
if n==3 and z==1 then
if params:get(track_edit.."_send_midi") == 1 then
page = (page + 1) % 4
-- there are only 2 pages of midi options
else page = (page + 1) % 2 end
end
end
if n==3 then alt = z end
-- track selection in track edit view
if view==1 then
if n==2 and z==1 then
track_edit = (track_edit % 8) + 1
end
end
if alt==1 then
-- track phase reset
if n==2 and z==1 then
if gettrack(current_mem_cell, track_edit).mute == 1 then
gettrack(current_mem_cell, track_edit).mute = gettrack(current_mem_cell, track_edit).mute == 0 and 1 or 0
else
reset_pattern()
if stopped == 1 then
step()
end
end
end
end
-- home view. start/stop
if alt==0 and view==0 then
if n==2 and z==1 then
if stopped==0 then
stopped = 1
clock.cancel(clock_id)
elseif stopped==1 then
stopped = 0
clock_id = clock.run(pulse)
end
end
end
redraw()
end
function enc(n,d)
if alt==1 then
-- mix volume control
if n==1 then
params:delta("output", d)
-- track rotation control
elseif n==2 then
gettrack(current_mem_cell, track_edit).rotation = util.clamp(gettrack(current_mem_cell, track_edit).rotation + d, 0, 32)
gettrack(current_mem_cell,track_edit).s = rotate_pattern( gettrack(current_mem_cell,track_edit).s, gettrack(current_mem_cell, track_edit).rotation )
redraw()
-- bpm control
elseif n==3 then
params:delta("clock_tempo", d)
end
-- track edit view
elseif view==1 and page==0 then
-- only show the engine edit options if midi note send is off
if params:get(track_edit.."_send_midi") == 1 then
-- per track volume control
if n==1 then
params:delta(track_edit .. "_vol", d)
elseif n==2 then
params:delta(track_edit .. "_vol_env_atk", d)
elseif n==3 then
params:delta(track_edit .. "_vol_env_rel", d)
end
-- if send midi is on
else
-- encoder 1 sets midi channel, 2 selects a note to send
if n==1 then
params:delta(track_edit .. "_midi_chan", d)
elseif n==2 then
params:delta(track_edit .. "_midi_note", d)
end
end
elseif view==1 and page==1 then
-- trigger logic and probability settings
if n==1 then
gettrack(current_mem_cell,track_edit).trig_logic = util.clamp(d + gettrack(current_mem_cell,track_edit).trig_logic, 0, 5)
elseif n==2 then
gettrack(current_mem_cell,track_edit).logic_target = util.clamp(d+ gettrack(current_mem_cell,track_edit).logic_target, 1, 8)
elseif n==3 then
gettrack(current_mem_cell,track_edit).prob = util.clamp(d + gettrack(current_mem_cell,track_edit).prob, 1, 100)
end
elseif view==1 and page==2 then
-- sample playback settings
if n==1 then
params:delta(track_edit .. "_speed", d)
elseif n==2 then
params:delta(track_edit .. "_start_pos", d)
elseif n==3 then
params:delta(track_edit .. "_end_pos", d)
end
elseif view==1 and page==3 then
-- filter and fx sends
if n==1 then
params:delta(track_edit .. "_filter_cutoff", d)
elseif n==2 then
params:delta(track_edit .. "_delay_send", d)
elseif n==3 then
params:delta(track_edit .. "_reverb_send", d)
end
-- HOME
-- choose focused track, track fill, and track length
elseif n==1 and d==1 then
track_edit = (track_edit % 8) + d
elseif n==1 and d==-1 then
track_edit = (track_edit + 6) % 8 + 1
elseif n == 2 then
gettrack(current_mem_cell,track_edit).k = util.clamp(gettrack(current_mem_cell,track_edit).k+d,0,gettrack(current_mem_cell,track_edit).n)
elseif n==3 then
gettrack(current_mem_cell,track_edit).n = util.clamp(gettrack(current_mem_cell,track_edit).n+d,1,32)
gettrack(current_mem_cell,track_edit).k = util.clamp(gettrack(current_mem_cell,track_edit).k,0,gettrack(current_mem_cell,track_edit).n)
end
reer(track_edit)
redraw()
end
function redraw()
screen.aa(0)
screen.clear()
if view==0 and alt==0 then
for i=1, 8 do
if gettrack(current_mem_cell, i).mute == 1 then
screen.move(17,i*7.70)
screen.text_center("m")
end
screen.level((i == track_edit) and 15 or 4)
screen.move(8, i*7.70)
screen.text_center(gettrack(current_mem_cell,i).k)
screen.move(25,i*7.70)
screen.text_center(gettrack(current_mem_cell,i).n)
for x=1,gettrack(current_mem_cell,i).n do
screen.level(gettrack(current_mem_cell,i).pos==x and 15 or 2)
screen.move(x*3 + 32, i*7.70)
if gettrack(current_mem_cell,i).s[x] then
screen.line_rel(0,-6)
else
screen.line_rel(0,-2)
end
screen.stroke()
end
end
elseif view==0 and alt==1 then
screen.level(4)
screen.move(0, 8 + 11)
screen.text("vol")
screen.move(0, 16 + 11)
screen.text(string.format("%.1f", params:get("output_level")))
screen.move(0, 21 + 11)
screen.line(20, 21 + 11)
screen.move(0, 30 + 11)
screen.text("bpm")
screen.move(0, 40 + 11)
screen.text(string.format("%.1f", clock.get_tempo()))
if gettrack(current_mem_cell, track_edit).mute == 1 then
screen.font_face(25)
screen.font_size(6)
screen.move(0, 60)
screen.text("muted")
screen.font_face(1)
screen.font_size(8)
end
for i=1,8 do
screen.level((i == track_edit) and 15 or 4)
screen.move(25, i*7.70)
screen.text_center(gettrack(current_mem_cell, i).rotation)
for x=1,gettrack(current_mem_cell,i).n do
screen.level(gettrack(current_mem_cell,i).pos==x and 15 or 2)
screen.move(x*3 + 32, i*7.70)
if gettrack(current_mem_cell,i).s[x] then
screen.line_rel(0,-6)
else
screen.line_rel(0,-2)
end
screen.stroke()
end
end
elseif view==1 and page==0 then
if params:get(track_edit.."_send_midi") == 1 then
screen.move(5, 10)
screen.level(15)
screen.text("track : " .. track_edit)
screen.move(120, 10)
screen.text_right("page " .. page + 1)
screen.move(5, 15)
screen.line(121, 15)
screen.move(64, 25)
screen.level(4)
screen.text_center("1. vol : " .. string.format("%.1f", params:get(track_edit .. "_vol")))
screen.move(64, 35)
screen.text_center("2. envelope attack : " .. params:get(track_edit .. "_vol_env_atk"))
screen.move(64, 45)
screen.text_center("3. envelope release : " .. params:get(track_edit .. "_vol_env_rel"))
else
screen.move(5, 10)
screen.level(15)
screen.text("track : " .. track_edit)
screen.move(120, 10)
screen.text_right("page " .. page + 1)
screen.move(5, 15)
screen.line(121, 15)
screen.move(64, 25)
screen.level(4)
screen.text_center("1. midi channel : " .. params:get(track_edit .. "_midi_chan"))
screen.move(64, 35)
screen.text_center("2. midi note : " .. params:get(track_edit .. "_midi_note"))
end
elseif view==1 and page==1 then
screen.move(5, 10)
screen.level(15)
screen.text("track : " .. track_edit)
screen.move(120, 10)
screen.text_right("page " .. page + 1)
screen.move(5, 15)
screen.line(121, 15)
screen.move(64, 25)
screen.level(4)
if gettrack(current_mem_cell,track_edit).trig_logic == 0 then
screen.text_center("1. trig logic : -")
screen.move(64, 35)
screen.level(1)
screen.text_center("2. logic target : -")
screen.level(4)
elseif gettrack(current_mem_cell,track_edit).trig_logic == 1 then
screen.text_center("1. trig logic : and")
screen.move(64, 35)
screen.text_center("2. logic target : " .. gettrack(current_mem_cell,track_edit).logic_target)
elseif gettrack(current_mem_cell,track_edit).trig_logic == 2 then
screen.text_center("1. trig logic : or")
screen.move(64, 35)
screen.text_center("2. logic target : " .. gettrack(current_mem_cell,track_edit).logic_target)
elseif gettrack(current_mem_cell,track_edit).trig_logic == 3 then
screen.text_center("1. trig logic : nand")
screen.move(64, 35)
screen.text_center("2. logic target : " .. gettrack(current_mem_cell,track_edit).logic_target)
elseif gettrack(current_mem_cell,track_edit).trig_logic == 4 then
screen.text_center("1. trig logic : nor")
screen.move(64, 35)
screen.text_center("2. logic target : " .. gettrack(current_mem_cell,track_edit).logic_target)
elseif gettrack(current_mem_cell,track_edit).trig_logic == 5 then
screen.text_center("1. trig logic : xor")
screen.move(64, 35)
screen.text_center("2. logic target : " .. gettrack(current_mem_cell,track_edit).logic_target)
end
screen.move(64, 45)
screen.text_center("3. trig probability : " .. gettrack(current_mem_cell,track_edit).prob .. "%")
elseif view==1 and page==2 then
screen.move(5, 10)
screen.level(15)
screen.text("track : " .. track_edit)
screen.move(120, 10)
screen.text_right("page " .. page + 1)
screen.move(5, 15)
screen.line(121, 15)
screen.move(64, 25)
screen.level(4)
screen.text_center("1. speed : " .. params:get(track_edit .. "_speed"))
screen.move(64, 35)
screen.text_center("2. start pos : " .. params:get(track_edit .. "_start_pos"))
screen.move(64, 45)
screen.text_center("3. end pos : " .. params:get(track_edit .. "_end_pos"))
elseif view==1 and page==3 then
screen.move(5, 10)
screen.level(15)
screen.text("track : " .. track_edit)
screen.move(120, 10)
screen.text_right("page " .. page + 1)
screen.move(5, 15)
screen.line(121, 15)
screen.level(4)
screen.move(64, 25)
screen.text_center("1. filter cutoff : " .. math.floor(params:get(track_edit .. "_filter_cutoff") + 0.5))
screen.move(64, 35)
screen.text_center("2. delay send : " .. params:get(track_edit .. "_delay_send"))
screen.move(64, 45)
screen.text_center("3. reverb send : " .. params:get(track_edit .. "_reverb_send"))
end
screen.stroke()
screen.update()
end
-- grid stuff - junklight
function g.key(x, y, state)
-- use first column to switch track edit
if x == 1 then
track_edit = y
end
-- second column provides mutes
if x == 2 and state == 1 then
if gettrack(current_mem_cell, y).mute == 0 then
gettrack(current_mem_cell, y).mute = 1
elseif gettrack(current_mem_cell, y).mute == 1 then
gettrack(current_mem_cell, y).mute = 0
end
end
-- x 4-6, are used to open track parameters pages
if y == 8 and x >= 4 and x <= 7 and state == 1 then
view = 1
page = x - 4
else
view = 0
end
-- start and stop button.
if x == 4 and y == 7 and state == 1 then
if stopped == 1 then
stopped = 0
clock_id = clock.run(pulse)
else
stopped = 1
clock.cancel(clock_id)
end
end
-- reset button
if x == 5 and y == 7 and state == 1 then
reset_pattern()
if stopped == 1 then
step()
end
end
-- set pset load button
if x == 8 and y == 7 and state == 1 then
pset_load_mode = true
elseif x == 8 and y == 7 and state == 0 then
pset_load_mode = false
end
-- load pset 1-25
if pset_load_mode then
if y >= 1 and y <= 5 and x >= 4 and x <= 8 and state == 1 then
params:read(cellfromgrid(x,y))
params:bang()
print("loaded pset " .. cellfromgrid(x, y))
current_pset = cellfromgrid(x, y)
-- if you were stopped before loading, stay stopped after loading
if stopped == 1 then
run = false
end
end
end
-- copy button
if x == 8 and y==8 and state == 1 then
copy_mode = true
copy_source_x = -1
copy_source_y = -1
elseif x == 8 and y==8 and state == 0 then
copy_mode = false
copy_source_x = -1
copy_source_y = -1
end
-- memory cells
-- switches on grid down
if not copy_mode and not pset_load_mode then
if y >= 1 and y <= 5 and x >= 4 and x <= 8 and state == 1 then
current_mem_cell = cellfromgrid(x,y)
current_mem_cell_x = x
current_mem_cell_y = y
for i = 1, 8 do reer(i) end
end
else
if y >= 1 and y <= 5 and x >= 4 and x <= 8 and state == 0 then
if not pset_load_mode then
-- copy functionality
if copy_source_x == -1 then
-- first button sets the source
copy_source_x = x
copy_source_y = y
else
-- second button copies source into target
if copy_source_x ~= -1 and not ( copy_source_x == x and copy_source_y == y) then
sourcecell = cellfromgrid( copy_source_x , copy_source_y )
targetcell = cellfromgrid( x , y )
memory_cell[targetcell] = simplecopy(memory_cell[sourcecell])
end
end
end
end
end
redraw()
end
function grid_redraw()
if g == nil then
-- bail if we are too early
return
end
g:all(0)
-- highlight current track
g:led(1, track_edit, 15)
-- track edit page buttons
for page = 0, 3 do
g:led(page + 4, 8, 3)
end
-- highlight page if open
if view == 1 then
g:led(page + 4, 8, 14)
end
-- mutes - bright for on, dim for off
for i = 1,8 do
if gettrack(current_mem_cell, i).mute == 1 then
g:led(2, i, 15)
else g:led(2, i, 4)
end
end
-- memory cells
for x = 4,8 do
for y = 1,5 do
g:led(x, y, 3)
end
end
-- highlight active cell
g:led(current_mem_cell_x, current_mem_cell_y, 15)
if copy_mode then
-- copy mode - blink the source if set
if copy_source_x ~= -1 then
if blink then
g:led(copy_source_x, copy_source_y, 4)
else
g:led(copy_source_x, copy_source_y, 12)
end
end
end
-- start/stop
if stopped == 0 then
g:led(4, 7, 15)
elseif stopped == 1 then
if blink then
g:led(4, 7, 4)
else
g:led(4, 7, 12)
end
end
-- reset button
g:led(5, 7, 3)
-- load pset button
if pset_load_mode then
g:led(8, 7, 12)
else g:led(8, 7, 3) end
-- copy button
if copy_mode then
g:led(8, 8, 14)
else
g:led(8, 8, 3)
end
g:refresh()
end
function savestate()
local file = io.open(_path.data .. "foulplay/foulplay-pattern.data", "w+")
io.output(file)
io.write("v1" .. "\n")
for j = 1, 25 do
for i = 1, 8 do
io.write(memory_cell[j][i].k .. "\n")
io.write(memory_cell[j][i].n .. "\n")
io.write(memory_cell[j][i].prob .. "\n")
io.write(memory_cell[j][i].trig_logic .. "\n")
io.write(memory_cell[j][i].logic_target .. "\n")
io.write(memory_cell[j][i].rotation .. "\n")
io.write(memory_cell[j][i].mute .. "\n")
end
end
io.close(file)
end
function loadstate()
local file = io.open(_path.data .. "foulplay/foulplay-pattern.data", "r")
if file then
print("datafile found")
io.input(file)
if io.read() == "v1" then
for j = 1, 25 do
for i = 1, 8 do
memory_cell[j][i].k = tonumber(io.read())
memory_cell[j][i].n = tonumber(io.read())
memory_cell[j][i].prob = tonumber(io.read())
memory_cell[j][i].trig_logic = tonumber(io.read())
memory_cell[j][i].logic_target = tonumber(io.read())
memory_cell[j][i].rotation = tonumber(io.read())
memory_cell[j][i].mute = tonumber(io.read())
end
end
else
print("invalid data file")
end
io.close(file)
end
for i = 1, 8 do reer(i) end
end