Skip to content

Instantly share code, notes, and snippets.

@ypxk
Last active Nov 3, 2020
Embed
What would you like to do?
-- lofi OB4
-- holds the input in softcut in case you need it
-- enc 2 = scrub buffer
-- key 2 = rewind??
-- key 3 = jump to real time
-- arc 1 = scrub buffer
-- arc 2 = not interactive, graphics only
rate = 1 --longer values increase buffer but cost fidelity
local live_playback = 0
local tape_position = 0
local display_tape_position = 0
local buffer_length = 340
local scrub_resolution = .1 * rate
local overwriting = false
local rewinding= false
local relative_buffer_start
local start_time = util.time()
local live = true
local state_change=false
local initital_monitor_level
local initital_reverb_onoff
local initital_compressor_onoff
local ar = arc.connect()
local arcDirty = true
local tau = math.pi * 2
local framerate = 20
function update_positions(i,pos)
--if the buffer runs long enough to loop we want to be able to rewind into the previous buffer up to tape head
if not overwriting then
buffer_check = (util.time() - start_time)
if buffer_check>=buffer_length/rate then
overwriting= true
end
end
if i==1 then
live_playback=(pos)
end
if i==3 and pos > live_playback and not overwriting then
if rewinding then
tape_position = 0
rewinding=false
for i=3,4 do softcut.rate(i,rate) end
else
tape_position = live_playback - .001
end
end
--logic for what voices to hear
if live then
tape_position = live_playback - .001 --untested belief that this address crunchiness
display_tape_position = live_playback
params:set('monitor_level',0)
for x=3,4 do softcut.level(x,0.0) end
elseif not live then
if i==3 then
display_tape_position = pos
tape_position = pos
if rewinding then tape_position = pos end
end
for x=3,4 do softcut.level(x,1.0) end
params:set('monitor_level', -math.huge)
end
redraw()
end
function scrubber(d)
--ban negative numbers
if tape_position < 0 then
if overwriting then
tape_position = buffer_length
else
tape_position = 0.1
end
--ban portions of the loop not yet used
elseif tape_position > live_playback then
if overwriting then
if tape_position > live_playback then
tape_position = tape_position + d*scrub_resolution
end
elseif not overwriting then
live = true
tape_position = live_playback - .002
end
--normal use within buffer
elseif tape_position >= 0 and tape_position < live_playback then
tape_position = tape_position + d*scrub_resolution
end
--updates position
for i=3,4 do
softcut.position(i,tape_position)
end
end
function rewinder()
if rewinding then
--ban negative numbers
if tape_position < 0 then
if overwriting then
tape_position = buffer_length
for i=3,4 do softcut.rate(i,-1.5*rate) end
else
tape_position = 0
for i=3,4 do softcut.rate(i,1*rate) end
rewinding = false
end
--ban portions of the loop not yet used
elseif tape_position > live_playback and not overwriting then
live = true
tape_position = live_playback - .002
rewinding = false
elseif tape_position > 0 and tape_position < live_playback then
for i=3,4 do softcut.rate(i,-1.5*rate) end
end
end
if not rewinding then
for i=3,4 do softcut.rate(i,rate) end
end
end
function enc(n,d)
if n==1 then
elseif n==2 then
if live then
tape_position = display_tape_position
live = false
scrubber(d)
elseif not live then
scrubber(d)
end
elseif n==3 then
end
end
function ar.delta(n, delta)
if n==1 then
if live then
tape_position = display_tape_position
live = false
scrubber(delta/10)
elseif not live then
scrubber(delta/10)
end
end
end
function key(n,z)
relative_buffer_start= util.time()-start_time
if n==1 then
elseif n==2 then
tape_position = display_tape_position-1
if z==1 then
if live then
live = false
end
rewinding = true
rewinder()
elseif z==0 then
rewinding = false
rewinder()
end
elseif n==3 then
if z==1 then
if not live then
live = true
tape_position = live_playback
for i=3,4 do softcut.position(i,tape_position) end
end
end
end
end
function init()
local panning = {-1,1,-1,1}
-- send audio input to softcut input
audio.level_adc_cut(1)
-- Turn off the built-in monitoring, reverb, etc.
initital_monitor_level = params:get('monitor_level')
params:set('monitor_level', -math.huge)
initital_reverb_onoff = params:get('reverb')
params:set('reverb', 1) -- 1 is OFF
initital_compressor_onoff = params:get('compressor')
params:set('compressor', 1) -- 1 is OFF
--recordhead
for i=1,2 do
softcut.enable(i,1)
softcut.buffer(i,i)
softcut.level(i,1.0)
softcut.pan(i,panning[i])
softcut.loop(i,1)
softcut.loop_start(i,0)
softcut.loop_end(i,buffer_length)
softcut.position(i,0)
softcut.rate(i,rate)
softcut.level_input_cut(i,i,1.0)
softcut.level_input_cut(i,i,1.0)
softcut.rec_level(i,1)
softcut.pre_level(i,0)
softcut.rec(i,1)
softcut.phase_quant(i,scrub_resolution)
end
--playhead
for i=3,4 do
softcut.enable(i,1)
softcut.buffer(i,i-2)
softcut.pan(i,panning[i])
softcut.loop(i,1)
softcut.loop_start(i,0)
softcut.loop_end(i,buffer_length)
softcut.position(i,0)
softcut.rate(i,rate)
softcut.pre_level(i,0)
softcut.rec(i,0)
softcut.phase_quant(i,scrub_resolution)
softcut.level(i,0.0)
end
--polls
softcut.event_phase(update_positions)
softcut.poll_start_phase()
local arc_redraw_metro = metro.init()
arc_redraw_metro.event = function()
arc_redraw()
redraw()
end
arc_redraw_metro:start(1 /framerate)
for i = 3,4 do softcut.play(i,1) end
end
function arc_redraw()
local brightness
local tape_led = tape_position/buffer_length
local live_led = live_playback/buffer_length
ar:all(0)
if not overwriting then
ar:segment(2,0,live_led*tau,8)
ar:led(2,math.ceil(live_led*64),15)
ar:led(2,math.ceil(tape_led*64),15)
else
for i=1,64 do ar:led(2,i,8) end
ar:segment(2,tau*live_led+.2,live_led*tau,13)
end
ar:segment(1,tape_led*tau,tau*tape_led+.2,15)
ar:refresh()
end
function redraw()
screen.clear()
screen.move(0,20)
screen.font_size(25)
screen.font_face(8)
if live then
screen.text('LIVE')
else
screen.text('TAPE')
end
screen.font_face(1)
screen.font_size(8)
screen.move(128,25)
screen.text_right('(' .. string.format("%.2f",(buffer_length/rate)/60) ..' minutes)')
screen.font_size(8)
screen.move(5,35)
screen.text('SIZE:')
screen.move(128,35)
screen.text_right(string.format("%.2f",(buffer_length/rate)).. ' seconds')
screen.move(5,45)
screen.text("LIVE: ")
screen.move(128,45)
screen.text_right(string.format("%.2f",live_playback/rate) .. ' seconds')
screen.move(5,55)
screen.text("TAPE: ")
screen.move(128,55)
screen.text_right(string.format("%.2f",display_tape_position/rate).. ' seconds')
screen.update()
end
function cleanup()
-- Put user's audio settings back where they were
params:set('monitor_level', initital_monitor_level)
params:set('reverb', initital_reverb_onoff)
params:set('compressor', initital_compressor_onoff)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment