/* * melisma - extensible and scriptable audio engine * Copyright (C) 2026 Ludvig Gunne Lindström * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "melisma.h" #include "graph.h" #include "util.h" #include "event.h" #include "port.h" #include "mem.h" #include "io.h" jack_client_t *client; lua_State *state; static const char *program = NULL; static const char *path = NULL; static const char *client_name = "melisma"; static char **script_argv = NULL; static void usage(FILE *f) { fprintf(f,"Usage: %s [OPTION...] [FILE] [ARG...]\n",program); fprintf(f,"Options:\n"); fprintf(f,"\t-h Show this help message.\n"); fprintf(f,"\t-c NAME Set Jack client name to NAME.\n"); fprintf(f,"\t-C PATH Change working directory to PATH.\n"); } static void change_cwd(const char *path) { if (chdir(path) < 0) { fprintf(stderr,"%s: %s: %s\n", program,path,strerror(errno)); exit(EXIT_FAILURE); } } static void parse_args(int argc, char **argv) { program = argv[0]; char c; while ((c = getopt(argc, argv, "hc:C:")) != -1) { switch (c) { case 'h': usage(stdout); exit(EXIT_SUCCESS); case 'c': client_name = optarg; break; case 'C': change_cwd(optarg); break; default: usage(stderr); exit(EXIT_FAILURE); } } if (optind < argc && strcmp(argv[optind],"-") != 0) { path = argv[optind]; } script_argv = &argv[optind + 1]; } static int l_sample_rate(lua_State *s) { lua_pushinteger(s,ml_sample_rate()); return 1; } static int load_internal_module(lua_State *s) { lua_newtable(s); int tbl = lua_absindex(s,lua_gettop(s)); lua_pushcfunction(s,l_graph_layout); lua_setfield(s,tbl,"layout"); lua_pushcfunction(s,l_sample_rate); lua_setfield(s,tbl,"samplerate"); lua_newtable(s); int argv_tbl = lua_gettop(s); int i = 1; for (; *script_argv; script_argv++) { lua_pushstring(s,*script_argv); lua_rawseti(s,argv_tbl,i++); } lua_settop(s,argv_tbl); lua_setfield(s,tbl,"argv"); lua_settop(s,tbl); return 1; } static void register_internal_module(lua_State *s) { int top = lua_gettop(s); lua_getglobal(s,"package"); int package = lua_gettop(s); lua_getfield(s,package,"preload"); int preload = lua_gettop(s); lua_pushcfunction(s,load_internal_module); lua_setfield(s,preload,"melisma.internal"); lua_settop(s,top); } static char *read_stdin(void) { size_t len = 0; size_t cap = 0; char *buf = NULL; for (;;) { char c = fgetc(stdin); if (len == cap) { cap = cap ? 2 * cap : 1024; buf = srealloc(buf, cap); } buf[len++] = c == EOF ? 0 : c; if (c == EOF) { break; } } return buf; } int main(int argc, char **argv) { int exitc = EXIT_SUCCESS; parse_args(argc,argv); client = jack_client_open(client_name,0,NULL); if (client == NULL) { fprintf(stderr,"Failed to open Jack client '%s'\n",client_name); exit(EXIT_FAILURE); } state = luaL_newstate(); luaL_openlibs(state); register_internal_module(state); init_graph(state); init_events(state); init_ports(state); char *text = NULL; if (path == NULL) { text = read_stdin(); if (luaL_loadstring(state,text) != LUA_OK) { const char *err = lua_tostring(state,-1); fprintf(stderr,"%s\n",err); exitc = EXIT_FAILURE; goto cleanup; } } else { if (luaL_loadfile(state,path) != LUA_OK) { const char *err = lua_tostring(state,-1); fprintf(stderr,"%s\n",err); exitc = EXIT_FAILURE; goto cleanup; } } if (lua_pcall_with_backtrace(state,0,LUA_MULTRET) != LUA_OK) { const char *err = lua_tostring(state,-1); fprintf(stderr,"%s\n",err); exitc = EXIT_FAILURE; goto cleanup; } const char *err = build_graph(); if (err != NULL) { fprintf(stderr,"Error: %s\n",err); exitc = EXIT_FAILURE; goto cleanup; } jack_activate(client); ml_add_io_handler(STDIN_FILENO,POLLIN,NULL,NULL); io_loop(); cleanup: free(text); lua_close(state); jack_client_close(client); exit(exitc); } unsigned long ml_sample_rate(void) { return jack_get_sample_rate(client); }