Skip to content

Instantly share code, notes, and snippets.

@thykka
Created June 20, 2024 09:18
Show Gist options
  • Save thykka/a3cd52b9e559001270d1dd89c977ef84 to your computer and use it in GitHub Desktop.
Save thykka/a3cd52b9e559001270d1dd89c977ef84 to your computer and use it in GitHub Desktop.
PICO-8 Timeline module with Beat sync & drift correction
function init_timeline(state)
tl_scenes={}
tl_state={
time=0,duration=0,
seek_time=0,frame=0,
loop=true,
music_bpm=120.5,
music_sig=4,
music_idx=nil,
fixsync=true,
sync_time=0
}
for k,v in pairs(state) do
tl_state[k]=v
end
return tl_state
end
function set_music(idx,spd)
tl_state.music_idx=idx
music_spd(spd)
end
function music_spd(spd,bars)
tl_state.music_bpm=(7229.50819/(bars or 4))/(spd or 15)
end
function music_beat(time)
return (tl_state.music_bpm/60)*time
end
function music_bar(time)
return (tl_state.music_bpm/60)*time/tl_state.music_sig
end
function bars_to_seconds(bars)
return (bars * tl_state.music_sig * 60) / tl_state.music_bpm
end
function add_scene(sstart,sstop,init,update,draw,unload,start,stop)
add(tl_scenes,{
start=start or sstart and bars_to_seconds(sstart) or 0,
stop=stop or sstop and bars_to_seconds(sstop) or 0,
show=false,time=-1,
init=init,update=update,draw=draw,unload=unload
})
for scn in all(tl_scenes) do
tl_state.duration=max(tl_state.duration,scn.stop)
end
end
function get_time()
return tl_state.seek_time+tl_state.sync_time+t()
end
function local_time(scn)
return (tl_state.time-scn.start)/(scn.stop-scn.start)
end
function seek_time(bars)
local seek_seconds=bars_to_seconds(bars)
tl_state.seek_time+=seek_seconds
local music_pos=music_bar(get_time())%music_bar(tl_state.duration)
if music_pos>63 then
music_pos=63
end
music(music_pos)
end
function update_timeline()
local s=tl_state
s.time=get_time()
s.frame+=1
-- bpm detection
s.music_bar_last=s.music_bar or 0
-- sync correction
s.music_bar=music_bar(s.time)
if s.fixsync then
s.music_bar=stat(54)+stat(56)/256
s.sync_time+=bars_to_seconds(
s.music_bar-music_bar(s.time)
)
s.time=get_time()
end
s.music_bar_active=flr(s.music_bar)!=flr(s.music_bar_last)
s.music_beat_last=s.music_beat or 0
s.music_beat=music_beat(s.time)
s.music_beat_active=flr(s.music_beat)!=flr(s.music_beat_last)
for scn in all(tl_scenes) do
if not scn.show and (s.time>=scn.start)
and s.time<scn.stop then
scn.show=true
if scn.init then scn.init(s) end
elseif scn.show and (s.time>=scn.stop or s.time<scn.start) then
scn.show=false
if scn.unload then scn.unload(s) end
end
if scn.show and scn.update then
s.local_time=local_time(scn)
scn.update(s)
end
end
if s.time>s.duration then
if s.loop then
extcmd('reset')
else
extcmd('shutdown')
end
end
if s.debug then
if(btnp(⬅️)) seek_time(-1)
if(btnp(➡️)) seek_time(1)
end
end
function draw_timeline(s)
local s=tl_state
for scn in all(tl_scenes) do
if scn.show and scn.draw then
scn.draw(local_time(scn),s)
end
end
if (btnp(❎) and btnp(🅾️)) then
s.debug=not s.debug
end
if s.debug then
line(0,127,127,127,1)
line(0,127,flr(s.time/s.duration*127),127,5)
for si,scn in ipairs(tl_scenes) do
line(
flr(scn.start/s.duration*127),126-si,
flr(scn.stop/s.duration*127),126-si,si)
end
print('bar '..flr(s.music_bar)..':'..(
{'-','\\','|','/'})[flr(s.music_beat%4)+1]..'='..s.music_beat,
0,0,2)
print('trk '..(s.seek_time),0,6)
print('snc '..(s.sync_time),0,12)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment