From b618c4a6b74a52f830ca53029e1cc680d56a2501 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 6 Jul 2019 17:25:12 +0200 Subject: Implement Lua backend --- backends/lua.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 backends/lua.c (limited to 'backends/lua.c') diff --git a/backends/lua.c b/backends/lua.c new file mode 100644 index 0000000..ae2d460 --- /dev/null +++ b/backends/lua.c @@ -0,0 +1,202 @@ +#include +#include "lua.h" + +#define BACKEND_NAME "lua" +#define LUA_REGISTRY_KEY "_midimonster_lua_instance" + +//TODO instance identification for callvacks + +int init(){ + backend lua = { + .name = BACKEND_NAME, + .conf = lua_configure, + .create = lua_instance, + .conf_instance = lua_configure_instance, + .channel = lua_channel, + .handle = lua_set, + .process = lua_handle, + .start = lua_start, + .shutdown = lua_shutdown + }; + + //register backend + if(mm_backend_register(lua)){ + fprintf(stderr, "Failed to register lua backend\n"); + return 1; + } + return 0; +} + +static int lua_callback_output(lua_State* interpreter){ + int arguments = lua_gettop(interpreter); + size_t n; + channel_value val; + const char* channel_name = NULL; + channel* channel = NULL; + instance* inst = NULL; + lua_instance_data* data = NULL; + + if(arguments != 2){ + fprintf(stderr, "Lua output function called with %d arguments, expected 2\n", arguments); + return 0; + } + + channel_name = lua_tostring(interpreter, 1); + val.normalised = clamp(lua_tonumber(interpreter, 2), 1.0, 0.0); + + lua_pushstring(interpreter, LUA_REGISTRY_KEY); + lua_gettable(interpreter, LUA_REGISTRYINDEX); + inst = (instance *) lua_touserdata(interpreter, -1); + data = (lua_instance_data*) inst->impl; + + for(n = 0; n < data->channels; n++){ + if(!strcmp(channel_name, data->channel_name[n])){ + channel = mm_channel(inst, n, 0); + if(!channel){ + return 0; + } + mm_channel_event(channel, val); + return 0; + } + } + + fprintf(stderr, "Tried to set unknown channel %s.%s\n", inst->name, channel_name); + return 0; +} + +static int lua_configure(char* option, char* value){ + fprintf(stderr, "The lua backend does not take any global configuration\n"); + return 1; +} + +static int lua_configure_instance(instance* inst, char* option, char* value){ + lua_instance_data* data = (lua_instance_data*) inst->impl; + + if(!strcmp(option, "script")){ + if(luaL_dofile(data->interpreter, value)){ + fprintf(stderr, "Failed to load lua source file %s for instance %s: %s\n", value, inst->name, lua_tostring(data->interpreter, -1)); + return 1; + } + return 0; + } + + fprintf(stderr, "Unknown configuration parameter %s for lua backend\n", option); + return 1; +} + +static instance* lua_instance(){ + instance* inst = mm_instance(); + if(!inst){ + return NULL; + } + + lua_instance_data* data = calloc(1, sizeof(lua_instance_data)); + if(!data){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + + //load the interpreter + data->interpreter = luaL_newstate(); + if(!data->interpreter){ + fprintf(stderr, "Failed to initialize LUA\n"); + free(data); + return NULL; + } + luaL_openlibs(data->interpreter); + + //register lua api functions + lua_register(data->interpreter, "output", lua_callback_output); + + //store instance pointer to the lua state + lua_pushstring(data->interpreter, LUA_REGISTRY_KEY); + lua_pushlightuserdata(data->interpreter, (void *) inst); + lua_settable(data->interpreter, LUA_REGISTRYINDEX); + + inst->impl = data; + return inst; +} + +static channel* lua_channel(instance* inst, char* spec){ + size_t u; + lua_instance_data* data = (lua_instance_data*) inst->impl; + + //find matching channel + for(u = 0; u < data->channels; u++){ + if(!strcmp(spec, data->channel_name[u])){ + break; + } + } + + //allocate new channel + if(u == data->channels){ + data->channel_name = realloc(data->channel_name, (u + 1) * sizeof(char*)); + if(!data->channel_name){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + + data->channel_name[u] = strdup(spec); + if(!data->channel_name[u]){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + data->channels++; + } + + return mm_channel(inst, u, 1); +} + +static int lua_set(instance* inst, size_t num, channel** c, channel_value* v){ + size_t n = 0; + lua_instance_data* data = (lua_instance_data*) inst->impl; + + for(n = 0; n < num; n++){ + //call lua channel handlers + lua_getglobal(data->interpreter, data->channel_name[c[n]->ident]); + lua_pushnumber(data->interpreter, v[n].normalised); + if(lua_pcall(data->interpreter, 1, 0, 0) != LUA_OK){ + fprintf(stderr, "Failed to call handler for %s.%s: %s\n", inst->name, data->channel_name[c[n]->ident], lua_tostring(data->interpreter, -1)); + lua_pop(data->interpreter, 1); + } + } + return 0; +} + +static int lua_handle(size_t num, managed_fd* fds){ + //TODO call timer callbacks + return 0; +} + +static int lua_start(){ + //TODO start timers / register fds + return 0; +} + +static int lua_shutdown(){ + size_t n, u, p; + instance** inst = NULL; + lua_instance_data* data = NULL; + + if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list\n"); + return 1; + } + + for(u = 0; u < n; u++){ + data = (lua_instance_data*) inst[u]->impl; + //stop the interpreter + lua_close(data->interpreter); + //cleanup channel data + for(p = 0; p < data->channels; p++){ + free(data->channel_name[p]); + } + free(data->channel_name); + free(inst[u]->impl); + } + + free(inst); + + fprintf(stderr, "Lua backend shut down\n"); + return 0; +} -- cgit v1.2.3