// Copyright (c) 2026 Ourpad Network // See LICENSE file in the project root for full license text. #pragma once #include #include #include #include #include #include #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 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& outputRoutes() const { return outputRoutes_; } /// Set the entire output routing table. void setOutputRoutes(const std::vector& 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 findOutputRoutesForBus(int64_t busId) const; /// Get all current routes. const std::vector& 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 auxSendLevels; // indexed by aux bus index }; struct BusState { int64_t id; std::string name; MixerBusType type; float volume; bool mute; }; std::vector channels; std::vector buses; std::vector routes; }; MixerSnapshot captureSnapshot() const; void applySnapshot(const MixerSnapshot& snapshot); private: std::vector> channels_; std::map> buses_; MixerBus* masterBus_ = nullptr; // Routing entries std::vector routes_; // Output routing entries (bus → physical output channel mapping) std::vector 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 nextBusId_; // Temporary per-channel output buffers for routing // Allocated once at prepare time std::vector> 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 findRoutesForSource(int64_t sourceId); }; } // namespace pipedal