-- *transit authority* -- SuperCollider engine study 3 -- monome.org engine.name = "FXBusDemo" local formatters = require("formatters") -- NEW: add LFO for additional movement: local lfo = require("lib/lfo") -- NEW: add sequins to sequence hz values _s = require("sequins") hz_vals = _s({ 300, 400, 400 / 3, 100, 300 / 2, 300 / 1.5 }) random_offset = { 0.5, 1.5, 2, 3, 1, 0.75 } -- NEW: add screen redraw variables local bright = 1 local rad = 2 local screen_dirty = true local hz = 330 function clock.tempo_change_handler(x) engine.set_delay_time(clock.get_beat_sec() / 2) end function init() -- NEW: invoke our brightness poll // brightness = poll.set("brightness_poll") brightness.callback = function(val) bright = util.round(util.linlin(20, 20000, 1, 15, val)) screen_dirty = true end brightness.time = 1 / 60 brightness:start() -- // brightness poll -- NEW: invoke our amp poll // amp = poll.set("amp_poll") amp.callback = function(val) rad = util.round(util.linlin(0, 1, 2, 120, val)) screen_dirty = true end amp.time = 1 / 30 amp:start() -- // amp poll -- NEW: redraw at 60fps // redraw_timer = metro.init(function() if screen_dirty then redraw() screen_dirty = false end end, 1 / 60, -1) redraw_timer:start() -- // redraw -- NEW: synth controls // params:add({ type = "separator", id = "synth_separator", name = "synth", }) params:add({ type = "control", id = "fchz", name = "filter hz", controlspec = controlspec.FREQ, action = function(x) if fchzLFO.enabled == 0 then engine.set_synth("fchz", x) end fchz_raw = params:get_raw("fchz") end, }) -- // synth controls local cs_amp = controlspec.new(0, 2, "lin", 0.001, 1, nil, 1 / 200) local cs_fc1 = controlspec.new(20, 20000, "exp", 0, 600, "Hz") local cs_fc2 = controlspec.new(20, 20000, "exp", 0, 1800, "Hz") local cs_pan = controlspec.new(-1, 1, "lin", 0.001, 0, nil, 1 / 200) local frm_percent = function(param) return ((param:get() * 100) .. "%") end params:add({ type = "separator", id = "levels_separator", name = "levels", }) params:add({ type = "control", id = "dry_level", name = "dry level", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_level("dry", x) end, }) params:add({ type = "control", id = "delay_level", name = "delay level", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_level("delay_send", x) end, }) params:add({ type = "control", id = "reverb_level", name = "reverb level", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_level("reverb_send", x) end, }) params:add({ type = "separator", id = "pan_separator", name = "panning", }) params:add({ type = "control", id = "dry_pan", name = "dry", controlspec = cs_pan, formatter = formatters.bipolar_as_pan_widget, action = function(x) engine.set_pan("dry", x) end, }) params:add({ type = "control", id = "delay_pan", name = "delay", controlspec = cs_pan, formatter = formatters.bipolar_as_pan_widget, action = function(x) engine.set_pan("delay_send", x) end, }) params:add({ type = "control", id = "reverb_pan", name = "reverb", controlspec = cs_pan, formatter = formatters.bipolar_as_pan_widget, action = function(x) engine.set_pan("reverb_send", x) end, }) params:add({ type = "separator", id = "main_eq_separator", name = "main EQ", }) params:add({ type = "control", id = "eq_lo", name = "lo", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_main("ampLo", x) end, }) params:add({ type = "control", id = "eq_mid", name = "mid", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_main("ampMid", x) end, }) params:add({ type = "control", id = "eq_hi", name = "hi", controlspec = cs_amp, formatter = frm_percent, action = function(x) engine.set_main("ampHi", x) end, }) params:add({ type = "control", id = "fc1", name = "lo freq", controlspec = cs_fc1, action = function(x) engine.set_main("fc1", x) end, }) params:add({ type = "control", id = "fc2", name = "hi freq", controlspec = cs_fc2, action = function(x) engine.set_main("fc2", x) end, }) -- NEW: add 'fchz' LFO fchz_spec = params:lookup_param("fchz").controlspec fchzLFO = lfo:add({ shape = "sine", -- shape min = -1, -- min max = 1, -- max depth = 0.6, -- depth (0 to 1) mode = "clocked", -- mode period = 1 / 3, -- period (in 'clocked' mode, represents 4/4 bars) baseline = "center", action = function() engine.set_synth("fchz", calculate_bipolar_lfo_movement(fchzLFO, "fchz")) end, }) fchzLFO:add_params("myLFO", "lfo") params:hide("lfo_min_myLFO") params:hide("lfo_max_myLFO") _menu.rebuild_params() startup_actions = clock.run(function() clock.sleep(0.1) params:set("delay_level", 1) params:set("reverb_level", 0) params:set("fchz", 1500) params:bang() -- NEW: sequins clock sequence = clock.run(function() while true do engine.set_synth("hz", hz_vals() * random_offset[math.random(#random_offset)]) clock.sync(1 / 4) end end) fchzLFO:start() -- start our LFO, complements ':stop()' end) end function calculate_bipolar_lfo_movement(lfoID, paramID) if lfoID:get("depth") > 0 then return fchz_spec:map(lfoID:get("scaled") / 2 + fchz_raw) else return fchz_spec:map(fchz_raw) end end -- NEW: draw to screen // function redraw() screen.clear() screen.level(bright) screen.circle(64, 32, rad) screen.fill() screen.update() end -- // draw to screen