mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
New class: CatmullRomInterpolator
This commit is contained in:
parent
66a52e505b
commit
5a7be115ca
6 changed files with 304 additions and 161 deletions
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2015 - ROLI Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
struct CatmullRomAlgorithm
|
||||
{
|
||||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept
|
||||
{
|
||||
const float y0 = inputs[3];
|
||||
const float y1 = inputs[2];
|
||||
const float y2 = inputs[1];
|
||||
const float y3 = inputs[0];
|
||||
|
||||
const float halfY0 = 0.5f * y0;
|
||||
const float halfY3 = 0.5f * y3;
|
||||
|
||||
return y1 + offset * ((0.5f * y2 - halfY0)
|
||||
+ (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1))
|
||||
+ (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2))))));
|
||||
}
|
||||
};
|
||||
|
||||
CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); }
|
||||
CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {}
|
||||
|
||||
void CatmullRomInterpolator::reset() noexcept
|
||||
{
|
||||
subSamplePos = 1.0;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i)
|
||||
lastInputSamples[i] = 0;
|
||||
}
|
||||
|
||||
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
|
||||
{
|
||||
return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
|
||||
}
|
||||
|
||||
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
|
||||
{
|
||||
return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2015 - ROLI Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
Interpolator for resampling a stream of floats using Catmull-Rom interpolation.
|
||||
|
||||
Note that the resampler is 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 CatmullRomInterpolator
|
||||
object.
|
||||
|
||||
@see LagrangeInterpolator
|
||||
*/
|
||||
class JUCE_API CatmullRomInterpolator
|
||||
{
|
||||
public:
|
||||
CatmullRomInterpolator() noexcept;
|
||||
~CatmullRomInterpolator() noexcept;
|
||||
|
||||
/** Resets the state of the interpolator.
|
||||
Call this when there's a break in the continuity of the input data stream.
|
||||
*/
|
||||
void reset() noexcept;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
private:
|
||||
float lastInputSamples[5];
|
||||
double subSamplePos;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator)
|
||||
};
|
||||
|
|
@ -22,31 +22,141 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace LagrangeHelpers
|
||||
namespace
|
||||
{
|
||||
template <int k>
|
||||
struct ResampleHelper
|
||||
static forcedinline void pushInterpolationSample (float* lastInputSamples, const float newValue) noexcept
|
||||
{
|
||||
static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ResampleHelper <0>
|
||||
{
|
||||
static forcedinline void calc (float&, float) {}
|
||||
};
|
||||
|
||||
template <int k>
|
||||
static forcedinline float calcCoefficient (float input, const float offset) noexcept
|
||||
{
|
||||
ResampleHelper <0 - k>::calc (input, -2.0f - offset);
|
||||
ResampleHelper <1 - k>::calc (input, -1.0f - offset);
|
||||
ResampleHelper <2 - k>::calc (input, 0.0f - offset);
|
||||
ResampleHelper <3 - k>::calc (input, 1.0f - offset);
|
||||
ResampleHelper <4 - k>::calc (input, 2.0f - offset);
|
||||
return input;
|
||||
lastInputSamples[4] = lastInputSamples[3];
|
||||
lastInputSamples[3] = lastInputSamples[2];
|
||||
lastInputSamples[2] = lastInputSamples[1];
|
||||
lastInputSamples[1] = lastInputSamples[0];
|
||||
lastInputSamples[0] = newValue;
|
||||
}
|
||||
|
||||
static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept
|
||||
{
|
||||
if (numOut >= 5)
|
||||
{
|
||||
for (int i = 0; i < 5; ++i)
|
||||
lastInputSamples[i] = input[--numOut];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numOut; ++i)
|
||||
pushInterpolationSample (lastInputSamples, input[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InterpolatorType>
|
||||
static int interpolate (float* lastInputSamples, double& subSamplePos, const double actualRatio,
|
||||
const float* in, float* out, const int numOut) noexcept
|
||||
{
|
||||
if (actualRatio == 1.0)
|
||||
{
|
||||
memcpy (out, in, (size_t) numOut * sizeof (float));
|
||||
pushInterpolationSamples (lastInputSamples, in, numOut);
|
||||
return numOut;
|
||||
}
|
||||
|
||||
const float* const originalIn = in;
|
||||
double pos = subSamplePos;
|
||||
|
||||
if (actualRatio < 1.0)
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
if (pos >= 1.0)
|
||||
{
|
||||
pushInterpolationSample (lastInputSamples, *in++);
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
|
||||
pos += actualRatio;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
while (pos < actualRatio)
|
||||
{
|
||||
pushInterpolationSample (lastInputSamples, *in++);
|
||||
pos += 1.0;
|
||||
}
|
||||
|
||||
pos -= actualRatio;
|
||||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
|
||||
}
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
return (int) (in - originalIn);
|
||||
}
|
||||
|
||||
template <typename InterpolatorType>
|
||||
static int interpolateAdding (float* lastInputSamples, double& subSamplePos, const double actualRatio,
|
||||
const float* in, float* out, const int numOut, const float gain) noexcept
|
||||
{
|
||||
if (actualRatio == 1.0)
|
||||
{
|
||||
FloatVectorOperations::addWithMultiply (out, in, gain, numOut);
|
||||
pushInterpolationSamples (lastInputSamples, in, numOut);
|
||||
return numOut;
|
||||
}
|
||||
|
||||
const float* const originalIn = in;
|
||||
double pos = subSamplePos;
|
||||
|
||||
if (actualRatio < 1.0)
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
if (pos >= 1.0)
|
||||
{
|
||||
pushInterpolationSample (lastInputSamples, *in++);
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
|
||||
pos += actualRatio;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
while (pos < actualRatio)
|
||||
{
|
||||
pushInterpolationSample (lastInputSamples, *in++);
|
||||
pos += 1.0;
|
||||
}
|
||||
|
||||
pos -= actualRatio;
|
||||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
|
||||
}
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
return (int) (in - originalIn);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <int k>
|
||||
struct LagrangeResampleHelper
|
||||
{
|
||||
static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct LagrangeResampleHelper<0>
|
||||
{
|
||||
static forcedinline void calc (float&, float) noexcept {}
|
||||
};
|
||||
|
||||
struct LagrangeAlgorithm
|
||||
{
|
||||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept
|
||||
{
|
||||
return calcCoefficient<0> (inputs[4], offset)
|
||||
|
|
@ -56,19 +166,20 @@ namespace LagrangeHelpers
|
|||
+ calcCoefficient<4> (inputs[0], offset);
|
||||
}
|
||||
|
||||
static forcedinline void push (float* inputs, const float newValue) noexcept
|
||||
template <int k>
|
||||
static forcedinline float calcCoefficient (float input, const float offset) noexcept
|
||||
{
|
||||
inputs[4] = inputs[3];
|
||||
inputs[3] = inputs[2];
|
||||
inputs[2] = inputs[1];
|
||||
inputs[1] = inputs[0];
|
||||
inputs[0] = newValue;
|
||||
LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset);
|
||||
LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset);
|
||||
LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset);
|
||||
LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset);
|
||||
LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
LagrangeInterpolator::LagrangeInterpolator() { reset(); }
|
||||
LagrangeInterpolator::~LagrangeInterpolator() {}
|
||||
LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); }
|
||||
LagrangeInterpolator::~LagrangeInterpolator() noexcept {}
|
||||
|
||||
void LagrangeInterpolator::reset() noexcept
|
||||
{
|
||||
|
|
@ -78,129 +189,12 @@ void LagrangeInterpolator::reset() noexcept
|
|||
lastInputSamples[i] = 0;
|
||||
}
|
||||
|
||||
int LagrangeInterpolator::process (const double actualRatio, const float* in,
|
||||
float* out, const int numOut) noexcept
|
||||
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
|
||||
{
|
||||
if (actualRatio == 1.0)
|
||||
{
|
||||
memcpy (out, in, (size_t) numOut * sizeof (float));
|
||||
|
||||
if (numOut >= 4)
|
||||
{
|
||||
const float* end = in + numOut;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
lastInputSamples[i] = *--end;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numOut; ++i)
|
||||
LagrangeHelpers::push (lastInputSamples, in[i]);
|
||||
}
|
||||
|
||||
return numOut;
|
||||
}
|
||||
|
||||
const float* const originalIn = in;
|
||||
double pos = subSamplePos;
|
||||
|
||||
if (actualRatio < 1.0)
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
if (pos >= 1.0)
|
||||
{
|
||||
LagrangeHelpers::push (lastInputSamples, *in++);
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
|
||||
pos += actualRatio;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
while (pos < actualRatio)
|
||||
{
|
||||
LagrangeHelpers::push (lastInputSamples, *in++);
|
||||
pos += 1.0;
|
||||
}
|
||||
|
||||
pos -= actualRatio;
|
||||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
|
||||
}
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
return (int) (in - originalIn);
|
||||
return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
|
||||
}
|
||||
|
||||
int LagrangeInterpolator::processAdding (const double actualRatio, const float* in,
|
||||
float* out, const int numOut, const float gain) noexcept
|
||||
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
|
||||
{
|
||||
if (actualRatio == 1.0)
|
||||
{
|
||||
if (gain != 1.0f)
|
||||
{
|
||||
for (int i = 0; i < numOut; ++i)
|
||||
out[i] += in[i] * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numOut; ++i)
|
||||
out[i] += in[i];
|
||||
}
|
||||
|
||||
if (numOut >= 4)
|
||||
{
|
||||
const float* end = in + numOut;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
lastInputSamples[i] = *--end;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numOut; ++i)
|
||||
LagrangeHelpers::push (lastInputSamples, in[i]);
|
||||
}
|
||||
|
||||
return numOut;
|
||||
}
|
||||
|
||||
const float* const originalIn = in;
|
||||
double pos = subSamplePos;
|
||||
|
||||
if (actualRatio < 1.0)
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
if (pos >= 1.0)
|
||||
{
|
||||
LagrangeHelpers::push (lastInputSamples, *in++);
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
|
||||
pos += actualRatio;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = numOut; --i >= 0;)
|
||||
{
|
||||
while (pos < actualRatio)
|
||||
{
|
||||
LagrangeHelpers::push (lastInputSamples, *in++);
|
||||
pos += 1.0;
|
||||
}
|
||||
|
||||
pos -= actualRatio;
|
||||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
|
||||
}
|
||||
}
|
||||
|
||||
subSamplePos = pos;
|
||||
return (int) (in - originalIn);
|
||||
return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
|
||||
#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Interpolator for resampling a stream of floats using 4-point lagrange interpolation.
|
||||
|
||||
|
|
@ -35,12 +31,14 @@
|
|||
it any new data. And like with any other stateful filter, if you're resampling
|
||||
multiple channels, make sure each one uses its own LagrangeInterpolator
|
||||
object.
|
||||
|
||||
@see CatmullRomInterpolator
|
||||
*/
|
||||
class JUCE_API LagrangeInterpolator
|
||||
{
|
||||
public:
|
||||
LagrangeInterpolator();
|
||||
~LagrangeInterpolator();
|
||||
LagrangeInterpolator() noexcept;
|
||||
~LagrangeInterpolator() noexcept;
|
||||
|
||||
/** Resets the state of the interpolator.
|
||||
Call this when there's a break in the continuity of the input data stream.
|
||||
|
|
@ -89,6 +87,3 @@ private:
|
|||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ namespace juce
|
|||
#include "buffers/juce_FloatVectorOperations.cpp"
|
||||
#include "effects/juce_IIRFilter.cpp"
|
||||
#include "effects/juce_LagrangeInterpolator.cpp"
|
||||
#include "effects/juce_CatmullRomInterpolator.cpp"
|
||||
#include "effects/juce_FFT.cpp"
|
||||
#include "midi/juce_MidiBuffer.cpp"
|
||||
#include "midi/juce_MidiFile.cpp"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace juce
|
|||
#include "effects/juce_Decibels.h"
|
||||
#include "effects/juce_IIRFilter.h"
|
||||
#include "effects/juce_LagrangeInterpolator.h"
|
||||
#include "effects/juce_CatmullRomInterpolator.h"
|
||||
#include "effects/juce_FFT.h"
|
||||
#include "effects/juce_LinearSmoothedValue.h"
|
||||
#include "effects/juce_Reverb.h"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue