#include #include #include #include #include #include "engine.h" #include "tui.h" #include "cli.h" #include "gui.h" #include "dispatcher.h" #include "carla.h" static Engine engine; static volatile int keep_running = 1; static DispatchFn dispatch = NULL; void signal_handler(int sig) { (void)sig; keep_running = 0; if (dispatch) { Action action = { .type = ACTION_QUIT }; dispatch(action); } } void print_usage(const char *program) { printf("Usage: %s [options]\n", program); printf("Options:\n"); printf(" -n JACK client name (default: jack-looper)\n"); printf(" -c MIDI control channel (default: 0)\n"); printf(" -t Use TUI frontend (default)\n"); printf(" -i Use CLI frontend (interactive)\n"); printf(" -g Use GUI frontend\n"); printf(" -h Show this help\n"); } int main(int argc, char *argv[]) { const char *client_name = "jack-looper"; int control_channel = 0; enum { FRONTEND_TUI, FRONTEND_CLI, FRONTEND_GUI } frontend = FRONTEND_TUI; int opt; while ((opt = getopt(argc, argv, "n:c:tigh")) != -1) { switch (opt) { case 'n': client_name = optarg; break; case 'c': control_channel = atoi(optarg); if (control_channel < 0 || control_channel > 15) { fprintf(stderr, "Control channel must be 0-15\n"); return 1; } break; case 't': frontend = FRONTEND_TUI; break; case 'i': frontend = FRONTEND_CLI; break; case 'g': frontend = FRONTEND_GUI; break; case 'h': print_usage(argv[0]); return 0; default: print_usage(argv[0]); return 1; } } // Create initial state AppState initial_state; memset(&initial_state, 0, sizeof(AppState)); initial_state.transport_state = TRANSPORT_STOPPED; initial_state.clock_source = CLOCK_SOURCE_INTERNAL; initial_state.bpm = 120.0; initial_state.quantize_mode = QUANTIZE_OFF; initial_state.quantize_threshold = 0; initial_state.running = true; for (int i = 0; i < MAX_CLIPS; i++) { initial_state.clips[i].state = CLIP_EMPTY; initial_state.clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float)); initial_state.clips[i].buffer_size = 0; initial_state.clips[i].write_position = 0; initial_state.clips[i].read_position = 0; } // Initialize Carla host in the initial state (before dispatcher starts) carla_init(&initial_state.carla_host, NULL); carla_scan_plugins(&initial_state.carla_host); // Initialize dispatcher dispatch = dispatcher_init(&initial_state); // Initialize engine if (engine_init(&engine, client_name, dispatch) != 0) { fprintf(stderr, "Failed to initialize engine\n"); return 1; } // Set up signal handler signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // Start dispatcher dispatcher_start(); // Start engine if (engine_start(&engine) != 0) { fprintf(stderr, "Failed to start engine\n"); engine_cleanup(&engine); dispatcher_stop(); return 1; } printf("JACK Looper started\n"); printf("Client name: %s\n", client_name); printf("Control channel: %d\n", control_channel); printf("Sample rate: %u Hz\n", engine.sample_rate); printf("Press Ctrl+C to stop\n\n"); // Run selected frontend switch (frontend) { case FRONTEND_TUI: tui_init(&engine); tui_run(&engine); tui_cleanup(); break; case FRONTEND_CLI: cli_run(&engine); break; case FRONTEND_GUI: gui_main(&engine); break; } // Cleanup printf("\nShutting down...\n"); engine_stop(&engine); engine_cleanup(&engine); dispatcher_stop(); return 0; }