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,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
|
||||
Reference in New Issue
Block a user