Created
October 9, 2018 04:09
-
-
Save jlmitch5/95e09e0b0ddd3b16f544afbf1ffa349c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- 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