Skip to content

Instantly share code, notes, and snippets.

@jlmitch5
Created October 9, 2018 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jlmitch5/95e09e0b0ddd3b16f544afbf1ffa349c to your computer and use it in GitHub Desktop.
Save jlmitch5/95e09e0b0ddd3b16f544afbf1ffa349c to your computer and use it in GitHub Desktop.
--- boing_two - a bouncing ball app
--- with saved states
--- originally by declutter
--- Heavily based on flin
--- by artfwo
--- Press a grid key in columns 1-12 to
--- start a bouncing ball
--- and the bottom key to stop
--- columns 13-16 allow you to select
--- from different states of speed,
--- slew, scale and sound respectively
--- for a particular state setting
--- column, hold the row 1 key and
--- a key in row 2-7 to save the current
--- state to that key
--- hold the row 8 key and
--- a key in row 2-7 to remove the
--- state at that key
--- enc2 and enc3 to adjust the scales
--- key 2 to start / key 3 to stop a bounce on the currently selected note on the screen
--- Todo - add alternative scales from mark_eats
engine.name = 'PolySub'
local g = grid.connect(1)
function g.event(x,y,z) gridkey(x,y,z) end
-- grid and screen vars
local GRID_HEIGHT = 8
local NOTE_WIDTH = 12
local DURATION_1 = 1 / 20
local GRID_FRAMERATE = 1 / 60
local SCREEN_FRAMERATE = 1 / 30
local grid_refresh_metro
local screen_refresh_metro
-- mode vars
local NUM_MODES = 4
local SPEED_MODE = 1
local SLEW_MODE = 2
local SCALE_MODE = 3
local SOUND_MODE = 4
local selected_mode
local add_mode
local remove_mode
-- grid brightness levels vars
local MODE_KEY_OFF = 0
local MODE_KEY_UNSELECTED = 5
local MODE_KEY_SELECTED = 9
local MODE_KEY_FOCUSED = 15
-- env vars
local NUM_ENVS = 7
local envs = {}
local selected_env
-- note vars
local notes = { 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28 }
--- update with mark_eats scales
local transpose = 48
-- CYCLE RELATED STUFF
local function midicps(m)
return (440 / 32) * math.pow(2, (m - 9) / 12)
end
local function draw_cycle(x, stage)
if g then
for y = 1 , GRID_HEIGHT do
g.led(x, y, (y == envs[selected_env].cycles[x].led_pos) and 15 or 0)
end
end
end
local function update_cycle(x, stage)
---set led.po
local h = envs[selected_env].cycles[x].height
local a = (stage-1) % (16-2*h) + 1
envs[selected_env].cycles[x].led_pos = (a <= (9-h) and 1 or 0) * (a + h - 1) + (a > (9-h) and 1 or 0) * (17 - a - h)
local on = envs[selected_env].cycles[x].led_pos == 8
if on then
engine.start(x, midicps(notes[x] + params:get("transpose")))
else
engine.stop(x)
end
end
local function start_cycle(x)
local timer = cycle_metros[x]
timer.time = 8 / params:get("tempo") * envs[selected_env].cycles[x].speed
timer.stage = envs[selected_env].cycles[x].height
timer:start()
timer.callback = function(stage)
update_cycle(x, stage)
draw_cycle(x, stage)
end
envs[selected_env].cycles[x].running = true
envs[selected_env].cycles[x].created = true
end
local function resume_cycle(x)
if envs[selected_env].cycles[x].created then
print (x)
start_cycle(x)
end
end
local function stop_cycle(x)
cycle_metros[x]:stop()
cycle_metros[x].callback = nil
envs[selected_env].cycles[x].running = false
envs[selected_env].cycles[x].created = false
if g then
for y = 1 , GRID_HEIGHT do
g.led(x, y, 0)
end
end
engine.stop(x)
end
local function pause_cycle(x)
if envs[selected_env].cycles[x].created then
stop_cycle(x)
envs[selected_env].cycles[x].created = true
else
stop_cycle(x)
end
end
-- MODE SETTING RELATED STUFF
local function set_env(pressed_env)
for i=1,NOTE_WIDTH do
pause_cycle(i)
end
selected_env = pressed_env
for i=1,NOTE_WIDTH do
resume_cycle(i)
end
end
local function set_grid_led(x, y, l)
if g then g.led(x, y, l) end
end
local function set_unselected_mode_grid_leds()
for i=1,NUM_MODES do
if i ~= selected_mode then
set_grid_led(i + NOTE_WIDTH, 1, MODE_KEY_UNSELECTED)
set_grid_led(i + NOTE_WIDTH, 8, MODE_KEY_UNSELECTED)
end
end
end
local function not_in_setting_mode()
return add_mode == 0 and remove_mode == 0
end
local function unpress_active_mode(y, pressed_mode)
return y == 1 and add_mode == pressed_mode or y == 8 and remove_mode == pressed_mode
end
local function set_mode(x, y, pressed, pressed_mode)
if pressed == 1 then
if pressed_mode ~= selected_mode and not_in_setting_mode() then
selected_mode = pressed_mode
end
if y == 1 and not_in_setting_mode() then
add_mode = pressed_mode
set_grid_led(x, 1, MODE_KEY_FOCUSED)
set_grid_led(x, 8, MODE_KEY_SELECTED)
set_unselected_mode_grid_leds()
elseif y == 8 and not_in_setting_mode() then
remove_mode = pressed_mode
set_grid_led(x, 8, MODE_KEY_FOCUSED)
set_grid_led(x, 1, MODE_KEY_SELECTED)
set_unselected_mode_grid_leds()
end
elseif unpress_active_mode(y, pressed_mode) then
add_mode = 0
remove_mode = 0
set_grid_led(x, 1, MODE_KEY_SELECTED)
set_grid_led(x, 8, MODE_KEY_SELECTED)
set_unselected_mode_grid_leds()
end
end
local function mode_key_action(x, y, pressed)
local pressed_mode = x - NOTE_WIDTH
if y == 1 or y == 8 then
set_mode(x, y, pressed, pressed_mode)
elseif pressed_mode == SOUND_MODE then
set_env(y)
end
end
-- INITIALIZATION
local function init_envs()
cycle_metros = {}
for i=1,NUM_ENVS do
envs[i] = {}
envs[i].cycles = {}
envs[i].current_cycle = 1
end
selected_env = 2
end
local function init_cycles()
for i=1,NOTE_WIDTH do
cycle_metros[i] = metro.alloc()
end
for i=1,NUM_ENVS do
for j=1,NOTE_WIDTH do
envs[i].cycles[j] = { running = false, created = false, keys_pressed = 0, speed = 1, led_pos = 0, height = 0}
end
end
params:add_number("tempo", "tempo", 10, 240, 40)
params:set_action("tempo", function(t)
for i=1,NUM_ENVS do
for j=1,NOTE_WIDTH do
cycle_metros[j].time = 8 / t * envs[i].cycles[j].speed
end
end
end)
end
local function init_params()
params:add_number("transpose", "transpose", 0, 127, 48)
params:add_separator()
params:add_control("legato", "legato", controlspec.new(0, 3, "lin", 0, 0.1, "s"))
params:set_action("legato", function(x) engine.hzLag(x) end)
params:add_control("shape", "shape", controlspec.new(0, 1, "lin", 0, 0, ""))
params:set_action("shape", function(x) engine.shape(x) end)
params:add_control("timbre", "timbre", controlspec.new(0, 1, "lin", 0, 0.5, ""))
params:set_action("timbre", function(x) engine.timbre(x) end)
params:add_control("noise", "noise", controlspec.new(0, 1, "lin", 0, 0, ""))
params:set_action("noise", function(x) engine.noise(x) end)
params:add_control("cut", "cut", controlspec.new(0, 32, "lin", 0, 8, ""))
params:set_action("cut", function(x) engine.cut(x) end)
params:add_control("fgain", "fgain", controlspec.new(0, 6, "lin", 0, 0, ""))
params:set_action("fgain", function(x) engine.fgain(x) end)
params:add_control("cutEnvAmt", "cutEnvAmt", controlspec.new(0, 1, "lin", 0, 0,""))
params:set_action("cutEnvAmt", function(x) engine.cutEnvAmt(x) end)
params:add_control("detune", "detune", controlspec.new(0, 1, "lin", 0, 0, ""))
params:set_action("detune", function(x) engine.detune(x) end)
params:add_control("ampAtk", "ampAtk", controlspec.new(0.01, 10, "lin", 0, 0.05, ""))
params:set_action("ampAtk", function(x) engine.ampAtk(x) end)
params:add_control("ampDec", "ampDec", controlspec.new(0, 2, "lin", 0, 0.1, ""))
params:set_action("ampDec", function(x) engine.ampDec(x) end)
params:add_control("ampSus", "ampSus", controlspec.new(0, 1, "lin", 0, 1, ""))
params:set_action("ampSus", function(x) engine.ampSus(x) end)
params:add_control("ampRel", "ampRel", controlspec.new(0.01, 10, "lin", 0, 1, ""))
params:set_action("ampRel", function(x) engine.ampRel(x) end)
params:add_control("cutAtk", "cutAtk", controlspec.new(0.01, 10, "lin", 0, 0.05, ""))
params:set_action("cutAtk", function(x) engine.cutAtk(x) end)
params:add_control("cutDec", "cutDec", controlspec.new(0, 2, "lin", 0, 0.1, ""))
params:set_action("cutDec", function(x) engine.cutDec(x) end)
params:add_control("cutSus", "cutSus", controlspec.new(0, 1, "lin", 0, 1, ""))
params:set_action("cutSus", function(x) engine.cutSus(x) end)
params:add_control("cutRel", "cutRel", controlspec.new(0.01, 10, "lin", 0, 1, ""))
params:set_action("cutRel", function(x) engine.cutRel(x) end)
end
local function init_grid()
if g then g.all(0) end
grid_refresh_metro = metro.alloc()
print (grid_refresh_metro)
grid_refresh_metro.time = GRID_FRAMERATE
grid_refresh_metro.callback = function(stage)
if g then g:refresh() end
end
grid_refresh_metro:start()
end
local function init_screen()
screen_refresh_metro = metro.alloc()
screen_refresh_metro.time = SCREEN_FRAMERATE
screen_refresh_metro.callback = function(stage)
redraw()
end
screen_refresh_metro:start()
end
local function init_mode()
selected_mode = SOUND_MODE
add_mode = 0
remove_mode = 0
set_grid_led(selected_mode + NOTE_WIDTH, 1, MODE_KEY_SELECTED)
set_grid_led(selected_mode + NOTE_WIDTH, 8, MODE_KEY_SELECTED)
set_unselected_mode_grid_leds()
end
function init()
init_envs()
init_cycles()
init_params()
init_grid()
init_screen()
init_mode()
end
-- UI HANDLERS
function gridkey(x, y, s)
if x >= 0 and x <= NOTE_WIDTH then
if y == GRID_HEIGHT then
if s == 1 then stop_cycle(x) end
return
else
if s == 1 then
envs[selected_env].cycles[x].height = y
envs[selected_env].cycles[x].led_pos = 8-y
start_cycle(x)
--- print("Starting col " .. x .. "height" .. y)
end
end
else
mode_key_action(x, y, s)
end
end
function enc(n, d)
if n == 1 then
mix:delta("output", d)
redraw()
elseif n == 2 then
envs[selected_env].current_cycle = util.clamp(envs[selected_env].current_cycle + d, 1, NOTE_WIDTH)
redraw()
elseif n == 3 then
notes[envs[selected_env].current_cycle] = util.clamp(notes[envs[selected_env].current_cycle] + d, -32, 32)
redraw()
end
end
function key(n, z)
if z == 1 then
if n == 2 then
start_cycle(envs[selected_env].current_cycle)
elseif n == 3 then
stop_cycle(envs[selected_env].current_cycle)
end
end
end
function redraw()
screen.clear()
screen.aa(0)
screen.line_width(1)
screen.level(1)
screen.move(0, 48)
screen.line(128,48)
screen.stroke()
for i=1,NOTE_WIDTH do
local x = (i-1) * 8
local y = 48 - notes[i] - 2
if i == envs[selected_env].current_cycle then
screen.level(1)
screen.move(x+2, 0)
screen.line(x+2, 64)
screen.stroke()
end
if envs[selected_env].cycles[i].running then
screen.level(15)
else
screen.level(i == envs[selected_env].current_cycle and 7 or 2)
end
screen.rect (x, y, 5, 4)
screen.fill()
if envs[selected_env].cycles[i].running then
screen.circle(x+2,y+(envs[selected_env].cycles[i].led_pos) * 4 - 32,2)
screen.fill()
end
end
screen.update()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment