a5423b7f55
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.
169 lines
5.1 KiB
C++
169 lines
5.1 KiB
C++
// 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
|