df5a317ceb
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.
125 lines
3.7 KiB
C++
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
|