// 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": , "method": , "params": {}} // Response: {"id": , "result": , "error": } // Push: {"type": "push", "event": , "data": {}} // // 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 #include #include #include #include #include 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; using MessageCallback = std::function; 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 running_{false}; int serverFd_ = -1; std::string socketPath_; std::thread acceptThread_; // Per-client state struct ClientConnection { int fd = -1; bool active = false; std::vector readBuffer; uint32_t expectedLength = 0; bool readingHeader = true; }; std::map> 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 connectedClientFds() const; }; } // namespace pipedal