aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authorLudvig Gunne Lindström <ludviggunnelindstrom@gmail.com>2026-07-17 11:42:57 +0200
committerLudvig Gunne Lindström <ludviggunnelindstrom@gmail.com>2026-11-08 14:07:53 +0100
commit98b3c66ebc79597f5b54fff132eaafbebf581248 (patch)
treef999ec466ae3ac635653d1fe0a6a6dd1a2f2c3b0 /src/main.c
downloadmelisma-main.tar.gz
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b1224a0
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <jack/jack.h>
+
+#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);
+}