// Copyright (c) 2026 Ourpad Network // See LICENSE file in the project root for full license text. #pragma once #include #include #include #include #include namespace pipedal { /// Types of buses in the mixer architecture. enum class MixerBusType { Master, // Main L/R output — end of signal chain Subgroup, // Named subgroup (Drums, Guitars, Vocals...) Aux, // Aux send bus (monitor mix or FX send) FxReturn, // Stereo return from a shared FX processor (reverb, delay) }; /// 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 /// /// Key design decisions: /// - Buses are flat accumulators: they sum incoming audio with gain /// - Bus processing is minimal (volume, mute only) /// - A bus can be fed INTO another bus via the routing graph /// - All audio is floating-point, 32-bit 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. /// Performs: bus[ch][i] += source[ch][i] * gain for all channels 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. /// Reads internal mix buffer, applies gain, writes back. 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 volume_{0.0f}; // dB std::atomic mute_{false}; // Internal accumulation buffers [channel][sample] std::vector> buffers_; // VU tracking std::atomic vuLeft_{-96.0f}; std::atomic vuRight_{-96.0f}; size_t maxFrames_ = 512; }; } // namespace pipedal