261 lines
8.3 KiB
C++
261 lines
8.3 KiB
C++
// 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 "MixerChannelStrip.hpp"
|
|
#include "MixerBus.hpp"
|
|
#include "MidiMapper.hpp"
|
|
|
|
namespace pipedal {
|
|
|
|
class MixerChannelStrip;
|
|
class MixerBus;
|
|
class Lv2PedalboardErrorList;
|
|
class IHost;
|
|
|
|
/// Routing entry: a source (channel or bus) feeds a target bus with a level.
|
|
struct MixerRouteEntry {
|
|
enum SourceType {
|
|
SourceChannel,
|
|
SourceBus
|
|
};
|
|
|
|
SourceType sourceType;
|
|
int64_t sourceId; // channel instanceId or bus ID
|
|
int64_t targetBusId; // the bus being fed into
|
|
float level = 0.0f; // dB
|
|
};
|
|
|
|
/// 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: process FX chain → 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);
|
|
|
|
/// 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 ---
|
|
|
|
/// Describes a mapping from a mixer bus to physical output channels.
|
|
/// Multiple routes can be active simultaneously (e.g. Master→1-2, Aux1→3-4).
|
|
struct MixerOutputRoute {
|
|
int64_t sourceBusId; // Bus to route from
|
|
int sourceStartChannel; // Starting channel on the bus (0=L, 1=R)
|
|
int targetStartChannel; // Starting physical output channel
|
|
int channels; // Number of consecutive channels to route (1 or 2 typically)
|
|
};
|
|
|
|
/// 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_; }
|
|
|
|
/// --- Audio Processing (real-time thread) ---
|
|
|
|
/// Prepare all channels and allocate buffers.
|
|
void Prepare(IHost* pHost, Lv2PedalboardErrorList& errorList);
|
|
|
|
/// Activate all channels.
|
|
void Activate();
|
|
|
|
/// Deactivate all channels.
|
|
void Deactivate();
|
|
|
|
/// Process one full mixer cycle.
|
|
/// deviceInputs/outputs are the raw audio interface buffers.
|
|
/// The mixer reads from inputs, processes through channels → buses, writes to outputs.
|
|
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;
|
|
|
|
/// --- MIDI Control Surface Mapping ---
|
|
|
|
/// Access the MIDI mapper for CC control surface mapping.
|
|
MidiMapper& midiMapper() { return midiMapper_; }
|
|
const MidiMapper& midiMapper() const { return midiMapper_; }
|
|
|
|
/// Process a MIDI event (typically from the real-time audio thread).
|
|
/// Routes CC messages to the midi mapper. Returns true if consumed.
|
|
bool processMidiEvent(const struct MidiEvent& event);
|
|
|
|
/// --- State Serialization ---
|
|
|
|
struct MixerSnapshot {
|
|
struct ChannelState {
|
|
int channelIndex;
|
|
float volume;
|
|
float pan;
|
|
bool mute;
|
|
bool solo;
|
|
MixerChannelType channelType;
|
|
std::string label;
|
|
bool hpEnabled;
|
|
float hpFrequency;
|
|
std::vector<float> auxSendLevels; // indexed by aux bus index
|
|
};
|
|
struct BusState {
|
|
int64_t id;
|
|
std::string name;
|
|
MixerBusType type;
|
|
float volume;
|
|
bool mute;
|
|
};
|
|
std::vector<ChannelState> channels;
|
|
std::vector<BusState> buses;
|
|
std::vector<MixerRouteEntry> routes;
|
|
};
|
|
|
|
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;
|
|
|
|
// IHost reference for FX preparation
|
|
IHost* pHost_ = nullptr;
|
|
|
|
// MIDI CC control surface mapper
|
|
MidiMapper midiMapper_;
|
|
|
|
// Next bus ID counter
|
|
static std::atomic<int64_t> nextBusId_;
|
|
|
|
// Temporary per-channel output buffers for routing
|
|
// Allocated once at prepare time
|
|
std::vector<std::vector<float>> channelOutputBuffers_;
|
|
|
|
// 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 pipedal
|