From e2c23ffc28dcb31d035cb1c73cf0b71bac88133d Mon Sep 17 00:00:00 2001 From: Evan Johnson Date: Tue, 9 Dec 2025 11:51:33 -0700 Subject: [PATCH 1/3] add clock.sync offset capability --- lib/clock.c | 3 ++- lib/clock.h | 2 +- lib/lualink.c | 3 ++- lua/clock.lua | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/clock.c b/lib/clock.c index 51cd177a..9f9e2c0b 100644 --- a/lib/clock.c +++ b/lib/clock.c @@ -99,13 +99,14 @@ bool clock_schedule_resume_sleep( int coro_id, float seconds ) return ll_insert_event(&sleep_head, coro_id, wakeup); } -bool clock_schedule_resume_sync( int coro_id, float beats ){ +bool clock_schedule_resume_sync( int coro_id, float beats, float offset ){ double dbeats = beats; // modulo sync time against base beat double awaken = floor(reference.beat / dbeats); awaken *= dbeats; awaken += dbeats; + awaken += (double)offset; // check we haven't already passed it in the sub-beat & add another step if we have // we have to loop because fractional beats values may occur >2 times within a beat diff --git a/lib/clock.h b/lib/clock.h index f39db6be..a2a0be8f 100644 --- a/lib/clock.h +++ b/lib/clock.h @@ -17,7 +17,7 @@ void clock_init( int max_clocks ); void clock_update(uint32_t time_now); bool clock_schedule_resume_sleep( int coro_id, float seconds ); -bool clock_schedule_resume_sync( int coro_id, float beats ); +bool clock_schedule_resume_sync( int coro_id, float beats, float sync_beat_offset ); bool clock_schedule_resume_beatsync( int coro_id, float beats ); void clock_update_reference( double beats, double beat_duration ); void clock_update_reference_from( double beats, double beat_duration, clock_source_t source); diff --git a/lib/lualink.c b/lib/lualink.c index cc6d1578..0316fdb7 100644 --- a/lib/lualink.c +++ b/lib/lualink.c @@ -704,11 +704,12 @@ static int _clock_schedule_sync( lua_State* L ) { int coro_id = (int)luaL_checkinteger(L, 1); float beats = luaL_checknumber(L, 2); + float offset = luaL_optnumber(L, 3, 0); if (beats <= 0) { L_queue_clock_resume(coro_id); // immediate callback } else { - clock_schedule_resume_sync(coro_id, beats); + clock_schedule_resume_sync(coro_id, beats, offset); } lua_pop(L, 2); return 0; diff --git a/lua/clock.lua b/lua/clock.lua index 52ebef2e..058d22eb 100644 --- a/lua/clock.lua +++ b/lua/clock.lua @@ -60,7 +60,7 @@ clock.resume = function(coro_id, ...) return end - local result, mode, time = coroutine.resume(coro, ...) + local result, mode, time, offset = coroutine.resume(coro, ...) if coroutine.status(coro) == 'dead' then if result then @@ -73,7 +73,7 @@ clock.resume = function(coro_id, ...) if mode == 0 then -- SLEEP clock_schedule_sleep(coro_id, time) elseif mode == 1 then -- SYNC - clock_schedule_sync(coro_id, time) + clock_schedule_sync(coro_id, time, offset) elseif mode == 2 then -- BEATSYNC clock_schedule_beat(coro_id, time) end From ce0028bb0c758d9a5f9d2adc3717dac666c2c0d8 Mon Sep 17 00:00:00 2001 From: Evan Johnson Date: Mon, 8 Dec 2025 14:13:33 -0700 Subject: [PATCH 2/3] add clock tempo change handler --- lib/clock.c | 7 +++++++ lib/lualink.c | 17 +++++++++++++++++ lib/lualink.h | 1 + lua/clock.lua | 2 ++ 4 files changed, 27 insertions(+) diff --git a/lib/clock.c b/lib/clock.c index 9f9e2c0b..35ae3062 100644 --- a/lib/clock.c +++ b/lib/clock.c @@ -8,6 +8,7 @@ #include // HAL_GetTick #include "clock_ll.h" // linked list for clocks +#include "caw.h" // Caw_printf /////////////////////////////// // private types @@ -124,10 +125,16 @@ bool clock_schedule_resume_beatsync( int coro_id, float beats ){ void clock_update_reference(double beats, double beat_duration) { + float prev_beat_duration = reference.beat_duration; reference.beat_duration = beat_duration; reference.beat_duration_inverse = (double)1.0 / (double)beat_duration; // for optimized precision_beat_of_now (called every ms) reference.last_beat_time = clock_get_time_seconds(); // seconds since system boot reference.beat = beats; + + // external clock can easily result in fluctuations below this threshold + if(fabsf((float)beat_duration - prev_beat_duration) >= 0.00001){ + L_queue_tempo_change(60.0 * (float)reference.beat_duration_inverse); + } } void clock_update_reference_from(double beats, double beat_duration, clock_source_t source) diff --git a/lib/lualink.c b/lib/lualink.c index 0316fdb7..69e15507 100644 --- a/lib/lualink.c +++ b/lib/lualink.c @@ -64,6 +64,7 @@ void L_handle_peak( event_t* e ); void L_handle_clock_resume( event_t* e ); void L_handle_clock_start( event_t* e ); void L_handle_clock_stop( event_t* e ); +void L_handle_tempo_change( event_t* e ); void L_handle_freq( event_t* e ); void _printf(char* error_message) @@ -1258,3 +1259,19 @@ void L_handle_clock_stop( event_t* e ) lua_pop( L, 1 ); } } + +void L_queue_tempo_change( float tempo ) +{ + event_t e = { .handler = L_handle_tempo_change + , .data.f = tempo + }; + event_post(&e); +} +void L_handle_tempo_change( event_t* e ) +{ + lua_getglobal(L, "tempo_change_handler"); + lua_pushnumber(L, e->data.f); + if( Lua_call_usercode(L, 1, 0) != LUA_OK ){ + lua_pop( L, 1 ); + } +} diff --git a/lib/lualink.h b/lib/lualink.h index 2eca95c5..03afc15a 100644 --- a/lib/lualink.h +++ b/lib/lualink.h @@ -37,6 +37,7 @@ extern void L_queue_ii_followRx( void ); extern void L_queue_clock_resume( int coro_id ); extern void L_queue_clock_start( void ); extern void L_queue_clock_stop( void ); +extern void L_queue_tempo_change( float tempo ); // Callback declarations extern float L_handle_ii_followRxTx( uint8_t cmd, int args, float* data ); diff --git a/lua/clock.lua b/lua/clock.lua index 058d22eb..3b96d165 100644 --- a/lua/clock.lua +++ b/lua/clock.lua @@ -5,6 +5,7 @@ local clock = { threads = {} , transport = {} , id = 0 } +clock.handlers = clock.transport -- piggyback on existing handler table to save space --- create a coroutine to run but do not immediately run it; -- @tparam function f @@ -103,6 +104,7 @@ clock.stop = clock_internal_stop clock_resume_handler = clock.resume function clock_start_handler() if clock.transport.start then clock.transport.start() end end function clock_stop_handler() if clock.transport.stop then clock.transport.stop() end end +function tempo_change_handler(tempo) if clock.handlers.tempo_change then clock.handlers.tempo_change(tempo) end end clock.__newindex = function(self, ix, val) if ix == 'tempo' then clock_internal_set_tempo(val) end From b661e39e96d232dc5091500dbda8aa5032d3c5df Mon Sep 17 00:00:00 2001 From: Evan Johnson Date: Sun, 7 Dec 2025 17:41:39 -0700 Subject: [PATCH 3/3] add clock.time_since_last_input() I needed this function to automatically switch between internal and external clock with a timeout system --- lib/clock.c | 8 ++++++++ lib/clock.h | 1 + lib/lualink.c | 6 ++++++ lua/clock.lua | 2 ++ 4 files changed, 17 insertions(+) diff --git a/lib/clock.c b/lib/clock.c index 35ae3062..45f57ce1 100644 --- a/lib/clock.c +++ b/lib/clock.c @@ -329,3 +329,11 @@ void clock_crow_in_div( float div ) { crow_in_div = 1.0/div; } + +double clock_get_crow_last_time(void) +{ + if( clock_crow_last_time_set == false ){ + return -1.0; // No clock received yet + } + return clock_get_time_seconds() - clock_crow_last_time; +} diff --git a/lib/clock.h b/lib/clock.h index a2a0be8f..2e4208bf 100644 --- a/lib/clock.h +++ b/lib/clock.h @@ -50,3 +50,4 @@ void clock_crow_init(void); void clock_input_handler( int id, float freq ); // Called from Detect lib void clock_crow_handle_clock(void); void clock_crow_in_div( float div ); +double clock_get_crow_last_time(void); diff --git a/lib/lualink.c b/lib/lualink.c index 69e15507..c9b297b1 100644 --- a/lib/lualink.c +++ b/lib/lualink.c @@ -744,6 +744,11 @@ static int _clock_set_source( lua_State* L ) lua_pop(L, 1); return 0; } +static int _clock_get_crow_last_time( lua_State* L ) +{ + lua_pushnumber(L, clock_get_crow_last_time()); + return 1; +} static int _clock_internal_set_tempo( lua_State* L ) { float bpm = luaL_checknumber(L, 1); @@ -867,6 +872,7 @@ static const struct luaL_Reg libCrow[]= , { "clock_get_time_beats" , _clock_get_time_beats } , { "clock_get_tempo" , _clock_get_tempo } , { "clock_set_source" , _clock_set_source } + , { "clock_get_crow_last_time", _clock_get_crow_last_time } // clock.internal , { "clock_internal_set_tempo" , _clock_internal_set_tempo } , { "clock_internal_start" , _clock_internal_start } diff --git a/lua/clock.lua b/lua/clock.lua index 3b96d165..32325a4a 100644 --- a/lua/clock.lua +++ b/lua/clock.lua @@ -96,6 +96,8 @@ end clock.get_beats = clock_get_time_beats clock.get_beat_sec = function(x) return (x or 1) * 60.0 / clock.tempo end +clock.time_since_last_input = clock_get_crow_last_time + clock.start = function(beat) return clock_internal_start(beat or 0) end clock.stop = clock_internal_stop