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,109 @@
|
||||
// Copyright (c) 2026 Ourpad Network
|
||||
// See LICENSE file in the project root for full license text.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include "mixer/Types.hpp"
|
||||
|
||||
namespace mixer {
|
||||
|
||||
/// An audio bus that accumulates contributions from multiple sources.
|
||||
///
|
||||
/// Buses form the mixing topology:
|
||||
/// Channels → subgroups → master
|
||||
/// Channels → aux sends → aux buses (monitor mixes)
|
||||
/// Aux buses → FxReturn buses → subgroup or master
|
||||
class MixerBus {
|
||||
public:
|
||||
MixerBus(int64_t id, MixerBusType type, const std::string& name, int channels = 2);
|
||||
~MixerBus() = default;
|
||||
|
||||
/// Bus identity
|
||||
int64_t id() const { return id_; }
|
||||
MixerBusType type() const { return type_; }
|
||||
const std::string& name() const { return name_; }
|
||||
void setName(const std::string& name) { name_ = name; }
|
||||
|
||||
/// Channel count (1 = mono, 2 = stereo, N = multi-channel)
|
||||
int channelCount() const { return channelCount_; }
|
||||
|
||||
/// --- Control surface (atomic for RT-safe writes) ---
|
||||
|
||||
/// Master volume in dB (-inf to +12.0)
|
||||
float volume() const { return volume_.load(); }
|
||||
void setVolume(float db);
|
||||
|
||||
/// Mute
|
||||
bool mute() const { return mute_.load(); }
|
||||
void setMute(bool mute);
|
||||
|
||||
/// --- Audio buffers ---
|
||||
|
||||
/// Allocate internal buffers. Must be called before processing.
|
||||
void allocateBuffers(size_t maxFrames);
|
||||
|
||||
/// Get read/write pointer to internal buffer for a channel
|
||||
float* buffer(int channel) {
|
||||
if (channel >= 0 && channel < (int)buffers_.size())
|
||||
return buffers_[channel].data();
|
||||
return nullptr;
|
||||
}
|
||||
const float* buffer(int channel) const {
|
||||
if (channel >= 0 && channel < (int)buffers_.size())
|
||||
return buffers_[channel].data();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Accumulate (sum) audio from a source into this bus with gain.
|
||||
void accumulate(
|
||||
const float* const* source,
|
||||
uint32_t frames,
|
||||
float gain,
|
||||
int sourceChannels
|
||||
);
|
||||
|
||||
/// Same as accumulate but for a single interleaved source buffer
|
||||
void accumulateMono(
|
||||
const float* source,
|
||||
uint32_t frames,
|
||||
float gain
|
||||
);
|
||||
|
||||
/// Clear all bus buffers to zero (must be called at start of each cycle)
|
||||
void clear();
|
||||
|
||||
/// Apply bus-level processing (volume, mute) to the internal mix.
|
||||
void process(uint32_t frames);
|
||||
|
||||
/// VU meter values after processing
|
||||
float vuLeft() const { return vuLeft_.load(); }
|
||||
float vuRight() const { return vuRight_.load(); }
|
||||
|
||||
/// Max frames this bus can handle
|
||||
size_t maxFrames() const { return maxFrames_; }
|
||||
|
||||
private:
|
||||
int64_t id_;
|
||||
MixerBusType type_;
|
||||
std::string name_;
|
||||
int channelCount_;
|
||||
|
||||
std::atomic<float> volume_{0.0f}; // dB
|
||||
std::atomic<bool> mute_{false};
|
||||
|
||||
// Internal accumulation buffers [channel][sample]
|
||||
std::vector<std::vector<float>> buffers_;
|
||||
|
||||
// VU tracking
|
||||
std::atomic<float> vuLeft_{-96.0f};
|
||||
std::atomic<float> vuRight_{-96.0f};
|
||||
|
||||
size_t maxFrames_ = 512;
|
||||
};
|
||||
|
||||
} // namespace mixer
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2026 Ourpad Network
|
||||
// See LICENSE file in the project root for full license text.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include "mixer/Types.hpp"
|
||||
|
||||
namespace mixer {
|
||||
|
||||
/// A single channel strip in the mixer.
|
||||
///
|
||||
/// Each MixerChannelStrip wraps:
|
||||
/// - Volume fader, pan pot, mute, solo
|
||||
/// - High-pass filter on input
|
||||
/// - Aux sends (pre/post fader)
|
||||
/// - VU metering
|
||||
///
|
||||
/// The channel operates in the real-time audio thread.
|
||||
/// Control changes are made from the non-realtime thread via atomic snapshots.
|
||||
class MixerChannelStrip {
|
||||
public:
|
||||
MixerChannelStrip(int channelIndex);
|
||||
~MixerChannelStrip();
|
||||
|
||||
// Disable copy
|
||||
MixerChannelStrip(const MixerChannelStrip&) = delete;
|
||||
MixerChannelStrip& operator=(const MixerChannelStrip&) = delete;
|
||||
|
||||
/// Channel identity
|
||||
int channelIndex() const { return channelIndex_; }
|
||||
int64_t instanceId() const { return instanceId_; }
|
||||
|
||||
/// --- Control surface (thread-safe via atomics for simple values) ---
|
||||
|
||||
/// Volume in dB (-inf to +12.0)
|
||||
float volume() const { return volume_; }
|
||||
void setVolume(float db);
|
||||
|
||||
/// Pan: -1.0 (full left) to +1.0 (full right). 0.0 = center.
|
||||
float pan() const { return pan_; }
|
||||
void setPan(float pan);
|
||||
|
||||
/// Mute
|
||||
bool mute() const { return mute_; }
|
||||
void setMute(bool mute);
|
||||
|
||||
/// Solo — overrides mute for monitoring
|
||||
bool solo() const { return solo_; }
|
||||
void setSolo(bool solo);
|
||||
|
||||
/// Channel type for UI classification
|
||||
MixerChannelType channelType() const { return channelType_; }
|
||||
void setChannelType(MixerChannelType type) { channelType_ = type; }
|
||||
|
||||
/// User-assignable label
|
||||
const std::string& label() const { return label_; }
|
||||
void setLabel(const std::string& label) { label_ = label; }
|
||||
|
||||
/// --- Input processing ---
|
||||
|
||||
/// High-pass filter
|
||||
bool hpEnabled() const { return hpEnabled_; }
|
||||
void setHpEnabled(bool enabled) { hpEnabled_ = enabled; }
|
||||
float hpFrequency() const { return hpFrequency_; }
|
||||
void setHpFrequency(float freq) { hpFrequency_ = freq; }
|
||||
|
||||
/// --- Aux Sends ---
|
||||
|
||||
void setAuxSend(int index, const AuxSendConfig& config);
|
||||
const AuxSendConfig& auxSend(int index) const;
|
||||
size_t auxSendCount() const { return auxSends_.size(); }
|
||||
void resizeAuxSends(size_t count);
|
||||
|
||||
/// --- Audio Processing (real-time thread) ---
|
||||
|
||||
/// Set sample rate and max buffer size
|
||||
void setSampleRate(uint32_t sampleRate);
|
||||
void setMaxBufferSize(size_t frames);
|
||||
|
||||
/// Allocate internal processing buffers. Must be called before process().
|
||||
void allocateBuffers();
|
||||
|
||||
/// Process one audio block through this channel strip.
|
||||
/// Reads from input, runs HPF, applies volume/pan.
|
||||
/// Output goes to provided output buffer(s).
|
||||
void process(
|
||||
const float* const* inputBuffers,
|
||||
size_t inputChannels,
|
||||
float* const* outputBuffers,
|
||||
size_t outputChannels,
|
||||
uint32_t frames
|
||||
);
|
||||
|
||||
/// Get post-FX, pre-fader audio for aux send calculation
|
||||
const float* postFxBuffer(int channel) const {
|
||||
if (channel < (int)postFxBuffers_.size()) return postFxBuffers_[channel].data();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Get pre-FX audio for pre-fader aux sends
|
||||
const float* preFxBuffer(int channel) const {
|
||||
if (channel < (int)preFxBuffers_.size()) return preFxBuffers_[channel].data();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Calculate aux send level considering pre/post fader and mute
|
||||
float effectiveAuxLevel(int auxIndex, bool anySoloActive) const;
|
||||
|
||||
/// VU meter values (peak, not RMS — computed during process)
|
||||
float vuLeft() const { return vuLeft_; }
|
||||
float vuRight() const { return vuRight_; }
|
||||
|
||||
/// --- Lifecycle ---
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
void Unprepare();
|
||||
|
||||
private:
|
||||
int channelIndex_;
|
||||
int64_t instanceId_;
|
||||
|
||||
static std::atomic<int64_t> nextInstanceId_;
|
||||
|
||||
// Control values (atomic for RT-safe reads from control thread)
|
||||
std::atomic<float> volume_{-96.0f}; // dB, -inf default
|
||||
std::atomic<float> pan_{0.0f};
|
||||
std::atomic<bool> mute_{false};
|
||||
std::atomic<bool> solo_{false};
|
||||
std::atomic<bool> hpEnabled_{false};
|
||||
std::atomic<float> hpFrequency_{80.0f};
|
||||
|
||||
MixerChannelType channelType_ = MixerChannelType::Instrument;
|
||||
std::string label_;
|
||||
|
||||
// Aux sends
|
||||
std::vector<AuxSendConfig> auxSends_;
|
||||
|
||||
// Audio buffers (allocated at prepare time)
|
||||
std::vector<std::vector<float>> preFxBuffers_; // Before FX chain (for pre-fader sends)
|
||||
std::vector<std::vector<float>> postFxBuffers_; // After FX chain, before fader
|
||||
|
||||
// Sample rate / buffer size
|
||||
uint32_t sampleRate_ = 48000;
|
||||
size_t maxBufferSize_ = 512;
|
||||
|
||||
// VU tracking
|
||||
float vuLeft_ = -96.0f;
|
||||
float vuRight_ = -96.0f;
|
||||
|
||||
// Simple 1-pole HPF state (per channel)
|
||||
struct HpfState {
|
||||
float x1 = 0.0f, y1 = 0.0f;
|
||||
};
|
||||
std::vector<HpfState> hpfStates_;
|
||||
|
||||
// Apply pan law: constant power (-3dB center)
|
||||
void applyPan(float& leftGain, float& rightGain) const;
|
||||
|
||||
// Apply HPF biquad to a buffer
|
||||
void applyHpf(float* buffer, uint32_t frames, HpfState& state);
|
||||
};
|
||||
|
||||
} // namespace mixer
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 Ourpad Network
|
||||
// See LICENSE file in the project root for full license text.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "mixer/MixerEngine.hpp"
|
||||
|
||||
namespace mixer {
|
||||
|
||||
/// Unix domain socket JSON control server for the MixerEngine.
|
||||
///
|
||||
/// Listens on a Unix domain socket (default: /tmp/mixer-daemon.sock).
|
||||
/// Each client connection sends one JSON command per line and receives
|
||||
/// one JSON response per line.
|
||||
///
|
||||
/// Supported commands:
|
||||
/// {"cmd":"getState"} → full mixer state snapshot
|
||||
/// {"cmd":"getVU"} → VU levels for all channels
|
||||
/// {"cmd":"setChannelVolume","channel":N,"volume":-6.0}
|
||||
/// {"cmd":"setChannelPan","channel":N,"pan":0.0}
|
||||
/// {"cmd":"setChannelMute","channel":N,"mute":true}
|
||||
/// {"cmd":"setChannelSolo","channel":N,"solo":false}
|
||||
/// {"cmd":"setChannelLabel","channel":N,"label":"Guitar"}
|
||||
/// {"cmd":"setBusVolume","busId":N,"volume":0.0}
|
||||
/// {"cmd":"setBusMute","busId":N,"mute":false}
|
||||
/// {"cmd":"routeChannelToBus","channel":N,"busId":N,"level":0.0}
|
||||
/// {"cmd":"routeBusToBus","sourceBusId":N,"targetBusId":N,"level":0.0}
|
||||
/// {"cmd":"removeRoute","sourceId":N,"targetBusId":N}
|
||||
/// {"cmd":"autoCreateChannels","inputs":N,"outputs":N}
|
||||
/// {"cmd":"addChannel","physicalInput":N} → {"status":"ok","channelIndex":N}
|
||||
/// {"cmd":"removeChannel","channel":N}
|
||||
class MixerControlServer {
|
||||
public:
|
||||
MixerControlServer(MixerEngine* engine, const std::string& socketPath = "/tmp/mixer-daemon.sock");
|
||||
~MixerControlServer();
|
||||
|
||||
// Disable copy
|
||||
MixerControlServer(const MixerControlServer&) = delete;
|
||||
MixerControlServer& operator=(const MixerControlServer&) = delete;
|
||||
|
||||
/// Start the control server in a background thread.
|
||||
/// Returns true if the socket was created successfully.
|
||||
bool start();
|
||||
|
||||
/// Stop the control server and join the thread.
|
||||
void stop();
|
||||
|
||||
/// True if the server is running.
|
||||
bool running() const { return running_.load(); }
|
||||
|
||||
private:
|
||||
MixerEngine* engine_;
|
||||
std::string socketPath_;
|
||||
int serverFd_ = -1;
|
||||
std::thread serverThread_;
|
||||
std::atomic<bool> running_{false};
|
||||
|
||||
// Server thread main loop
|
||||
void serverLoop();
|
||||
|
||||
// Handle one client connection
|
||||
void handleClient(int clientFd);
|
||||
|
||||
// Process a single JSON command and return JSON response
|
||||
std::string processCommand(const std::string& commandJson);
|
||||
|
||||
// Helper: build full state JSON
|
||||
std::string buildStateJson() const;
|
||||
|
||||
// Helper: build VU JSON
|
||||
std::string buildVuJson() const;
|
||||
};
|
||||
|
||||
} // namespace mixer
|
||||
@@ -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
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2026 Ourpad Network
|
||||
// See LICENSE file in the project root for full license text.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mixer {
|
||||
|
||||
/// Types of buses in the mixer architecture.
|
||||
enum class MixerBusType {
|
||||
Master, // Main L/R output — end of signal chain
|
||||
Subgroup, // Named subgroup (Drums, Guitars, Vocals...)
|
||||
Aux, // Aux send bus (monitor mix or FX send)
|
||||
FxReturn, // Stereo return from a shared FX processor (reverb, delay)
|
||||
};
|
||||
|
||||
/// Channel type classification for mixer channel strips.
|
||||
enum class MixerChannelType {
|
||||
Instrument, // guitar, bass, keys — expects amp modeling
|
||||
Mic, // vocal mic — expects compressor, EQ, reverb chain
|
||||
Line, // line-level input (backing tracks, synths, drum machines)
|
||||
AuxReturn, // return from external FX processor
|
||||
};
|
||||
|
||||
/// Configuration for a single aux send on a channel strip.
|
||||
struct AuxSendConfig {
|
||||
float level = -96.0f; // dB, -96 ≈ -inf (effectively off)
|
||||
bool preFader = false; // true = pre-fader (monitor send), false = post-fader (FX send)
|
||||
int64_t targetBusId = -1; // bus ID this sends to
|
||||
|
||||
bool isActive() const { return level > -90.0f && targetBusId >= 0; }
|
||||
};
|
||||
|
||||
/// Per-channel high-pass filter configuration.
|
||||
struct HpfConfig {
|
||||
bool enabled = false;
|
||||
float frequency = 80.0f; // Hz
|
||||
};
|
||||
|
||||
/// 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
|
||||
};
|
||||
|
||||
/// Describes a mapping from a mixer bus to physical output channels.
|
||||
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)
|
||||
};
|
||||
|
||||
/// A complete snapshot of the mixer state for save/restore.
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace mixer
|
||||
Reference in New Issue
Block a user