// Copyright (c) 2026 Ourpad Network // See LICENSE file in the project root for full license text. #pragma once #include #include #include #include #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 nextInstanceId_; // Control values (atomic for RT-safe reads from control thread) std::atomic volume_{-96.0f}; // dB, -inf default std::atomic pan_{0.0f}; std::atomic mute_{false}; std::atomic solo_{false}; std::atomic hpEnabled_{false}; std::atomic hpFrequency_{80.0f}; MixerChannelType channelType_ = MixerChannelType::Instrument; std::string label_; // Aux sends std::vector auxSends_; // Audio buffers (allocated at prepare time) std::vector> preFxBuffers_; // Before FX chain (for pre-fader sends) std::vector> 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 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