glut-64
| -- glut64 | |
| -- | |
| -- granular sampler in progress | |
| -- (currently requires a grid) | |
| -- | |
| -- trigger voices | |
| -- using grid rows 2-8 | |
| -- | |
| -- mute voices and record | |
| -- patterns using grid row 1 | |
| -- | |
| -- 64 edition by @cfd90 | |
| engine.name = 'Glut' | |
| local g = grid.connect() | |
| local VOICES = 6 | |
| local positions = {} | |
| local gates = {} | |
| local voice_levels = {} | |
| for i=1, VOICES do | |
| positions[i] = -1 | |
| gates[i] = 0 | |
| voice_levels[i] = 0 | |
| end | |
| local gridbuf = require 'lib/gridbuf' | |
| local grid_ctl = gridbuf.new(8, 8) | |
| local grid_voc = gridbuf.new(8, 8) | |
| local metro_grid_refresh | |
| local metro_blink | |
| --[[ | |
| recorder | |
| ]] | |
| local pattern_banks = {} | |
| local pattern_timers = {} | |
| local pattern_leds = {} -- for displaying button presses | |
| local pattern_positions = {} -- playback positions | |
| local record_bank = -1 | |
| local record_prevtime = -1 | |
| local record_length = -1 | |
| local alt = false | |
| local blink = 0 | |
| local metro_blink | |
| local function record_event(x, y, z) | |
| if record_bank > 0 then | |
| -- record first event tick | |
| local current_time = util.time() | |
| if record_prevtime < 0 then | |
| record_prevtime = current_time | |
| end | |
| local time_delta = current_time - record_prevtime | |
| table.insert(pattern_banks[record_bank], {time_delta, x, y, z}) | |
| record_prevtime = current_time | |
| end | |
| end | |
| local function start_playback(n) | |
| pattern_timers[n]:start(0.001, 1) -- TODO: timer doesn't start immediately with zero | |
| end | |
| local function stop_playback(n) | |
| pattern_timers[n]:stop() | |
| pattern_positions[n] = 1 | |
| end | |
| local function arm_recording(n) | |
| record_bank = n | |
| end | |
| local function stop_recording() | |
| local recorded_events = #pattern_banks[record_bank] | |
| if recorded_events > 0 then | |
| -- save last delta to first event | |
| local current_time = util.time() | |
| local final_delta = current_time - record_prevtime | |
| pattern_banks[record_bank][1][1] = final_delta | |
| start_playback(record_bank) | |
| end | |
| record_bank = -1 | |
| record_prevtime = -1 | |
| end | |
| local function pattern_next(n) | |
| local bank = pattern_banks[n] | |
| local pos = pattern_positions[n] | |
| local event = bank[pos] | |
| local delta, x, y, z = table.unpack(event) | |
| pattern_leds[n] = z | |
| grid_key(x, y, z, true) | |
| local next_pos = pos + 1 | |
| if next_pos > #bank then | |
| next_pos = 1 | |
| end | |
| local next_event = bank[next_pos] | |
| local next_delta = next_event[1] | |
| pattern_positions[n] = next_pos | |
| -- schedule next event | |
| pattern_timers[n]:start(next_delta, 1) | |
| end | |
| local function record_handler(n) | |
| if alt then | |
| -- clear pattern | |
| if n == record_bank then stop_recording() end | |
| if pattern_timers[n].is_running then stop_playback(n) end | |
| pattern_banks[n] = {} | |
| do return end | |
| end | |
| if n == record_bank then | |
| -- stop if pressed current recording | |
| stop_recording() | |
| else | |
| local pattern = pattern_banks[n] | |
| if #pattern > 0 then | |
| -- toggle playback if there's data | |
| if pattern_timers[n].is_running then stop_playback(n) else start_playback(n) end | |
| else | |
| -- stop recording if it's happening | |
| if record_bank > 0 then | |
| stop_recording() | |
| end | |
| -- arm new pattern for recording | |
| arm_recording(n) | |
| end | |
| end | |
| end | |
| --[[ | |
| internals | |
| ]] | |
| local function display_voice(phase, width) | |
| local pos = phase * width | |
| local levels = {} | |
| for i = 1, width do levels[i] = 0 end | |
| local left = math.floor(pos) | |
| local index_left = left + 1 | |
| local dist_left = math.abs(pos - left) | |
| local right = math.floor(pos + 1) | |
| local index_right = right + 1 | |
| local dist_right = math.abs(pos - right) | |
| if index_left < 1 then index_left = width end | |
| if index_left > width then index_left = 1 end | |
| if index_right < 1 then index_right = width end | |
| if index_right > width then index_right = 1 end | |
| levels[index_left] = math.floor(math.abs(1 - dist_left) * 15) | |
| levels[index_right] = math.floor(math.abs(1 - dist_right) * 15) | |
| return levels | |
| end | |
| local function start_voice(voice, pos) | |
| engine.seek(voice, pos) | |
| engine.gate(voice, 1) | |
| gates[voice] = 1 | |
| end | |
| local function stop_voice(voice) | |
| gates[voice] = 0 | |
| engine.gate(voice, 0) | |
| end | |
| local function grid_refresh() | |
| if g == nil then | |
| return | |
| end | |
| grid_ctl:led_level_all(0) | |
| grid_voc:led_level_all(0) | |
| -- alt | |
| grid_ctl:led_level_set(8, 1, alt and 15 or 1) | |
| -- pattern banks | |
| for i=1, VOICES do | |
| local level = 2 | |
| if #pattern_banks[i] > 0 then level = 5 end | |
| if pattern_timers[i].is_running then | |
| level = 15 | |
| if pattern_leds[i] > 0 then | |
| level = 1 | |
| end | |
| end | |
| grid_ctl:led_level_set(i, 2, level) | |
| end | |
| -- blink armed pattern | |
| if record_bank > 0 then | |
| grid_ctl:led_level_set(7, 1, 15 * blink) | |
| end | |
| -- voices | |
| for i=1, VOICES do | |
| if voice_levels[i] > 0 then | |
| grid_ctl:led_level_set(i, 1, math.min(math.ceil(voice_levels[i] * 15), 15)) | |
| grid_voc:led_level_row(1, i + 2, display_voice(positions[i], 8)) | |
| end | |
| end | |
| local buf = grid_ctl | grid_voc | |
| buf:render(g) | |
| g:refresh() | |
| end | |
| function grid_key(x, y, z, skip_record) | |
| if y > 2 or (y == 1 and x < 9) then | |
| if not skip_record then | |
| record_event(x, y, z) | |
| end | |
| end | |
| if z > 0 then | |
| -- set voice pos | |
| if y > 2 then | |
| local voice = y - 2 | |
| start_voice(voice, (x - 1) / 8) | |
| else | |
| if y == 1 then | |
| if x == 8 then | |
| -- alt | |
| alt = true | |
| elseif x <= 6 then | |
| -- stop | |
| local voice = x | |
| stop_voice(voice) | |
| end | |
| else | |
| if x <= 6 then | |
| -- pattern recorder | |
| record_handler(x) | |
| end | |
| end | |
| end | |
| else | |
| -- alt | |
| if x == 8 and y == 1 then alt = false end | |
| end | |
| end | |
| function init() | |
| g.key = function(x, y, z) | |
| grid_key(x, y, z) | |
| end | |
| -- polls | |
| for v = 1, VOICES do | |
| local phase_poll = poll.set('phase_' .. v, function(pos) positions[v] = pos end) | |
| phase_poll.time = 0.05 | |
| phase_poll:start() | |
| local level_poll = poll.set('level_' .. v, function(lvl) voice_levels[v] = lvl end) | |
| level_poll.time = 0.05 | |
| level_poll:start() | |
| end | |
| -- recorders | |
| for v = 1, VOICES do | |
| table.insert(pattern_timers, metro.init(function(tick) pattern_next(v) end)) | |
| table.insert(pattern_banks, {}) | |
| table.insert(pattern_leds, 0) | |
| table.insert(pattern_positions, 1) | |
| end | |
| -- grid refresh timer, 40 fps | |
| metro_grid_refresh = metro.init(function(stage) grid_refresh() end, 1 / 40) | |
| metro_grid_refresh:start() | |
| metro_blink = metro.init(function(stage) blink = blink ~ 1 end, 1 / 4) | |
| metro_blink:start() | |
| local sep = ": " | |
| params:add_taper("reverb_mix", "*"..sep.."mix", 0, 100, 50, 0, "%") | |
| params:set_action("reverb_mix", function(value) engine.reverb_mix(value / 100) end) | |
| params:add_taper("reverb_room", "*"..sep.."room", 0, 100, 50, 0, "%") | |
| params:set_action("reverb_room", function(value) engine.reverb_room(value / 100) end) | |
| params:add_taper("reverb_damp", "*"..sep.."damp", 0, 100, 50, 0, "%") | |
| params:set_action("reverb_damp", function(value) engine.reverb_damp(value / 100) end) | |
| for v = 1, VOICES do | |
| params:add_separator() | |
| params:add_file(v.."sample", v..sep.."sample") | |
| params:set_action(v.."sample", function(file) engine.read(v, file) end) | |
| params:add_taper(v.."volume", v..sep.."volume", -60, 20, 0, 0, "dB") | |
| params:set_action(v.."volume", function(value) engine.volume(v, math.pow(10, value / 20)) end) | |
| params:add_taper(v.."speed", v..sep.."speed", -200, 200, 100, 0, "%") | |
| params:set_action(v.."speed", function(value) engine.speed(v, value / 100) end) | |
| params:add_taper(v.."jitter", v..sep.."jitter", 0, 500, 0, 5, "ms") | |
| params:set_action(v.."jitter", function(value) engine.jitter(v, value / 1000) end) | |
| params:add_taper(v.."size", v..sep.."size", 1, 500, 100, 5, "ms") | |
| params:set_action(v.."size", function(value) engine.size(v, value / 1000) end) | |
| params:add_taper(v.."density", v..sep.."density", 0, 512, 20, 6, "hz") | |
| params:set_action(v.."density", function(value) engine.density(v, value) end) | |
| params:add_taper(v.."pitch", v..sep.."pitch", -24, 24, 0, 0, "st") | |
| params:set_action(v.."pitch", function(value) engine.pitch(v, math.pow(0.5, -value / 12)) end) | |
| params:add_taper(v.."spread", v..sep.."spread", 0, 100, 0, 0, "%") | |
| params:set_action(v.."spread", function(value) engine.spread(v, value / 100) end) | |
| params:add_taper(v.."fade", v..sep.."att / dec", 1, 9000, 1000, 3, "ms") | |
| params:set_action(v.."fade", function(value) engine.envscale(v, value / 1000) end) | |
| end | |
| params:bang() | |
| end | |
| --[[ | |
| exports | |
| ]] | |
| function enc(n, d) | |
| end | |
| function key(n, z) | |
| end | |
| function redraw() | |
| -- do return end | |
| screen.clear() | |
| screen.level(15) | |
| screen.move(0, 10) | |
| screen.text("^ load samples") | |
| screen.move(0, 20) | |
| screen.text(" via menu > parameters") | |
| screen.update() | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment