diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-04-13 18:05:51 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-04-13 18:05:51 +0000 |
| commit | a98c602ceaafb67f479e2e917035b8ae443b042e (patch) | |
| tree | fe0587afa529a14e676986bf96bc34f1cb266a48 /abletonlink.c | |
| parent | examples/linkhut: fix phase display (diff) | |
| download | lua-abletonlink-a98c602ceaafb67f479e2e917035b8ae443b042e.tar.gz lua-abletonlink-a98c602ceaafb67f479e2e917035b8ae443b042e.zip | |
implement callbacks
Diffstat (limited to 'abletonlink.c')
| -rw-r--r-- | abletonlink.c | 236 |
1 files changed, 224 insertions, 12 deletions
diff --git a/abletonlink.c b/abletonlink.c index 1e603fa..c4a280e 100644 --- a/abletonlink.c +++ b/abletonlink.c @@ -1,6 +1,7 @@ #include <lua.h> #include <lauxlib.h> #include "link/extensions/abl_link/include/abl_link.h" +#include "tinycthread.h" #if LUA_VERSION_NUM > 501 #define luax_len(L, i) (int) lua_rawlen(L, i) @@ -11,12 +12,88 @@ #define LUA_RIDX_MAINTHREAD 1 #endif +typedef struct { + enum { + EVENT_TYPE_NUM_PEERS, + EVENT_TYPE_TEMPO, + EVENT_TYPE_START_STOP, + } type; + union { + uint64_t num_peers; + double tempo; + bool start_stop; + }; +} event_t; + +#define NUM_EVENTS 64 + +typedef struct { + event_t buffer[NUM_EVENTS]; + event_t *head; + event_t *tail; + event_t *end; + bool full; +} event_queue_t; + +void eq_init(event_queue_t *queue) { + queue->head = queue->buffer; + queue->tail = queue->buffer; + queue->end = queue->buffer + NUM_EVENTS; +} + +static void eq_advance(event_queue_t *queue, event_t **ptr) { + (*ptr)++; + if (*ptr == queue->end) { + *ptr = queue->buffer; + } +} + +void eq_push(event_queue_t *queue, event_t evt) { + if (queue->full) return; + + *queue->head = evt; + + eq_advance(queue, &queue->head); + + if (queue->head == queue->tail) { + queue->full = true; + } +} + +bool eq_pop(event_queue_t *queue, event_t *evt) { + if (!queue->full && queue->head == queue->tail) { + return false; + } + + *evt = *(queue->tail); + eq_advance(queue, &queue->tail); + + return true; +} + +typedef struct { + abl_link link; + + int cb_num_peers; + int cb_tempo; + int cb_start_stop; + + mtx_t mutex; + event_queue_t queue; +} link_state_t; + static int l_create_link(lua_State* L) { double bpm = luaL_checknumber(L, 1); - abl_link* link = (abl_link*)lua_newuserdata(L, sizeof(abl_link)); + link_state_t* state = (link_state_t*)lua_newuserdata(L, sizeof(link_state_t)); - *link = abl_link_create(bpm); + state->link = abl_link_create(bpm); + state->cb_num_peers = LUA_NOREF; + state->cb_tempo = LUA_NOREF; + state->cb_start_stop = LUA_NOREF; + + mtx_init(&state->mutex, mtx_plain); + eq_init(&state->queue); luaL_getmetatable(L, "abletonlink.link"); lua_setmetatable(L, -2); @@ -35,11 +112,16 @@ static int l_create_session_state(lua_State* L) { return 1; } +static link_state_t *checklinkstate(lua_State *L) { + link_state_t* state = (link_state_t*)luaL_checkudata(L, 1, "abletonlink.link"); + luaL_argcheck(L, state != NULL, 1, "`link' expected"); + luaL_argcheck(L, state->link.impl != NULL, 1, "`link' is already destroyed"); + return state; +} + static abl_link checklink(lua_State *L) { - abl_link* link = (abl_link*)luaL_checkudata(L, 1, "abletonlink.link"); - luaL_argcheck(L, link != NULL, 1, "`link' expected"); - luaL_argcheck(L, link->impl != NULL, 1, "`link' is already destroyed"); - return *link; + link_state_t* state = checklinkstate(L); + return state->link; } static abl_link_session_state checksessionstate(lua_State *L, int ud) { @@ -50,12 +132,22 @@ static abl_link_session_state checksessionstate(lua_State *L, int ud) { } static int l_destroy_link(lua_State* L) { - abl_link* link = (abl_link*)luaL_checkudata(L, 1, "abletonlink.link"); - luaL_argcheck(L, link != NULL, 1, "`link' expected"); - if (link->impl) { - abl_link_destroy(*link); - link->impl = NULL; + link_state_t* state = checklinkstate(L); + + if (state->link.impl) { + abl_link_destroy(state->link); + + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_num_peers); + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_tempo); + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_start_stop); + state->cb_num_peers = LUA_NOREF; + state->cb_tempo = LUA_NOREF; + state->cb_start_stop = LUA_NOREF; + + mtx_destroy(&state->mutex); + state->link.impl = NULL; } + return 0; } @@ -125,10 +217,126 @@ static int l_capture_app_session_state(lua_State* L) { static int l_commit_app_session_state(lua_State* L) { abl_link link = checklink(L); abl_link_session_state session_state = checksessionstate(L, 2); - abl_link_commit_app_session_state(link, session_state); + abl_link_commit_app_session_state(link, session_state); + return 0; +} + +static void cb_num_peers(uint64_t num_peers, void *context) { + link_state_t* state = (link_state_t *)context; + event_t evt; + evt.type = EVENT_TYPE_NUM_PEERS; + evt.num_peers = num_peers; + eq_push(&state->queue, evt); +} +static int l_set_num_peers_callback(lua_State* L) { + link_state_t* state = checklinkstate(L); + + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_num_peers); + + if (lua_isnil(L, 2)) { + state->cb_num_peers = LUA_NOREF; + abl_link_set_num_peers_callback(state->link, NULL, NULL); + return 0; + } + + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushvalue(L, 2); + state->cb_num_peers = luaL_ref(L, LUA_REGISTRYINDEX); + abl_link_set_num_peers_callback(state->link, cb_num_peers, state); return 0; } +static void cb_tempo(double tempo, void *context) { + link_state_t* state = (link_state_t *)context; + event_t evt; + evt.type = EVENT_TYPE_TEMPO; + evt.tempo = tempo; + eq_push(&state->queue, evt); +} +static int l_set_tempo_callback(lua_State* L) { + link_state_t* state = checklinkstate(L); + + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_tempo); + + if (lua_isnil(L, 2)) { + state->cb_tempo = LUA_NOREF; + abl_link_set_tempo_callback(state->link, NULL, NULL); + return 0; + } + + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushvalue(L, 2); + state->cb_tempo = luaL_ref(L, LUA_REGISTRYINDEX); + abl_link_set_tempo_callback(state->link, cb_tempo, state); + return 0; +} + +static void cb_start_stop(bool start_stop, void *context) { + link_state_t* state = (link_state_t *)context; + event_t evt; + evt.type = EVENT_TYPE_START_STOP; + evt.start_stop = start_stop; + eq_push(&state->queue, evt); +} +static int l_set_start_stop_callback(lua_State* L) { + link_state_t* state = checklinkstate(L); + + luaL_unref(L, LUA_REGISTRYINDEX, state->cb_start_stop); + + if (lua_isnil(L, 2)) { + state->cb_start_stop = LUA_NOREF; + abl_link_set_start_stop_callback(state->link, NULL, NULL); + return 0; + } + + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushvalue(L, 2); + state->cb_start_stop = luaL_ref(L, LUA_REGISTRYINDEX); + abl_link_set_start_stop_callback(state->link, cb_start_stop, state); + return 0; +} + +static int l_pump_events(lua_State* L) { + link_state_t *state = checklinkstate(L); + + bool was_full = state->queue.full; + state->queue.full = false; + + event_t evt; + while (eq_pop(&state->queue, &evt)) { + mtx_lock(&state->mutex); + + switch (evt.type) { + case EVENT_TYPE_NUM_PEERS: + if (state->cb_num_peers == LUA_NOREF) continue; + + lua_rawgeti(L, LUA_REGISTRYINDEX, state->cb_num_peers); + lua_pushinteger(L, evt.num_peers); + break; + + case EVENT_TYPE_TEMPO: + if (state->cb_tempo == LUA_NOREF) continue; + + lua_rawgeti(L, LUA_REGISTRYINDEX, state->cb_tempo); + lua_pushnumber(L, evt.tempo); + break; + + case EVENT_TYPE_START_STOP: + if (state->cb_start_stop == LUA_NOREF) continue; + + lua_rawgeti(L, LUA_REGISTRYINDEX, state->cb_start_stop); + lua_pushinteger(L, evt.start_stop); + break; + } + + mtx_unlock(&state->mutex); + lua_call(L, 1, 0); + } + + lua_pushboolean(L, was_full); + return 1; +} + static const luaL_Reg abl_link_r[] = { { "destroy", l_destroy_link }, { "is_enabled", l_is_enabled}, @@ -141,6 +349,10 @@ static const luaL_Reg abl_link_r[] = { { "commit_audio_session_state", l_commit_audio_session_state }, { "capture_app_session_state", l_capture_app_session_state }, { "commit_app_session_state", l_commit_app_session_state }, + { "set_num_peers_callback", l_set_num_peers_callback }, + { "set_tempo_callback", l_set_tempo_callback }, + { "set_start_stop_callback", l_set_start_stop_callback }, + { "pump_events", l_pump_events }, { "__gc", l_destroy_link }, { NULL, NULL }, }; |
