// Copyright (c) 2026 Ourpad Network // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #pragma once #include "Pedalboard.hpp" #include "Lv2Pedalboard.hpp" #include "IEffect.hpp" #include "BufferPool.hpp" #include #include #include #include namespace pipedal { /// Channel type classification for mixer channel strips. enum class MixerChannelType { Instrument, // guitar, bass, keys — expects NAM/guitar amp modeling Mic, // vocal mic — expects compressor, EQ, reverb chain Line, // line-level input (backing tracks, synths, drum machines) AuxReturn, // return from external FX processor }; /// Configuration for a single aux send on a channel strip. struct AuxSendConfig { float level = -96.0f; // dB, -96 ≈ -inf (effectively off) bool preFader = false; // true = pre-fader (monitor send), false = post-fader (FX send) int64_t targetBusId = -1; // bus ID this sends to bool isActive() const { return level > -90.0f && targetBusId >= 0; } }; /// Per-channel high-pass filter configuration. struct HpfConfig { bool enabled = false; float frequency = 80.0f; // Hz // Filter state for simple biquad — sized for max buffer // Allocated at prepare time }; /// A single channel strip in the mixer. /// /// Each MixerChannelStrip wraps: /// - A mini-pedalboard (FX chain using existing Lv2Pedalboard) /// - 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; } /// --- FX Chain --- /// Access the channel's pedalboard for plugin management Pedalboard& fxChain() { return fxChain_; } const Pedalboard& fxChain() const { return fxChain_; } /// Get the real-time processor for this channel's FX chain Lv2Pedalboard* fxProcessor() { return fxProcessor_.get(); } /// Prepare the FX chain for processing void prepareFx(IHost* pHost, Lv2PedalboardErrorList& errorList, ExistingEffectMap* existingEffects = nullptr); /// --- 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); /// Process one audio block through this channel strip. /// Reads from input, runs HPF → FX chain, applies volume/pan. /// Output goes to provided output buffer(s). /// Returns the number of processed samples. 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; } /// Calcuate 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_; // FX chain Pedalboard fxChain_; std::unique_ptr fxProcessor_; // 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 BufferPool bufferPool_; // 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 pipedal