Skip to content
Permalink
1f4f5d9002
Go to file
 
 
Cannot retrieve contributors at this time
991 lines (926 sloc) 28.6 KB
#include "lib/lualink.h"
#include <string.h> // strcmp(), strlen()
// Lua itself
//#include "../submodules/lua/src/lua.h" // in header
#include "../submodules/lua/src/lauxlib.h"
#include "../submodules/lua/src/lualib.h"
// Hardware IO
#include "lib/slopes.h" // S_toward
#include "lib/ashapes.h" // AShaper_unset_scale(), AShaper_set_scale()
#include "lib/detect.h" // Detect*
#include "lib/caw.h" // Caw_send_*()
#include "lib/ii.h" // ii_*()
#include "lib/bootloader.h" // bootloader_enter()
#include "lib/metro.h" // metro_start() metro_stop() metro_set_time()
#include "lib/io.h" // IO_GetADC()
#include "../ll/random.h" // Random_Get()
#include "../ll/adda.h" // CAL_Recalibrate() CAL_PrintCalibration()
#include "../ll/system.h" // getUID_Word()
#include "lib/events.h" // event_t event_post()
#include "lib/midi.h" // MIDI_Active()
#include "stm32f7xx_hal.h" // HAL_GetTick()
#include "stm32f7xx_it.h" // CPU_GetCount()
// Lua libs wrapped in C-headers: Note the extra '.h'
#include "lua/bootstrap.lua.h" // MUST LOAD THIS MANUALLY FIRST
#include "lua/crowlib.lua.h"
#include "lua/asl.lua.h"
#include "lua/asllib.lua.h"
#include "lua/metro.lua.h"
#include "lua/input.lua.h"
#include "lua/output.lua.h"
#include "lua/ii.lua.h"
#include "build/iihelp.lua.h" // generated lua stub for loading i2c modules
#include "lua/calibrate.lua.h"
#include "lua/midi.lua.h"
#include "build/ii_lualink.h" // generated C header for linking to lua
#define WATCHDOG_FREQ 0x100000 // ~1s how often we run the watchdog
#define WATCHDOG_COUNT 2 // how many watchdogs before 'frozen'
const struct lua_lib_locator Lua_libs[] =
{ { "lua_crowlib" , lua_crowlib }
, { "lua_asl" , lua_asl }
, { "lua_asllib" , lua_asllib }
, { "lua_metro" , lua_metro }
, { "lua_input" , lua_input }
, { "lua_output" , lua_output }
, { "lua_ii" , lua_ii }
, { "build_iihelp" , build_iihelp }
, { "lua_calibrate" , lua_calibrate }
, { "lua_midi" , lua_midi }
, { NULL , NULL }
};
// Basic crow script
#include "lua/First.lua.h"
// Private prototypes
static void Lua_linkctolua( lua_State* L );
static float Lua_check_memory( void );
static int Lua_call_usercode( lua_State* L, int nargs, int nresults );
static int Lua_handle_error( lua_State* L );
static void timeouthook( lua_State* L, lua_Debug* ar );
// Handler prototypes
void L_handle_toward( event_t* e );
void L_handle_metro( event_t* e );
void L_handle_stream( event_t* e );
void L_handle_change( event_t* e );
void L_handle_ii_leadRx( event_t* e );;
void L_handle_ii_followRx( event_t* e );
void L_handle_ii_followRx_cont( uint8_t cmd, int args, float* data );
void L_handle_midi( event_t* e );
void L_handle_window( event_t* e );
void L_handle_in_scale( event_t* e );
void L_handle_volume( event_t* e );
void L_handle_peak( event_t* e );
void _printf(char* error_message)
{
printf("%s\n",error_message);
}
lua_State* L; // global access for 'reset-environment'
// Public functions
lua_State* Lua_Init(void)
{
L = luaL_newstate();
luaL_openlibs(L);
Lua_linkctolua(L);
Lua_eval(L, lua_bootstrap
, strlen(lua_bootstrap)
, "=lib"
); // redefine dofile(), print(), load crowlib
return L;
}
lua_State* Lua_Reset( void )
{
printf("Lua_Reset\n");
Metro_stop_all();
for( int i=0; i<2; i++ ){
Detect_none( Detect_ix_to_p(i) );
}
for( int i=0; i<4; i++ ){
S_toward( i, 0.0, 0.0, SHAPE_Linear, NULL );
}
events_clear();
Lua_DeInit();
return Lua_Init();
}
void Lua_load_default_script( void )
{
Lua_eval(L, lua_First
, strlen(lua_First)
, "=First.lua"
);
}
void Lua_DeInit(void)
{
lua_close(L);
}
// C-fns accessible to lua
// NB these static functions are prefixed with '_'
// to avoid shadowing similar-named extern functions in other modules
// and also to distinguish from extern 'L_' functions.
static int _find_lib( const struct lua_lib_locator* lib, const char* name )
{
uint8_t i = 0;
while( lib[i].addr_of_luacode != NULL ){
if( !strcmp( name, lib[i].name ) ){ // if the strings match
if( luaL_dostring( L, lib[i].addr_of_luacode ) ){
printf("can't load library: %s\n", (char*)lib[i].name );
// lua error
printf( "%s\n", (char*)lua_tostring( L, -1 ) );
lua_pop( L, 1 );
return -1; // error
}
return 1; // table is left on the stack as retval
}
i++;
}
return 0; // not found
}
static int _dofile( lua_State *L )
{
const char* l_name = luaL_checkstring(L, 1);
lua_pop( L, 1 );
switch( _find_lib( Lua_libs, l_name ) ){
case -1: goto fail;
case 1: return 1;
default: break;
}
switch( _find_lib( Lua_ii_libs, l_name ) ){
case -1: goto fail;
case 1: return 1;
default: break;
}
printf("can't find library: %s\n", (char*)l_name);
fail:
lua_pushnil(L);
return 1;
}
static int _debug( lua_State *L )
{
const char* msg = luaL_checkstring(L, 1);
lua_pop( L, 1 );
printf( "%s\n",(char*)msg);
lua_settop(L, 0);
return 0;
}
static int _print_serial( lua_State *L )
{
Caw_send_luachunk( (char*)luaL_checkstring(L, 1) );
lua_pop( L, 1 );
lua_settop(L, 0);
return 0;
}
static int _print_tell( lua_State *L )
{
int nargs = lua_gettop(L);
char teller[60];
// nb: luaL_checkstring() will coerce ints & nums into strings
switch( nargs ){
case 0:
return luaL_error(L, "no event to tell.");
case 1:
sprintf( teller, "^^%s()", luaL_checkstring(L, 1) );
break;
case 2:
sprintf( teller, "^^%s(%s)", luaL_checkstring(L, 1)
, luaL_checkstring(L, 2) );
break;
case 3:
sprintf( teller, "^^%s(%s,%s)", luaL_checkstring(L, 1)
, luaL_checkstring(L, 2)
, luaL_checkstring(L, 3) );
break;
case 4:
sprintf( teller, "^^%s(%s,%s,%s)", luaL_checkstring(L, 1)
, luaL_checkstring(L, 2)
, luaL_checkstring(L, 3)
, luaL_checkstring(L, 4) );
break;
case 5:
sprintf( teller, "^^%s(%s,%s,%s,%s)", luaL_checkstring(L, 1)
, luaL_checkstring(L, 2)
, luaL_checkstring(L, 3)
, luaL_checkstring(L, 4)
, luaL_checkstring(L, 5) );
break;
default:
return luaL_error(L, "too many args to tell.");
}
Caw_send_luachunk( teller );
lua_pop( L, nargs );
lua_settop(L, 0);
return 0;
}
static int _bootloader( lua_State *L )
{
bootloader_enter();
return 0;
}
static int _unique_id( lua_State *L )
{
lua_pushinteger(L, getUID_Word(0));
lua_pushinteger(L, getUID_Word(4));
lua_pushinteger(L, getUID_Word(8));
return 3;
}
static int _time( lua_State *L )
{
lua_pushinteger(L, HAL_GetTick());
return 1;
}
static int _cpu_time( lua_State *L )
{
// returns count of background loops for the last 8ms
lua_pushinteger(L, CPU_GetCount());
return 1;
}
static int _go_toward( lua_State *L )
{
S_toward( luaL_checkinteger(L, 1)-1 // C is zero-based
, luaL_checknumber(L, 2)
, luaL_checknumber(L, 3) * 1000.0
, S_str_to_shape( luaL_checkstring(L, 4) )
, L_queue_toward
);
lua_pop( L, 4 );
lua_settop(L, 0);
return 0;
}
static int _get_state( lua_State *L )
{
float s = S_get_state( luaL_checkinteger(L, 1)-1 );
lua_pop( L, 1 );
lua_pushnumber( L, s );
return 1;
}
static int _set_scale( lua_State *L )
{
int nargs = lua_gettop(L);
// first arg is index!
// special cases:
if( nargs == 1 ){ // no user arguments
float divs[1] = {0.0};
AShaper_set_scale( luaL_checknumber( L, 1 )-1 // index is 1-based in lua
, divs
, 1
, 1
, 1.0/12.0 // hack it not to need the array
);
lua_pop( L, 1 ); // pop index
return 0;
} else if( lua_isstring( L, 2 ) ){ // if arg1 == 'none' -> disable scaling
AShaper_unset_scale( luaL_checknumber( L, 1 )-1 ); // lua is 1-based
lua_pop( L, 2 );
return 0;
}
// arg1 is a list:
// empty list == chromatic
// 12TET semitones based at 0
// just ratios relative to 1/1 in the [1,2) range
int tlen = lua_rawlen( L, 2 ); // length of the table
float divs[tlen];
for( int i=0; i<tlen; i++ ){ // iterate table to get pitch list
lua_pushnumber( L, i+1 ); // lua is 1-based!
lua_gettable( L, 2 ); // table is still in index 2
divs[i] = luaL_checknumber( L, -1 ); // value is now on top of the stack
lua_pop( L, 1 ); // remove our introspected value
}
float mod = 12.0; // default to 12TET
if( nargs >= 3 ){
// TODO allow string = 'just' to select JI mode for note list
mod = luaL_checknumber( L, 3 );
}
float scaling = 1.0; // default to v/8
if( nargs >= 4 ){
scaling = luaL_checknumber( L, 4 );
}
AShaper_set_scale( luaL_checknumber( L, 1 )-1 // index is 1-based in lua
, divs
, tlen
, mod
, scaling
);
lua_pop( L, nargs );
return 0;
}
static int _io_get_input( lua_State *L )
{
float adc = IO_GetADC( luaL_checkinteger(L, 1)-1 );
lua_pop( L, 1 );
lua_pushnumber( L, adc );
return 1;
}
static int _set_input_none( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_none( d );
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 1 );
lua_settop(L, 0);
return 0;
}
static int _set_input_stream( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_stream( d
, L_queue_stream
, luaL_checknumber(L, 2)
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 2 );
lua_settop(L, 0);
return 0;
}
static int _set_input_change( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_change( d
, L_queue_change
, luaL_checknumber(L, 2)
, luaL_checknumber(L, 3)
, Detect_str_to_dir( luaL_checkstring(L, 4) )
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 4 );
lua_settop(L, 0);
return 0;
}
static int _set_input_midi( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
if( ix == 0 ){ // only first channel supports midi
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_none( d );
MIDI_Active( 1 );
}
}
lua_pop( L, 1 );
lua_settop(L, 0);
return 0;
}
static int _set_input_window( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
// capture window table from lua
int wLen = lua_rawlen( L, 2 ); // length of the table
float wins[wLen];
for( int i=0; i<wLen; i++ ){ // iterate table to get windows
lua_pushnumber( L, i+1 ); // lua is 1-based!
lua_gettable( L, 2 ); // table is in index 2
wins[i] = luaL_checknumber( L, -1 ); // value is now on top of the stack
lua_pop( L, 1 ); // remove our introspected value
}
Detect_window( d
, L_queue_window
, wins
, wLen
, luaL_checknumber(L, 3) // hysteresis
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 3 );
return 0;
}
static int _set_input_scale( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
int sLen = lua_rawlen( L, 2 ); // length of the scale table
float scale[sLen];
for( int i=0; i<sLen; i++ ){ // iterate table to get pitch list
lua_pushnumber( L, i+1 ); // lua is 1-based!
lua_gettable( L, 2 ); // table is still in index 2
scale[i] = luaL_checknumber( L, -1 ); // value is now on top of the stack
lua_pop( L, 1 ); // remove our introspected value
}
Detect_scale( d
, L_queue_in_scale
, scale
, sLen
, luaL_checknumber(L, 3) // divs-per-octave
, luaL_checknumber(L, 4) // volts-per-octave
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 4 );
return 0;
}
static int _set_input_volume( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_volume( d
, L_queue_volume
, luaL_checknumber(L, 2)
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 2 );
lua_settop(L, 0);
return 0;
}
static int _set_input_peak( lua_State *L )
{
uint8_t ix = luaL_checkinteger(L, 1)-1;
Detect_t* d = Detect_ix_to_p( ix ); // Lua is 1-based
if(d){ // valid index
Detect_peak( d
, L_queue_peak
, luaL_checknumber(L, 2)
, luaL_checknumber(L, 3)
);
if( ix == 0 ){ MIDI_Active( 0 ); } // deactivate MIDI if first chan
}
lua_pop( L, 3 );
lua_settop(L, 0);
return 0;
}
static int _send_usb( lua_State *L )
{
// pattern match on type: handle values vs strings vs chunk
const char* msg = luaL_checkstring(L, 1);
lua_pop( L, 1 );
uint32_t len = strlen(msg);
Caw_send_raw( (uint8_t*) msg, len );
lua_settop(L, 0);
return 0;
}
static int _ii_list_modules( lua_State *L )
{
Caw_send_luachunk( (char*)ii_list_modules() );
printf( "printing ii help\n" );
return 0;
}
static int _ii_list_commands( lua_State *L )
{
uint8_t address = luaL_checkinteger(L, 1);
printf("i2c help %i\n", address);
Caw_send_luachunk( (char*)ii_list_cmds(address) );
return 0;
}
static int _ii_pullup( lua_State *L )
{
ii_set_pullups( luaL_checkinteger(L, 1) );
return 0;
}
static int _ii_lead( lua_State *L )
{
float data[4] = {0,0,0,0}; // always zero out data
int nargs = lua_gettop(L);
if( nargs > 2
&& nargs <= 6 ){
for( int i=0; i<(nargs-2); i++ ){
data[i] = luaL_checknumber(L, i+3);
}
}
if( ii_leader_enqueue( luaL_checkinteger(L, 1) // address
, luaL_checkinteger(L, 2) // command
, data
) ){ printf("ii_lead failed\n"); }
lua_settop(L, 0);
return 0;
}
static int _ii_address( lua_State *L )
{
ii_set_address( luaL_checkinteger(L, 1) );
lua_pop( L, 1 );
lua_settop(L, 0);
return 0;
}
static int _ii_get_address( lua_State *L )
{
lua_pushinteger( L, ii_get_address() );
return 1;
}
static int _metro_start( lua_State* L )
{
static int idx = 0;
float seconds = -1.0; // metro will re-use previous value
int count = -1; // default: infinite
int stage = 0;
int nargs = lua_gettop(L);
if (nargs > 0) { idx = (int) luaL_checkinteger(L, 1) - 1; } // 1-ix'd
if (nargs > 1) { seconds = (float)luaL_checknumber(L, 2); }
if (nargs > 2) { count = (int)luaL_checkinteger(L, 3); }
if (nargs > 3) { stage = (int)luaL_checkinteger(L, 4) - 1; } // 1-ix'd
lua_pop( L, 4 );
Metro_start( idx, seconds, count, stage );
lua_settop(L, 0);
return 0;
}
static int _metro_stop( lua_State* L )
{
if( lua_gettop(L) != 1 ){ return luaL_error(L, "wrong number of arguments"); }
int idx = (int)luaL_checkinteger(L, 1) - 1; // 1-ix'd
lua_pop( L, 1 );
Metro_stop(idx);
lua_settop(L, 0);
return 0;
}
static int _metro_set_time( lua_State* L )
{
if( lua_gettop(L) != 2 ){ return luaL_error(L, "wrong number of arguments"); }
int idx = (int)luaL_checkinteger(L, 1) - 1; // 1-ix'd
float sec = (float) luaL_checknumber(L, 2);
lua_pop( L, 2 );
Metro_set_time(idx, sec);
lua_settop(L, 0);
return 0;
}
static int _random_float( lua_State* L )
{
lua_pushnumber( L, Random_Float() );
return 1;
}
static int _random_int( lua_State* L )
{
int r = Random_Int( luaL_checknumber(L, 1)
, luaL_checknumber(L, 2) );
lua_pop(L, 2);
lua_pushinteger( L, r);
return 1;
}
static int _calibrate_now( lua_State* L )
{
CAL_Recalibrate( (lua_gettop(L)) ); // if arg present, use defaults
lua_settop(L, 0);
return 0;
}
static int _calibrate_print( lua_State* L )
{
CAL_PrintCalibration();
return 0;
}
// array of all the available functions
static const struct luaL_Reg libCrow[]=
// bootstrap
{ { "c_dofile" , _dofile }
, { "debug_usart" , _debug }
, { "print_serial" , _print_serial }
, { "tell" , _print_tell }
// system
, { "sys_bootloader" , _bootloader }
, { "unique_id" , _unique_id }
, { "time" , _time }
, { "cputime" , _cpu_time }
//, { "sys_cpu_load" , _sys_cpu }
// io
, { "go_toward" , _go_toward }
, { "get_state" , _get_state }
, { "set_output_scale" , _set_scale }
, { "io_get_input" , _io_get_input }
, { "set_input_none" , _set_input_none }
, { "set_input_stream" , _set_input_stream }
, { "set_input_change" , _set_input_change }
, { "set_input_midi" , _set_input_midi }
, { "set_input_scale" , _set_input_scale }
, { "set_input_window" , _set_input_window }
, { "set_input_volume" , _set_input_volume }
, { "set_input_peak" , _set_input_peak }
// usb
, { "send_usb" , _send_usb }
// i2c
, { "ii_list_modules" , _ii_list_modules }
, { "ii_list_commands" , _ii_list_commands }
, { "ii_pullup" , _ii_pullup }
, { "ii_lead" , _ii_lead }
, { "ii_set_add" , _ii_address }
, { "ii_get_add" , _ii_get_address }
// metro
, { "metro_start" , _metro_start }
, { "metro_stop" , _metro_stop }
, { "metro_set_time" , _metro_set_time }
// random
, { "random_float" , _random_float }
, { "random_int" , _random_int }
// calibration
, { "calibrate_now" , _calibrate_now }
, { "calibrate_print" , _calibrate_print }
, { NULL , NULL }
};
// make functions available to lua
static void Lua_linkctolua( lua_State *L )
{
// Make C fns available to Lua
uint8_t fn = 0;
while( libCrow[fn].func != NULL ){
lua_register( L, libCrow[fn].name, libCrow[fn].func );
fn++;
}
}
uint8_t Lua_eval( lua_State* L
, const char* script
, size_t script_len
, const char* chunkname
){
int error = luaL_loadbuffer( L, script, script_len, chunkname );
if( error != LUA_OK ){
Caw_send_luachunk( (char*)lua_tostring( L, -1 ) );
lua_pop( L, 1 );
return 1;
}
if( (error |= Lua_call_usercode( L, 0, 0 )) != LUA_OK ){
lua_pop( L, 1 );
switch( error ){
case LUA_ERRSYNTAX: Caw_send_luachunk("syntax error."); break;
case LUA_ERRMEM: Caw_send_luachunk("not enough memory."); break;
case LUA_ERRRUN: Caw_send_luachunk("runtime error."); break;
case LUA_ERRERR: Caw_send_luachunk("error in error handler."); break;
default: break;
}
return 1;
}
return 0;
}
static float Lua_check_memory( void )
{
lua_getglobal(L,"collectgarbage");
lua_pushstring(L, "count");
lua_pcall(L,1,1,0); // NOT PROTECTED (called from watchdog)
float mem = luaL_checknumber(L, 1);
lua_pop(L,1);
return mem;
}
void Lua_crowbegin( void )
{
printf("init()\n"); // call in C to avoid user seeing in lua
lua_getglobal(L,"init");
if( Lua_call_usercode(L,0,0) != LUA_OK ){
lua_pop(L, 1);
}
}
// Watchdog timer for infinite looped Lua scripts
volatile int watchdog = WATCHDOG_COUNT;
static void timeouthook( lua_State* L, lua_Debug* ar )
{
if( --watchdog <= 0 ){
Caw_send_luachunk("CPU timed out.");
lua_sethook(L, timeouthook, LUA_MASKLINE, 0); // error until top
luaL_error(L, "user code timeout exceeded");
}
}
static int Lua_handle_error( lua_State *L )
{
const char *msg = lua_tostring( L, 1 );
if( msg == NULL ){
if( luaL_callmeta( L, 1, "__tostring" )
&& lua_type ( L, -1 ) == LUA_TSTRING ) {
return 1;
} else {
msg = lua_pushfstring( L
, "(error object is a %s value)"
, luaL_typename( L, 1 ) );
}
}
luaL_traceback( L, L, msg, 1 );
char* traceback = (char*)lua_tostring( L, -1 );
Caw_send_luachunk( traceback );
_printf( traceback );
return 1;
}
static int Lua_call_usercode( lua_State* L, int nargs, int nresults )
{
lua_sethook(L, timeouthook, LUA_MASKCOUNT, WATCHDOG_FREQ); // reset timeout hook
watchdog = WATCHDOG_COUNT; // reset timeout hook counter
int errFunc = lua_gettop(L) - nargs;
lua_pushcfunction( L, Lua_handle_error );
lua_insert( L, errFunc );
int status = lua_pcall(L, nargs, nresults, errFunc);
lua_remove( L, errFunc );
lua_sethook(L, timeouthook, 0, 0);
return status;
}
// Public Callbacks from C to Lua
void L_queue_toward( int id )
{
event_t e = { .handler = L_handle_toward
, .index.i = id
};
event_post(&e);
}
void L_handle_toward( event_t* e )
{
lua_getglobal(L, "toward_handler");
lua_pushinteger(L, e->index.i + 1); // 1-ix'd
if( Lua_call_usercode(L, 1, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_metro( int id, int state )
{
event_t e = { .handler = L_handle_metro
, .index.i = id
, .data.i = state
};
event_post(&e);
}
void L_handle_metro( event_t* e )
{
lua_getglobal(L, "metro_handler");
lua_pushinteger(L, e->index.i +1); // 1-ix'd
lua_pushinteger(L, e->data.i +1); // 1-ix'd
if( Lua_call_usercode(L, 2, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_stream( int id, float state )
{
event_t e = { .handler = L_handle_stream
, .index.i = id
, .data.f = state
};
event_post(&e);
}
void L_handle_stream( event_t* e )
{
lua_getglobal(L, "stream_handler");
lua_pushinteger(L, e->index.i +1); // 1-ix'd
lua_pushnumber(L, e->data.f);
if( Lua_call_usercode(L, 2, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_change( int id, float state )
{
event_t e = { .handler = L_handle_change
, .index.i = id
, .data.f = state
};
event_post(&e);
}
void L_handle_change( event_t* e )
{
lua_getglobal(L, "change_handler");
lua_pushinteger(L, e->index.i +1); // 1-ix'd
lua_pushnumber(L, e->data.f);
if( Lua_call_usercode(L, 2, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_ii_leadRx( uint8_t address, uint8_t cmd, float data, uint8_t arg )
{
event_t e = { .handler = L_handle_ii_leadRx
, .data.f = data
};
e.index.u8s[0] = address;
e.index.u8s[1] = cmd;
e.index.u8s[2] = arg;
event_post(&e);
}
void L_handle_ii_leadRx( event_t* e )
{
lua_getglobal(L, "ii_LeadRx_handler");
lua_pushinteger(L, e->index.u8s[0]); // address
lua_pushinteger(L, e->index.u8s[1]); // command
lua_pushinteger(L, e->index.u8s[2]); // arg
lua_pushnumber(L, e->data.f);
if( Lua_call_usercode(L, 4, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_ii_followRx( void )
{
event_t e = { .handler = L_handle_ii_followRx };
event_post(&e);
}
void L_handle_ii_followRx( event_t* e )
{
// FIXME: should pass the 'cont' as a fnptr continuation
ii_process_dequeue_decode();
}
void L_handle_ii_followRx_cont( uint8_t cmd, int args, float* data )
{
lua_getglobal(L, "ii_followRx_handler");
lua_pushinteger(L, cmd);
int a = args;
while(a-- > 0){
lua_pushnumber(L, *data++);
}
if( Lua_call_usercode(L, 1+args, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
// FIXME called directly from ii lib for now
float L_handle_ii_followRxTx( uint8_t cmd, int args, float* data )
{
lua_getglobal(L, "ii_followRxTx_handler");
lua_pushinteger(L, cmd);
int a = args;
while(a-- > 0){
lua_pushnumber(L, *data++);
}
if( Lua_call_usercode(L, 1+args, 1) != LUA_OK ){
lua_pop( L, 1 );
}
float n = luaL_checknumber(L, 1);
lua_pop( L, 1 );
return n;
}
void L_queue_midi( uint8_t* data )
{
event_t e = { .handler = L_handle_midi };
e.data.u8s[0] = data[0];
e.data.u8s[1] = data[1];
e.data.u8s[2] = data[2];
event_post(&e);
}
void L_handle_midi( event_t* e )
{
uint8_t* data = e->data.u8s;
lua_getglobal(L, "midi_handler");
int count = MIDI_byte_count(data[0]) + 1; // +1 for cmd byte itself
for( int i=0; i<count; i++ ){
lua_pushinteger(L, data[i]);
}
if( Lua_call_usercode(L, count, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_in_scale( int id, float note )
{
event_t e = { .handler = L_handle_in_scale
, .index.i = id
};
event_post(&e);
}
void L_handle_in_scale( event_t* e )
{
lua_getglobal(L, "scale_handler");
Detect_t* d = Detect_ix_to_p( e->index.i );
// TODO these should be wrapped in a table here rather than lua
lua_pushinteger(L, e->index.i +1); // 1-ix'd
lua_pushinteger(L, d->scale.lastIndex +1); // 1-ix'd
lua_pushinteger(L, d->scale.lastOct);
lua_pushnumber(L, d->scale.lastNote);
lua_pushnumber(L, d->scale.lastVolts);
if( Lua_call_usercode(L, 5, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_window( int id, float window )
{
event_t e = { .handler = L_handle_window
, .index.i = id
};
if( window >= 0.0 ){
e.data.u8s[0] = window;
e.data.u8s[1] = 1;
} else {
e.data.u8s[0] = -window; // flip sign for positive index
e.data.u8s[1] = 0;
}
event_post(&e);
}
void L_handle_window( event_t* e )
{
lua_getglobal(L, "window_handler");
lua_pushinteger(L, e->index.i+1); // 1-ix'd
lua_pushinteger(L, e->data.u8s[0]);
lua_pushnumber(L, e->data.u8s[1]);
if( Lua_call_usercode(L, 3, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_volume( int id, float level )
{
event_t e = { .handler = L_handle_volume
, .index.i = id
, .data.f = level
};
event_post(&e);
}
void L_handle_volume( event_t* e )
{
lua_getglobal(L, "volume_handler");
lua_pushinteger(L, e->index.i+1); // 1-ix'd
lua_pushnumber(L, e->data.f);
if( Lua_call_usercode(L, 2, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}
void L_queue_peak( int id, float ignore )
{
event_t e = { .handler = L_handle_peak
, .index.i = id
};
event_post(&e);
}
void L_handle_peak( event_t* e )
{
lua_getglobal(L, "peak_handler");
lua_pushinteger(L, e->index.i +1); // 1-ix'd
if( Lua_call_usercode(L, 1, 0) != LUA_OK ){
lua_pop( L, 1 );
}
}