aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends/lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/lua.c')
-rw-r--r--backends/lua.c202
1 files changed, 202 insertions, 0 deletions
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 <string.h>
+#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;
+}