Initial extraction: C++ MixerEngine standalone daemon
Extracted from op-pedal mixer-engine branch. Core components: - MixerEngine / MixerChannelStrip / MixerBus — cleaned of LV2/Pedalboard/IHost/MidiMapper deps - MixerControlServer — Unix domain socket JSON control interface - main.cpp — JACK audio I/O client with headless fallback - CMake build — auto-fetches nlohmann/json, links JACK - 9 core tests passing Architecture: JACK ports → MixerEngine::process() → JACK ports, controlled via Unix socket JSON commands.
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2026 Ourpad Network
|
||||
// See LICENSE file in the project root for full license text.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include "mixer/MixerChannelStrip.hpp"
|
||||
#include "mixer/MixerBus.hpp"
|
||||
#include "mixer/Types.hpp"
|
||||
|
||||
namespace mixer {
|
||||
|
||||
class MixerChannelStrip;
|
||||
class MixerBus;
|
||||
|
||||
/// The MixerEngine is the heart of the band-in-a-box digital mixer.
|
||||
///
|
||||
/// It owns and manages:
|
||||
/// - N channel strips (MixerChannelStrip), one per physical/logical input
|
||||
/// - M buses (MixerBus), including master, subgroups, aux sends
|
||||
/// - A routing graph connecting channels to buses and buses to buses
|
||||
///
|
||||
/// Processing order per audio cycle:
|
||||
/// 1. Clear all bus buffers
|
||||
/// 2. For each channel: apply volume/pan → accumulate to routed buses
|
||||
/// 3. For each aux send: calculate send level, accumulate to aux buses
|
||||
/// 4. Route buses to buses according to routing matrix
|
||||
/// 5. Process each bus (apply volume, compute VU)
|
||||
/// 6. Master bus outputs are the final mix
|
||||
///
|
||||
/// All control methods are thread-safe for use from the non-RT thread.
|
||||
/// The process() method runs in the RT audio thread.
|
||||
class MixerEngine {
|
||||
public:
|
||||
MixerEngine();
|
||||
~MixerEngine();
|
||||
|
||||
// Disable copy
|
||||
MixerEngine(const MixerEngine&) = delete;
|
||||
MixerEngine& operator=(const MixerEngine&) = delete;
|
||||
|
||||
/// --- Configuration ---
|
||||
|
||||
/// Set sample rate and max buffer size before preparation
|
||||
void setSampleRate(uint32_t sampleRate);
|
||||
void setMaxBufferSize(size_t frames);
|
||||
|
||||
/// --- Channel Management ---
|
||||
|
||||
/// Add a new channel strip for the given physical input index.
|
||||
/// Returns a pointer to the new channel (valid until removed).
|
||||
MixerChannelStrip* addChannel(int physicalInputIndex);
|
||||
|
||||
/// Auto-create channels based on the number of detected input channels.
|
||||
/// Removes existing channels and creates one strip per input.
|
||||
/// Also sets up default output routes.
|
||||
void autoCreateChannels(uint32_t inputChannelCount, uint32_t outputChannelCount = 2);
|
||||
|
||||
/// Remove a channel by its channel index.
|
||||
void removeChannel(int channelIndex);
|
||||
|
||||
/// Get a channel by index. Returns nullptr if not found.
|
||||
MixerChannelStrip* getChannel(int channelIndex);
|
||||
const MixerChannelStrip* getChannel(int channelIndex) const;
|
||||
|
||||
/// Number of channels currently in the mixer.
|
||||
size_t channelCount() const { return channels_.size(); }
|
||||
|
||||
/// --- Bus Management ---
|
||||
|
||||
/// Add a new bus and return its ID.
|
||||
int64_t addBus(MixerBusType type, const std::string& name, int channels = 2);
|
||||
|
||||
/// Remove a bus by ID.
|
||||
void removeBus(int64_t busId);
|
||||
|
||||
/// Get a bus by ID. Returns nullptr if not found.
|
||||
MixerBus* getBus(int64_t busId);
|
||||
const MixerBus* getBus(int64_t busId) const;
|
||||
|
||||
/// Access the master bus (always present).
|
||||
MixerBus* masterBus() { return masterBus_; }
|
||||
const MixerBus* masterBus() const { return masterBus_; }
|
||||
|
||||
/// Get all bus IDs (for iteration).
|
||||
std::vector<int64_t> busIds() const;
|
||||
|
||||
/// --- Routing ---
|
||||
|
||||
/// Route a channel to a bus with a given level in dB.
|
||||
void routeChannelToBus(int channelIndex, int64_t busId, float levelDb = 0.0f);
|
||||
|
||||
/// Route a bus to another bus (e.g., subgroup to master).
|
||||
void routeBusToBus(int64_t sourceBusId, int64_t targetBusId, float levelDb = 0.0f);
|
||||
|
||||
/// Remove a route.
|
||||
void removeRoute(int64_t sourceId, int64_t targetBusId);
|
||||
|
||||
/// Clear all routes.
|
||||
void clearRoutes();
|
||||
|
||||
/// --- Output Routing ---
|
||||
|
||||
/// Get the current output routing table (bus → physical output channel mapping).
|
||||
const std::vector<MixerOutputRoute>& outputRoutes() const { return outputRoutes_; }
|
||||
|
||||
/// Set the entire output routing table.
|
||||
void setOutputRoutes(const std::vector<MixerOutputRoute>& routes);
|
||||
|
||||
/// Add a single output route.
|
||||
void addOutputRoute(int64_t busId, int sourceStartChannel, int targetStartChannel, int channels);
|
||||
|
||||
/// Remove all output routes for a given bus.
|
||||
void removeOutputRoutes(int64_t busId);
|
||||
|
||||
/// Get all routes for a given bus.
|
||||
std::vector<MixerOutputRoute> findOutputRoutesForBus(int64_t busId) const;
|
||||
|
||||
/// Get all current routes.
|
||||
const std::vector<MixerRouteEntry>& routes() const { return routes_; }
|
||||
|
||||
/// --- Physical I/O Info ---
|
||||
|
||||
/// Number of physical input channels detected on the audio device.
|
||||
uint32_t physicalInputCount() const { return physicalInputCount_; }
|
||||
/// Number of physical output channels detected on the audio device.
|
||||
uint32_t physicalOutputCount() const { return physicalOutputCount_; }
|
||||
|
||||
/// Set the physical I/O channel counts.
|
||||
void setPhysicalChannelCounts(uint32_t inputs, uint32_t outputs) {
|
||||
physicalInputCount_ = inputs;
|
||||
physicalOutputCount_ = outputs;
|
||||
}
|
||||
|
||||
/// --- Audio Processing (real-time thread) ---
|
||||
|
||||
/// Prepare all channels and allocate buffers.
|
||||
void Prepare();
|
||||
|
||||
/// Activate all channels.
|
||||
void Activate();
|
||||
|
||||
/// Deactivate all channels.
|
||||
void Deactivate();
|
||||
|
||||
/// Process one full mixer cycle.
|
||||
/// deviceInputs/outputs are the raw audio interface buffers.
|
||||
void process(
|
||||
float** deviceInputs,
|
||||
uint32_t inputChannels,
|
||||
float** deviceOutputs,
|
||||
uint32_t outputChannels,
|
||||
uint32_t frames
|
||||
);
|
||||
|
||||
/// --- Solo Management ---
|
||||
|
||||
/// True if any channel has solo engaged.
|
||||
bool anySoloActive() const;
|
||||
|
||||
/// --- State Serialization ---
|
||||
|
||||
MixerSnapshot captureSnapshot() const;
|
||||
void applySnapshot(const MixerSnapshot& snapshot);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<MixerChannelStrip>> channels_;
|
||||
std::map<int64_t, std::unique_ptr<MixerBus>> buses_;
|
||||
MixerBus* masterBus_ = nullptr;
|
||||
|
||||
// Routing entries
|
||||
std::vector<MixerRouteEntry> routes_;
|
||||
|
||||
// Output routing entries (bus → physical output channel mapping)
|
||||
std::vector<MixerOutputRoute> outputRoutes_;
|
||||
|
||||
// Audio configuration
|
||||
uint32_t sampleRate_ = 48000;
|
||||
size_t maxBufferSize_ = 512;
|
||||
|
||||
// Next bus ID counter
|
||||
static std::atomic<int64_t> nextBusId_;
|
||||
|
||||
// Temporary per-channel output buffers for routing
|
||||
std::vector<std::vector<float>> channelOutputBuffers_;
|
||||
|
||||
// Physical I/O channel counts
|
||||
uint32_t physicalInputCount_ = 0;
|
||||
uint32_t physicalOutputCount_ = 0;
|
||||
|
||||
// Internal helper: accumulate a channel's output to all its routed buses
|
||||
void routeChannelOutput(
|
||||
MixerChannelStrip* channel,
|
||||
float** channelOutput,
|
||||
uint32_t frames
|
||||
);
|
||||
|
||||
// Internal helper: process all bus-to-bus routing
|
||||
void processBusRouting(uint32_t frames);
|
||||
|
||||
// Build a list of all routes from a given source
|
||||
std::vector<MixerRouteEntry*> findRoutesForSource(int64_t sourceId);
|
||||
};
|
||||
|
||||
} // namespace mixer
|
||||
Reference in New Issue
Block a user