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.
230 lines
7.8 KiB
C++
230 lines
7.8 KiB
C++
// Copyright (c) 2026 Ourpad Network
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
// this software and associated documentation files (the "Software"), to deal in
|
|
// the Software without restriction, including without limitation the rights to
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
// of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
// subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
#pragma once
|
|
|
|
#include "Pedalboard.hpp"
|
|
#include "Lv2Pedalboard.hpp"
|
|
#include "IEffect.hpp"
|
|
#include "BufferPool.hpp"
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <atomic>
|
|
#include <string>
|
|
|
|
namespace pipedal {
|
|
|
|
/// Channel type classification for mixer channel strips.
|
|
enum class MixerChannelType {
|
|
Instrument, // guitar, bass, keys — expects NAM/guitar 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
|
|
// Filter state for simple biquad — sized for max buffer
|
|
// Allocated at prepare time
|
|
};
|
|
|
|
/// A single channel strip in the mixer.
|
|
///
|
|
/// Each MixerChannelStrip wraps:
|
|
/// - A mini-pedalboard (FX chain using existing Lv2Pedalboard)
|
|
/// - 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; }
|
|
|
|
/// --- FX Chain ---
|
|
|
|
/// Access the channel's pedalboard for plugin management
|
|
Pedalboard& fxChain() { return fxChain_; }
|
|
const Pedalboard& fxChain() const { return fxChain_; }
|
|
|
|
/// Get the real-time processor for this channel's FX chain
|
|
Lv2Pedalboard* fxProcessor() { return fxProcessor_.get(); }
|
|
|
|
/// Prepare the FX chain for processing
|
|
void prepareFx(IHost* pHost, Lv2PedalboardErrorList& errorList,
|
|
ExistingEffectMap* existingEffects = nullptr);
|
|
|
|
/// --- 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);
|
|
|
|
/// Process one audio block through this channel strip.
|
|
/// Reads from input, runs HPF → FX chain, applies volume/pan.
|
|
/// Output goes to provided output buffer(s).
|
|
/// Returns the number of processed samples.
|
|
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;
|
|
}
|
|
|
|
/// Calcuate 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_;
|
|
|
|
// FX chain
|
|
Pedalboard fxChain_;
|
|
std::unique_ptr<Lv2Pedalboard> fxProcessor_;
|
|
|
|
// 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
|
|
BufferPool bufferPool_;
|
|
|
|
// 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 pipedal
|