/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { namespace dsp { /** Adds a DC offset (voltage bias) to the audio samples. This is a useful preprocessor for asymmetric waveshaping when a waveshaper is bookended by a bias on input and a DC-offset removing high pass filter on output. This is an extremely simple bias implementation that simply adds a value to a signal. More complicated bias behaviours exist in real circuits - for your homework ;). @tags{DSP} */ template class Bias { public: Bias() noexcept = default; //============================================================================== /** Sets the DC bias @param newBias DC offset in range [-1, 1] */ void setBias (FloatType newBias) noexcept { jassert (newBias >= static_cast (-1) && newBias <= static_cast (1)); bias.setTargetValue (newBias); } //============================================================================== /** Returns the DC bias @return DC bias, which should be in the range [-1, 1] */ FloatType getBias() const noexcept { return bias.getTargetValue(); } /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { if (rampDurationSeconds != newDurationSeconds) { rampDurationSeconds = newDurationSeconds; updateRamp(); } } double getRampDurationSeconds() const noexcept { return rampDurationSeconds; } //============================================================================== /** Called before processing starts */ void prepare (const ProcessSpec& spec) noexcept { sampleRate = spec.sampleRate; updateRamp(); } void reset() noexcept { bias.reset (sampleRate, rampDurationSeconds); } //============================================================================== /** Returns the result of processing a single sample. */ template SampleType processSample (SampleType inputSample) noexcept { return inputSample + bias.getNextValue(); } //============================================================================== /** Processes the input and output buffers supplied in the processing context. */ template void process (const ProcessContext& context) noexcept { auto&& inBlock = context.getInputBlock(); auto&& outBlock = context.getOutputBlock(); jassert (inBlock.getNumChannels() == outBlock.getNumChannels()); jassert (inBlock.getNumSamples() == outBlock.getNumSamples()); auto len = inBlock.getNumSamples(); auto numChannels = inBlock.getNumChannels(); if (context.isBypassed) { bias.skip (static_cast (len)); if (context.usesSeparateInputAndOutputBlocks()) outBlock.copyFrom (inBlock); return; } if (numChannels == 1) { auto* src = inBlock.getChannelPointer (0); auto* dst = outBlock.getChannelPointer (0); for (size_t i = 0; i < len; ++i) dst[i] = src[i] + bias.getNextValue(); } else { JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386) auto* biases = static_cast (alloca (sizeof (FloatType) * len)); for (size_t i = 0; i < len; ++i) biases[i] = bias.getNextValue(); for (size_t chan = 0; chan < numChannels; ++chan) FloatVectorOperations::add (outBlock.getChannelPointer (chan), inBlock.getChannelPointer (chan), biases, static_cast (len)); JUCE_END_IGNORE_WARNINGS_MSVC } } private: //============================================================================== SmoothedValue bias; double sampleRate = 0, rampDurationSeconds = 0; void updateRamp() noexcept { if (sampleRate > 0) bias.reset (sampleRate, rampDurationSeconds); } }; } // namespace dsp } // namespace juce