Files
oplabs-mixer-app/daemon/MixerDaemon.cpp
T
shawn d19e0ea7a8 Add MasterBus component + enhance BusStrip with routing selector
MasterBus: stereo master fader, mute, level meters in PiPedal theme
(purple accent per mixer-engine branch reference).

BusStrip: added per-channel routing selector (ch1Route..ch8Route)
for selecting output bus target (Main, Bus 2-8).

MixerPage: integrated MasterBus with levels derived from bus
master data. Local state for volume/mute until backend exposes
master output.
2026-06-23 12:15:09 -04:00

135 lines
4.7 KiB
C++

// 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 <cstdio>
#include <csignal>
#include <atomic>
#include <chrono>
#include <thread>
#include <memory>
#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<bool> 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<uint32_t>(std::stoul(argv[++i]));
} else if (arg == "--buffer-size" && i + 1 < argc) {
bufferSize = static_cast<size_t>(std::stoul(argv[++i]));
} else if (arg == "--input-channels" && i + 1 < argc) {
inputChannels = static_cast<uint32_t>(std::stoul(argv[++i]));
} else if (arg == "--output-channels" && i + 1 < argc) {
outputChannels = static_cast<uint32_t>(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<MixerEngine>();
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<MixerApi>();
api->setMixerEngine(engine.get());
// Create IPC server
auto server = std::make_unique<MixerIpcServer>();
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;
}