Files
shawn a5423b7f55 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.
2026-06-23 12:17:45 -04:00

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