// Copyright (c) 2026 Ourpad Network // See LICENSE file in the project root for full license text. // // MixerDaemon — Standalone OPLabs Mixer Engine daemon. // // This daemon: // 1. Creates a MixerEngine with JACK audio I/O // 2. Creates a MixerApi for control // 3. Starts the MixerIpcServer on a Unix domain socket // 4. Runs the audio loop until SIGINT/SIGTERM #include #include #include #include #include #include #include "MixerIpcServer.hpp" #include "MixerApi.hpp" #include "MixerEngine.hpp" using namespace pipedal; // Global pointers for signal handler static MixerIpcServer* g_server = nullptr; static std::atomic g_running{true}; void signalHandler(int sig) { (void)sig; fprintf(stderr, "\n[MixerDaemon] Signal received, shutting down...\n"); g_running.store(false); if (g_server) { g_server->stop(); } } void printUsage(const char* prog) { fprintf(stderr, "Usage: %s [options]\n", prog); fprintf(stderr, "Options:\n"); fprintf(stderr, " --socket-path PATH Unix socket path (default: /tmp/oplabs-mixer.sock)\n"); fprintf(stderr, " --sample-rate RATE Sample rate in Hz (default: 48000)\n"); fprintf(stderr, " --buffer-size SIZE Buffer size in frames (default: 512)\n"); fprintf(stderr, " --input-channels N Number of input channels (default: 8)\n"); fprintf(stderr, " --output-channels N Number of output channels (default: 8)\n"); fprintf(stderr, " --help Print this help\n"); } int main(int argc, char* argv[]) { // Default parameters std::string socketPath = "/tmp/oplabs-mixer.sock"; uint32_t sampleRate = 48000; size_t bufferSize = 512; uint32_t inputChannels = 8; uint32_t outputChannels = 8; // Parse command line for (int i = 1; i < argc; i++) { std::string arg = argv[i]; if (arg == "--socket-path" && i + 1 < argc) { socketPath = argv[++i]; } else if (arg == "--sample-rate" && i + 1 < argc) { sampleRate = static_cast(std::stoul(argv[++i])); } else if (arg == "--buffer-size" && i + 1 < argc) { bufferSize = static_cast(std::stoul(argv[++i])); } else if (arg == "--input-channels" && i + 1 < argc) { inputChannels = static_cast(std::stoul(argv[++i])); } else if (arg == "--output-channels" && i + 1 < argc) { outputChannels = static_cast(std::stoul(argv[++i])); } else if (arg == "--help") { printUsage(argv[0]); return 0; } else { fprintf(stderr, "Unknown option: %s\n", arg.c_str()); printUsage(argv[0]); return 1; } } fprintf(stderr, "[MixerDaemon] Starting OPLabs Mixer Daemon\n"); fprintf(stderr, "[MixerDaemon] Socket: %s\n", socketPath.c_str()); fprintf(stderr, "[MixerDaemon] SampleRate: %u Hz\n", sampleRate); fprintf(stderr, "[MixerDaemon] BufferSize: %zu frames\n", bufferSize); fprintf(stderr, "[MixerDaemon] Inputs: %u\n", inputChannels); fprintf(stderr, "[MixerDaemon] Outputs: %u\n", outputChannels); // Set up signal handling signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); // Create MixerEngine auto engine = std::make_unique(); engine->setSampleRate(sampleRate); engine->setMaxBufferSize(bufferSize); // Auto-create channels engine->autoCreateChannels(inputChannels); fprintf(stderr, "[MixerDaemon] MixerEngine created with %zu channels\n", engine->channelCount()); // Create MixerApi and attach engine auto api = std::make_unique(); api->setMixerEngine(engine.get()); // Create IPC server auto server = std::make_unique(); server->setMixerApi(api.get()); server->setMixerEngine(engine.get()); g_server = server.get(); if (!server->start(socketPath)) { fprintf(stderr, "[MixerDaemon] Failed to start IPC server on %s\n", socketPath.c_str()); return 1; } fprintf(stderr, "[MixerDaemon] IPC server listening on %s\n", socketPath.c_str()); fprintf(stderr, "[MixerDaemon] Ready. Waiting for connections...\n"); // Main loop — keep running until signal // In a full implementation, this would run the JACK audio processing loop. // For now, the daemon provides IPC access to the MixerEngine. while (g_running.load()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Cleanup server->stop(); api->setMixerEngine(nullptr); fprintf(stderr, "[MixerDaemon] Shutdown complete.\n"); return 0; }