|
- /*
- ==============================================================================
-
- 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.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- 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
- {
-
- /**
- An interpolator base class for resampling streams of floats.
-
- Note that the resamplers are stateful, so when there's a break in the continuity
- of the input stream you're feeding it, you should call reset() before feeding
- it any new data. And like with any other stateful filter, if you're resampling
- multiple channels, make sure each one uses its own interpolator object.
-
- @see LagrangeInterpolator, CatmullRomInterpolator, WindowedSincInterpolator,
- LinearInterpolator, ZeroOrderHoldInterpolator
-
- @tags{Audio}
- */
- template <class InterpolatorTraits, int memorySize>
- class JUCE_API GenericInterpolator
- {
- public:
- GenericInterpolator() noexcept { reset(); }
-
- GenericInterpolator (GenericInterpolator&&) noexcept = default;
- GenericInterpolator& operator= (GenericInterpolator&&) noexcept = default;
-
- /** Returns the latency of the interpolation algorithm in isolation.
-
- In the context of resampling the total latency of a process using
- the interpolator is the base latency divided by the speed ratio.
- */
- static constexpr float getBaseLatency() noexcept
- {
- return InterpolatorTraits::algorithmicLatency;
- }
-
- /** Resets the state of the interpolator.
-
- Call this when there's a break in the continuity of the input data stream.
- */
- void reset() noexcept
- {
- indexBuffer = 0;
- subSamplePos = 1.0;
- std::fill (std::begin (lastInputSamples), std::end (lastInputSamples), 0.0f);
- }
-
- /** Resamples a stream of samples.
-
- @param speedRatio the number of input samples to use for each output sample
- @param inputSamples the source data to read from. This must contain at
- least (speedRatio * numOutputSamplesToProduce) samples.
- @param outputSamples the buffer to write the results into
- @param numOutputSamplesToProduce the number of output samples that should be created
-
- @returns the actual number of input samples that were used
- */
- int process (double speedRatio,
- const float* inputSamples,
- float* outputSamples,
- int numOutputSamplesToProduce) noexcept
- {
- return interpolate (speedRatio, inputSamples, outputSamples, numOutputSamplesToProduce);
- }
-
- /** Resamples a stream of samples.
-
- @param speedRatio the number of input samples to use for each output sample
- @param inputSamples the source data to read from. This must contain at
- least (speedRatio * numOutputSamplesToProduce) samples.
- @param outputSamples the buffer to write the results into
- @param numOutputSamplesToProduce the number of output samples that should be created
- @param numInputSamplesAvailable the number of available input samples. If it needs more samples
- than available, it either wraps back for wrapAround samples, or
- it feeds zeroes
- @param wrapAround if the stream exceeds available samples, it wraps back for
- wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
-
- @returns the actual number of input samples that were used
- */
- int process (double speedRatio,
- const float* inputSamples,
- float* outputSamples,
- int numOutputSamplesToProduce,
- int numInputSamplesAvailable,
- int wrapAround) noexcept
- {
- return interpolate (speedRatio, inputSamples, outputSamples,
- numOutputSamplesToProduce, numInputSamplesAvailable, wrapAround);
- }
-
- /** Resamples a stream of samples, adding the results to the output data
- with a gain.
-
- @param speedRatio the number of input samples to use for each output sample
- @param inputSamples the source data to read from. This must contain at
- least (speedRatio * numOutputSamplesToProduce) samples.
- @param outputSamples the buffer to write the results to - the result values will be added
- to any pre-existing data in this buffer after being multiplied by
- the gain factor
- @param numOutputSamplesToProduce the number of output samples that should be created
- @param gain a gain factor to multiply the resulting samples by before
- adding them to the destination buffer
-
- @returns the actual number of input samples that were used
- */
- int processAdding (double speedRatio,
- const float* inputSamples,
- float* outputSamples,
- int numOutputSamplesToProduce,
- float gain) noexcept
- {
- return interpolateAdding (speedRatio, inputSamples, outputSamples, numOutputSamplesToProduce, gain);
- }
-
- /** Resamples a stream of samples, adding the results to the output data
- with a gain.
-
- @param speedRatio the number of input samples to use for each output sample
- @param inputSamples the source data to read from. This must contain at
- least (speedRatio * numOutputSamplesToProduce) samples.
- @param outputSamples the buffer to write the results to - the result values will be added
- to any pre-existing data in this buffer after being multiplied by
- the gain factor
- @param numOutputSamplesToProduce the number of output samples that should be created
- @param numInputSamplesAvailable the number of available input samples. If it needs more samples
- than available, it either wraps back for wrapAround samples, or
- it feeds zeroes
- @param wrapAround if the stream exceeds available samples, it wraps back for
- wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
- @param gain a gain factor to multiply the resulting samples by before
- adding them to the destination buffer
-
- @returns the actual number of input samples that were used
- */
- int processAdding (double speedRatio,
- const float* inputSamples,
- float* outputSamples,
- int numOutputSamplesToProduce,
- int numInputSamplesAvailable,
- int wrapAround,
- float gain) noexcept
- {
- return interpolateAdding (speedRatio, inputSamples, outputSamples,
- numOutputSamplesToProduce, numInputSamplesAvailable, wrapAround, gain);
- }
-
- private:
- //==============================================================================
- forcedinline void pushInterpolationSample (float newValue) noexcept
- {
- lastInputSamples[indexBuffer] = newValue;
-
- if (++indexBuffer == memorySize)
- indexBuffer = 0;
- }
-
- forcedinline void pushInterpolationSamples (const float* input,
- int numOutputSamplesToProduce) noexcept
- {
- if (numOutputSamplesToProduce >= memorySize)
- {
- const auto* const offsetInput = input + (numOutputSamplesToProduce - memorySize);
-
- for (int i = 0; i < memorySize; ++i)
- pushInterpolationSample (offsetInput[i]);
- }
- else
- {
- for (int i = 0; i < numOutputSamplesToProduce; ++i)
- pushInterpolationSample (input[i]);
- }
- }
-
- forcedinline void pushInterpolationSamples (const float* input,
- int numOutputSamplesToProduce,
- int numInputSamplesAvailable,
- int wrapAround) noexcept
- {
- if (numOutputSamplesToProduce >= memorySize)
- {
- if (numInputSamplesAvailable >= memorySize)
- {
- pushInterpolationSamples (input,
- numOutputSamplesToProduce);
- }
- else
- {
- pushInterpolationSamples (input + ((numOutputSamplesToProduce - numInputSamplesAvailable) - 1),
- numInputSamplesAvailable);
-
- if (wrapAround > 0)
- {
- numOutputSamplesToProduce -= wrapAround;
-
- pushInterpolationSamples (input + ((numOutputSamplesToProduce - (memorySize - numInputSamplesAvailable)) - 1),
- memorySize - numInputSamplesAvailable);
- }
- else
- {
- for (int i = numInputSamplesAvailable; i < memorySize; ++i)
- pushInterpolationSample (0.0f);
- }
- }
- }
- else
- {
- if (numOutputSamplesToProduce > numInputSamplesAvailable)
- {
- for (int i = 0; i < numInputSamplesAvailable; ++i)
- pushInterpolationSample (input[i]);
-
- const auto extraSamples = numOutputSamplesToProduce - numInputSamplesAvailable;
-
- if (wrapAround > 0)
- {
- const auto* const offsetInput = input + (numInputSamplesAvailable - wrapAround);
-
- for (int i = 0; i < extraSamples; ++i)
- pushInterpolationSample (offsetInput[i]);
- }
- else
- {
- for (int i = 0; i < extraSamples; ++i)
- pushInterpolationSample (0.0f);
- }
- }
- else
- {
- for (int i = 0; i < numOutputSamplesToProduce; ++i)
- pushInterpolationSample (input[i]);
- }
- }
- }
-
- //==============================================================================
- int interpolate (double speedRatio,
- const float* input,
- float* output,
- int numOutputSamplesToProduce) noexcept
- {
- auto pos = subSamplePos;
- int numUsed = 0;
-
- while (numOutputSamplesToProduce > 0)
- {
- while (pos >= 1.0)
- {
- pushInterpolationSample (input[numUsed++]);
- pos -= 1.0;
- }
-
- *output++ = InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer);
- pos += speedRatio;
- --numOutputSamplesToProduce;
- }
-
- subSamplePos = pos;
- return numUsed;
- }
-
- int interpolate (double speedRatio,
- const float* input, float* output,
- int numOutputSamplesToProduce,
- int numInputSamplesAvailable,
- int wrap) noexcept
- {
- auto originalIn = input;
- auto pos = subSamplePos;
- bool exceeded = false;
-
- if (speedRatio < 1.0)
- {
- for (int i = numOutputSamplesToProduce; --i >= 0;)
- {
- if (pos >= 1.0)
- {
- if (exceeded)
- {
- pushInterpolationSample (0.0f);
- }
- else
- {
- pushInterpolationSample (*input++);
-
- if (--numInputSamplesAvailable <= 0)
- {
- if (wrap > 0)
- {
- input -= wrap;
- numInputSamplesAvailable += wrap;
- }
- else
- {
- exceeded = true;
- }
- }
- }
-
- pos -= 1.0;
- }
-
- *output++ = InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer);
- pos += speedRatio;
- }
- }
- else
- {
- for (int i = numOutputSamplesToProduce; --i >= 0;)
- {
- while (pos < speedRatio)
- {
- if (exceeded)
- {
- pushInterpolationSample (0);
- }
- else
- {
- pushInterpolationSample (*input++);
-
- if (--numInputSamplesAvailable <= 0)
- {
- if (wrap > 0)
- {
- input -= wrap;
- numInputSamplesAvailable += wrap;
- }
- else
- {
- exceeded = true;
- }
- }
- }
-
- pos += 1.0;
- }
-
- pos -= speedRatio;
- *output++ = InterpolatorTraits::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos), indexBuffer);
- }
- }
-
- subSamplePos = pos;
-
- if (wrap == 0)
- return (int) (input - originalIn);
-
- return ((int) (input - originalIn) + wrap) % wrap;
- }
-
- int interpolateAdding (double speedRatio,
- const float* input,
- float* output,
- int numOutputSamplesToProduce,
- int numInputSamplesAvailable,
- int wrap,
- float gain) noexcept
- {
- auto originalIn = input;
- auto pos = subSamplePos;
- bool exceeded = false;
-
- if (speedRatio < 1.0)
- {
- for (int i = numOutputSamplesToProduce; --i >= 0;)
- {
- if (pos >= 1.0)
- {
- if (exceeded)
- {
- pushInterpolationSample (0.0);
- }
- else
- {
- pushInterpolationSample (*input++);
-
- if (--numInputSamplesAvailable <= 0)
- {
- if (wrap > 0)
- {
- input -= wrap;
- numInputSamplesAvailable += wrap;
- }
- else
- {
- numInputSamplesAvailable = true;
- }
- }
- }
-
- pos -= 1.0;
- }
-
- *output++ += gain * InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer);
- pos += speedRatio;
- }
- }
- else
- {
- for (int i = numOutputSamplesToProduce; --i >= 0;)
- {
- while (pos < speedRatio)
- {
- if (exceeded)
- {
- pushInterpolationSample (0.0);
- }
- else
- {
- pushInterpolationSample (*input++);
-
- if (--numInputSamplesAvailable <= 0)
- {
- if (wrap > 0)
- {
- input -= wrap;
- numInputSamplesAvailable += wrap;
- }
- else
- {
- exceeded = true;
- }
- }
- }
-
- pos += 1.0;
- }
-
- pos -= speedRatio;
- *output++ += gain * InterpolatorTraits::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos), indexBuffer);
- }
- }
-
- subSamplePos = pos;
-
- if (wrap == 0)
- return (int) (input - originalIn);
-
- return ((int) (input - originalIn) + wrap) % wrap;
- }
-
- int interpolateAdding (double speedRatio,
- const float* input,
- float* output,
- int numOutputSamplesToProduce,
- float gain) noexcept
- {
- auto pos = subSamplePos;
- int numUsed = 0;
-
- while (numOutputSamplesToProduce > 0)
- {
- while (pos >= 1.0)
- {
- pushInterpolationSample (input[numUsed++]);
- pos -= 1.0;
- }
-
- *output++ += gain * InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer);
- pos += speedRatio;
- --numOutputSamplesToProduce;
- }
-
- subSamplePos = pos;
- return numUsed;
- }
-
- //==============================================================================
- float lastInputSamples[(size_t) memorySize];
- double subSamplePos = 1.0;
- int indexBuffer = 0;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericInterpolator)
- };
-
- } // namespace juce
|