Files
op-pedal/src/MixerBus.hpp
T
shawn df5a317ceb feat: add MixerEngine core — ChannelStrip, MixerBus, and MixerEngine for band-in-a-box digital mixer
MixerEngine architecture:
- MixerChannelStrip: per-input FX chain (Lv2Pedalboard reuse),
  volume, pan, mute, solo, HPF, aux sends, VU metering
- MixerBus: accumulation bus with volume, mute, VU. Supports
  master, subgroup, aux, and FX-return bus types
- MixerEngine: orchestrator managing channel→bus routing graph,
  bus→bus routing, solo override, and the full real-time audio
  processing cycle

All new code compiles cleanly with the existing C++20 build and
follows the existing PiPedal codebase conventions (namespaces,
error handling, buffer patterns). CPU-efficient real-time thread
processing with atomic control surface interaction.
2026-06-20 13:57:15 -04:00

125 lines
3.7 KiB
C++

// 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>
namespace pipedal {
/// 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)
};
/// 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
///
/// Key design decisions:
/// - Buses are flat accumulators: they sum incoming audio with gain
/// - Bus processing is minimal (volume, mute only)
/// - A bus can be fed INTO another bus via the routing graph
/// - All audio is floating-point, 32-bit
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.
/// Performs: bus[ch][i] += source[ch][i] * gain for all channels
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.
/// Reads internal mix buffer, applies gain, writes back.
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 pipedal