diff options
| author | Ludvig Gunne Lindström <ludviggunnelindstrom@gmail.com> | 2026-07-17 11:42:57 +0200 |
|---|---|---|
| committer | Ludvig Gunne Lindström <ludviggunnelindstrom@gmail.com> | 2026-11-08 14:05:19 +0100 |
| commit | fd27bacdd9c97f696938b12d2a437da4e514c26f (patch) | |
| tree | 2d676bb280b35420a2c105676b0e66fe5b91958d /src/main.c | |
| download | melisma-archive.tar.gz | |
initarchive
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 252 |
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); +} |
