Files
oplabs-mixer-app/daemon/MixerIpcServer.hpp
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

129 lines
4.4 KiB
C++

// Copyright (c) 2026 Ourpad Network
// See LICENSE file in the project root for full license text.
//
// MixerIpcServer — Unix domain socket IPC server for the OPLabs Mixer Daemon.
//
// Protocol:
// - Transport: Unix domain socket (AF_UNIX, SOCK_STREAM)
// - Socket path: /tmp/oplabs-mixer.sock
// - Framing: 4-byte big-endian length prefix (uint32_t) + JSON payload (UTF-8)
//
// Request: {"id": <int>, "method": <string>, "params": {<object>}}
// Response: {"id": <int>, "result": <any>, "error": <string|null>}
// Push: {"type": "push", "event": <string>, "data": {<object>}}
//
// Methods:
// getState params: {} result: {full mixer state}
// setChannelVolume params: {channelIndex, volumeDb} result: {}
// setChannelPan params: {channelIndex, pan} result: {}
// setChannelMute params: {channelIndex, mute} result: {}
// setChannelSolo params: {channelIndex, solo} result: {}
// addChannel params: {physicalInputIndex} result: {channelIndex}
// removeChannel params: {channelIndex} result: {}
// getOutputRoutes params: {} result: {routes: [...]}
// setOutputRoutes params: {routes: [...]} result: {}
// saveScene params: {name} result: {id, name}
// loadScene params: {sceneId} result: {ok: bool}
// listScenes params: {} result: {scenes: [...]}
// deleteScene params: {sceneId} result: {ok: bool}
#pragma once
#include <string>
#include <functional>
#include <atomic>
#include <thread>
#include <vector>
#include <map>
namespace pipedal {
class MixerApi;
class MixerEngine;
/// Unix domain socket IPC server that dispatches JSON messages to MixerApi.
class MixerIpcServer {
public:
/// Callback signature: clientConnected, clientDisconnected
using ConnectionCallback = std::function<void()>;
using MessageCallback = std::function<void(const std::string&)>;
MixerIpcServer();
~MixerIpcServer();
// Disable copy
MixerIpcServer(const MixerIpcServer&) = delete;
MixerIpcServer& operator=(const MixerIpcServer&) = delete;
/// Set the MixerApi instance to dispatch to.
void setMixerApi(MixerApi* api) { mixerApi_ = api; }
/// Set the MixerEngine instance directly (for push messages / VU).
void setMixerEngine(MixerEngine* engine) { mixerEngine_ = engine; }
/// Start the IPC server on the given socket path.
/// Returns true on success.
bool start(const std::string& socketPath = "/tmp/oplabs-mixer.sock");
/// Stop the IPC server and close all connections.
void stop();
/// Check if the server is running.
bool isRunning() const { return running_.load(); }
/// Broadcast a push message to all connected clients.
void broadcast(const std::string& jsonMessage);
/// Set callback for incoming messages (for logging/monitoring).
void setMessageCallback(MessageCallback cb) { messageCallback_ = std::move(cb); }
private:
MixerApi* mixerApi_ = nullptr;
MixerEngine* mixerEngine_ = nullptr;
std::atomic<bool> running_{false};
int serverFd_ = -1;
std::string socketPath_;
std::thread acceptThread_;
// Per-client state
struct ClientConnection {
int fd = -1;
bool active = false;
std::vector<uint8_t> readBuffer;
uint32_t expectedLength = 0;
bool readingHeader = true;
};
std::map<int, std::unique_ptr<ClientConnection>> clients_;
int nextClientId_ = 1;
MessageCallback messageCallback_;
// Accept loop (runs in its own thread)
void acceptLoop();
// Handle a single client connection
void handleClient(int clientFd);
// Read and accumulate data from a client
bool readFromClient(ClientConnection& conn);
// Process a complete JSON message
void processMessage(ClientConnection& conn, const std::string& jsonMessage);
// Send a JSON response to a specific client
bool sendResponse(int clientFd, const std::string& jsonResponse);
// Convenience: send a length-prefixed frame
bool sendFrame(int fd, const std::string& jsonPayload);
// Dispatch a method call to MixerApi and return JSON result
std::string dispatchMethod(const std::string& method, const std::string& paramsJson);
// All connected client file descriptors
std::vector<int> connectedClientFds() const;
};
} // namespace pipedal