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.
110 lines
3.0 KiB
C++
110 lines
3.0 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>
|
|
#include "mixer/Types.hpp"
|
|
|
|
namespace mixer {
|
|
|
|
/// 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
|
|
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.
|
|
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.
|
|
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 mixer
|