|
|
@@ -63,8 +63,8 @@ public: |
|
|
|
return dsp::AudioBlock<SampleType> (buffer).getSubBlock (0, numSamples);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) = 0;
|
|
|
|
virtual void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) = 0;
|
|
|
|
virtual void processSamplesUp (dsp::AudioBlock<SampleType>&) = 0;
|
|
|
|
virtual void processSamplesDown (dsp::AudioBlock<SampleType>&) = 0;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
//===============================================================================
|
|
|
@@ -88,10 +88,10 @@ public: |
|
|
|
//===============================================================================
|
|
|
|
SampleType getLatencyInSamples() override
|
|
|
|
{
|
|
|
|
return 0.f;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType>& inputBlock) override
|
|
|
|
{
|
|
|
|
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -101,7 +101,7 @@ public: |
|
|
|
inputBlock.getChannelPointer (channel), static_cast<int> (inputBlock.getNumSamples()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType>& outputBlock) override
|
|
|
|
{
|
|
|
|
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -109,8 +109,6 @@ public: |
|
|
|
outputBlock.copy (OversamplingEngine<SampleType>::getProcessedSamples (outputBlock.getNumSamples()));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
//===============================================================================
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OversamplingDummy)
|
|
|
|
};
|
|
|
|
|
|
|
@@ -121,10 +119,9 @@ private: |
|
|
|
leading to specific processing optimizations.
|
|
|
|
*/
|
|
|
|
template <typename SampleType>
|
|
|
|
class Oversampling2TimesEquirippleFIR : public OversamplingEngine<SampleType>
|
|
|
|
class Oversampling2TimesEquirippleFIR : public OversamplingEngine<SampleType>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
//===============================================================================
|
|
|
|
Oversampling2TimesEquirippleFIR (size_t numChans,
|
|
|
|
SampleType normalizedTransitionWidthUp,
|
|
|
|
SampleType stopbandAttenuationdBUp,
|
|
|
@@ -132,7 +129,7 @@ public: |
|
|
|
SampleType stopbandAttenuationdBDown)
|
|
|
|
: OversamplingEngine<SampleType> (numChans, 2)
|
|
|
|
{
|
|
|
|
coefficientsUp = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp);
|
|
|
|
coefficientsUp = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp);
|
|
|
|
coefficientsDown = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown);
|
|
|
|
|
|
|
|
auto N = coefficientsUp.getFilterOrder() + 1;
|
|
|
@@ -167,7 +164,7 @@ public: |
|
|
|
position.fill (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType>& inputBlock) override
|
|
|
|
{
|
|
|
|
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -192,6 +189,7 @@ public: |
|
|
|
|
|
|
|
// Convolution
|
|
|
|
auto out = static_cast<SampleType> (0.0);
|
|
|
|
|
|
|
|
for (size_t k = 0; k < Ndiv2; k += 2)
|
|
|
|
out += (buf[k] + buf[N - k - 1]) * fir[k];
|
|
|
|
|
|
|
@@ -206,7 +204,7 @@ public: |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType>& outputBlock) override
|
|
|
|
{
|
|
|
|
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -234,6 +232,7 @@ public: |
|
|
|
|
|
|
|
// Convolution
|
|
|
|
auto out = static_cast<SampleType> (0.0);
|
|
|
|
|
|
|
|
for (size_t k = 0; k < Ndiv2; k += 2)
|
|
|
|
out += (buf[k] + buf[N - k - 1]) * fir[k];
|
|
|
|
|
|
|
@@ -273,10 +272,9 @@ private: |
|
|
|
phase, and provided with a method to get the exact resulting latency.
|
|
|
|
*/
|
|
|
|
template <typename SampleType>
|
|
|
|
class Oversampling2TimesPolyphaseIIR : public OversamplingEngine<SampleType>
|
|
|
|
class Oversampling2TimesPolyphaseIIR : public OversamplingEngine<SampleType>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
//===============================================================================
|
|
|
|
Oversampling2TimesPolyphaseIIR (size_t numChans,
|
|
|
|
SampleType normalizedTransitionWidthUp,
|
|
|
|
SampleType stopbandAttenuationdBUp,
|
|
|
@@ -292,14 +290,14 @@ public: |
|
|
|
dsp::IIR::Coefficients<SampleType> coeffsDown = getCoefficients (structureDown);
|
|
|
|
latency += static_cast<SampleType> (-(coeffsDown.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * MathConstants<double>::twoPi));
|
|
|
|
|
|
|
|
for (auto i = 0; i < structureUp.directPath.size(); ++i)
|
|
|
|
coefficientsUp.add (structureUp.directPath[i].coefficients[0]);
|
|
|
|
for (auto& i : structureUp.directPath)
|
|
|
|
coefficientsUp.add (i.coefficients[0]);
|
|
|
|
|
|
|
|
for (auto i = 1; i < structureUp.delayedPath.size(); ++i)
|
|
|
|
coefficientsUp.add (structureUp.delayedPath[i].coefficients[0]);
|
|
|
|
|
|
|
|
for (auto i = 0; i < structureDown.directPath.size(); ++i)
|
|
|
|
coefficientsDown.add (structureDown.directPath[i].coefficients[0]);
|
|
|
|
for (auto& i : structureDown.directPath)
|
|
|
|
coefficientsDown.add (i.coefficients[0]);
|
|
|
|
|
|
|
|
for (auto i = 1; i < structureDown.delayedPath.size(); ++i)
|
|
|
|
coefficientsDown.add (structureDown.delayedPath[i].coefficients[0]);
|
|
|
@@ -320,13 +318,12 @@ public: |
|
|
|
void reset() override
|
|
|
|
{
|
|
|
|
OversamplingEngine<SampleType>::reset();
|
|
|
|
|
|
|
|
v1Up.clear();
|
|
|
|
v1Down.clear();
|
|
|
|
delayDown.fill (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override
|
|
|
|
void processSamplesUp (dsp::AudioBlock<SampleType>& inputBlock) override
|
|
|
|
{
|
|
|
|
jassert (inputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (inputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -349,6 +346,7 @@ public: |
|
|
|
{
|
|
|
|
// Direct path cascaded allpass filters
|
|
|
|
auto input = samples[i];
|
|
|
|
|
|
|
|
for (auto n = 0; n < directStages; ++n)
|
|
|
|
{
|
|
|
|
auto alpha = coeffs[n];
|
|
|
@@ -362,6 +360,7 @@ public: |
|
|
|
|
|
|
|
// Delayed path cascaded allpass filters
|
|
|
|
input = samples[i];
|
|
|
|
|
|
|
|
for (auto n = directStages; n < numStages; ++n)
|
|
|
|
{
|
|
|
|
auto alpha = coeffs[n];
|
|
|
@@ -379,7 +378,7 @@ public: |
|
|
|
snapToZero (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override
|
|
|
|
void processSamplesDown (dsp::AudioBlock<SampleType>& outputBlock) override
|
|
|
|
{
|
|
|
|
jassert (outputBlock.getNumChannels() <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumChannels()));
|
|
|
|
jassert (outputBlock.getNumSamples() * OversamplingEngine<SampleType>::factor <= static_cast<size_t> (OversamplingEngine<SampleType>::buffer.getNumSamples()));
|
|
|
@@ -403,6 +402,7 @@ public: |
|
|
|
{
|
|
|
|
// Direct path cascaded allpass filters
|
|
|
|
auto input = bufferSamples[i << 1];
|
|
|
|
|
|
|
|
for (auto n = 0; n < directStages; ++n)
|
|
|
|
{
|
|
|
|
auto alpha = coeffs[n];
|
|
|
@@ -410,10 +410,12 @@ public: |
|
|
|
lv1[n] = input - alpha * output;
|
|
|
|
input = output;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto directOut = input;
|
|
|
|
|
|
|
|
// Delayed path cascaded allpass filters
|
|
|
|
input = bufferSamples[(i << 1) + 1];
|
|
|
|
|
|
|
|
for (auto n = directStages; n < numStages; ++n)
|
|
|
|
{
|
|
|
|
auto alpha = coeffs[n];
|
|
|
@@ -467,12 +469,12 @@ private: |
|
|
|
*/
|
|
|
|
const dsp::IIR::Coefficients<SampleType> getCoefficients (typename dsp::FilterDesign<SampleType>::IIRPolyphaseAllpassStructure& structure) const
|
|
|
|
{
|
|
|
|
dsp::Polynomial<SampleType> numerator1 ({ static_cast<SampleType> (1.0) });
|
|
|
|
dsp::Polynomial<SampleType> denominator1 ({ static_cast<SampleType> (1.0) });
|
|
|
|
dsp::Polynomial<SampleType> numerator2 ({ static_cast<SampleType> (1.0) });
|
|
|
|
dsp::Polynomial<SampleType> denominator2 ({ static_cast<SampleType> (1.0) });
|
|
|
|
constexpr auto one = static_cast<SampleType> (1.0);
|
|
|
|
|
|
|
|
dsp::Polynomial<SampleType> temp;
|
|
|
|
dsp::Polynomial<SampleType> numerator1 ({ one });
|
|
|
|
dsp::Polynomial<SampleType> denominator1 ({ one });
|
|
|
|
dsp::Polynomial<SampleType> numerator2 ({ one });
|
|
|
|
dsp::Polynomial<SampleType> denominator2 ({ one });
|
|
|
|
|
|
|
|
for (auto n = 0; n < structure.directPath.size(); ++n)
|
|
|
|
{
|
|
|
@@ -480,19 +482,13 @@ private: |
|
|
|
|
|
|
|
if (structure.directPath[n].getFilterOrder() == 1)
|
|
|
|
{
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] });
|
|
|
|
numerator1 = numerator1.getProductWith (temp);
|
|
|
|
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[2] });
|
|
|
|
denominator1 = denominator1.getProductWith (temp);
|
|
|
|
numerator1 = numerator1.getProductWith (dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] }));
|
|
|
|
denominator1 = denominator1.getProductWith (dsp::Polynomial<SampleType> ({ one, coeffs[2] }));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] });
|
|
|
|
numerator1 = numerator1.getProductWith (temp);
|
|
|
|
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[3], coeffs[4] });
|
|
|
|
denominator1 = denominator1.getProductWith (temp);
|
|
|
|
numerator1 = numerator1.getProductWith (dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }));
|
|
|
|
denominator1 = denominator1.getProductWith (dsp::Polynomial<SampleType> ({ one, coeffs[3], coeffs[4] }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -502,31 +498,25 @@ private: |
|
|
|
|
|
|
|
if (structure.delayedPath[n].getFilterOrder() == 1)
|
|
|
|
{
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] });
|
|
|
|
numerator2 = numerator2.getProductWith (temp);
|
|
|
|
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[2] });
|
|
|
|
denominator2 = denominator2.getProductWith (temp);
|
|
|
|
numerator2 = numerator2.getProductWith (dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1] }));
|
|
|
|
denominator2 = denominator2.getProductWith (dsp::Polynomial<SampleType> ({ one, coeffs[2] }));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] });
|
|
|
|
numerator2 = numerator2.getProductWith (temp);
|
|
|
|
|
|
|
|
temp = dsp::Polynomial<SampleType> ({ static_cast<SampleType> (1.0), coeffs[3], coeffs[4] });
|
|
|
|
denominator2 = denominator2.getProductWith (temp);
|
|
|
|
numerator2 = numerator2.getProductWith (dsp::Polynomial<SampleType> ({ coeffs[0], coeffs[1], coeffs[2] }));
|
|
|
|
denominator2 = denominator2.getProductWith (dsp::Polynomial<SampleType> ({ one, coeffs[3], coeffs[4] }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dsp::Polynomial<SampleType> numeratorf1 = numerator1.getProductWith (denominator2);
|
|
|
|
dsp::Polynomial<SampleType> numeratorf2 = numerator2.getProductWith (denominator1);
|
|
|
|
dsp::Polynomial<SampleType> numerator = numeratorf1.getSumWith (numeratorf2);
|
|
|
|
dsp::Polynomial<SampleType> denominator = denominator1.getProductWith (denominator2);
|
|
|
|
auto numeratorf1 = numerator1.getProductWith (denominator2);
|
|
|
|
auto numeratorf2 = numerator2.getProductWith (denominator1);
|
|
|
|
auto numerator = numeratorf1.getSumWith (numeratorf2);
|
|
|
|
auto denominator = denominator1.getProductWith (denominator2);
|
|
|
|
|
|
|
|
dsp::IIR::Coefficients<SampleType> coeffs;
|
|
|
|
|
|
|
|
coeffs.coefficients.clear();
|
|
|
|
auto inversion = static_cast<SampleType> (1.0) / denominator[0];
|
|
|
|
auto inversion = one / denominator[0];
|
|
|
|
|
|
|
|
for (auto i = 0; i <= numerator.getOrder(); ++i)
|
|
|
|
coeffs.coefficients.add (numerator[i] * inversion);
|
|
|
@@ -551,7 +541,8 @@ private: |
|
|
|
|
|
|
|
//===============================================================================
|
|
|
|
template <typename SampleType>
|
|
|
|
Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor, FilterType newType, bool newMaxQuality)
|
|
|
|
Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor,
|
|
|
|
FilterType newType, bool newMaxQuality)
|
|
|
|
{
|
|
|
|
jassert (newFactor >= 0 && newFactor <= 4 && newNumChannels > 0);
|
|
|
|
|
|
|
@@ -571,13 +562,13 @@ Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor, |
|
|
|
|
|
|
|
for (size_t n = 0; n < numStages; ++n)
|
|
|
|
{
|
|
|
|
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.f);
|
|
|
|
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.f);
|
|
|
|
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.0f);
|
|
|
|
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.0f);
|
|
|
|
|
|
|
|
auto gaindBStartUp = (isMaximumQuality ? -75.f : -65.f);
|
|
|
|
auto gaindBStartDown = (isMaximumQuality ? -70.f : -60.f);
|
|
|
|
auto gaindBFactorUp = (isMaximumQuality ? 10.f : 8.f);
|
|
|
|
auto gaindBFactorDown = (isMaximumQuality ? 10.f : 8.f);
|
|
|
|
auto gaindBStartUp = (isMaximumQuality ? -75.0f : -65.0f);
|
|
|
|
auto gaindBStartDown = (isMaximumQuality ? -70.0f : -60.0f);
|
|
|
|
auto gaindBFactorUp = (isMaximumQuality ? 10.0f : 8.0f);
|
|
|
|
auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f);
|
|
|
|
|
|
|
|
engines.add (new Oversampling2TimesPolyphaseIIR<SampleType> (numChannels,
|
|
|
|
twUp, gaindBStartUp + gaindBFactorUp * n,
|
|
|
@@ -590,13 +581,13 @@ Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor, |
|
|
|
|
|
|
|
for (size_t n = 0; n < numStages; ++n)
|
|
|
|
{
|
|
|
|
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.f);
|
|
|
|
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.f);
|
|
|
|
auto twUp = (isMaximumQuality ? 0.10f : 0.12f) * (n == 0 ? 0.5f : 1.0f);
|
|
|
|
auto twDown = (isMaximumQuality ? 0.12f : 0.15f) * (n == 0 ? 0.5f : 1.0f);
|
|
|
|
|
|
|
|
auto gaindBStartUp = (isMaximumQuality ? -90.f : -70.f);
|
|
|
|
auto gaindBStartDown = (isMaximumQuality ? -70.f : -60.f);
|
|
|
|
auto gaindBFactorUp = (isMaximumQuality ? 10.f : 8.f);
|
|
|
|
auto gaindBFactorDown = (isMaximumQuality ? 10.f : 8.f);
|
|
|
|
auto gaindBStartUp = (isMaximumQuality ? -90.0f : -70.0f);
|
|
|
|
auto gaindBStartDown = (isMaximumQuality ? -70.0f : -60.0f);
|
|
|
|
auto gaindBFactorUp = (isMaximumQuality ? 10.0f : 8.0f);
|
|
|
|
auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f);
|
|
|
|
|
|
|
|
engines.add (new Oversampling2TimesEquirippleFIR<SampleType> (numChannels,
|
|
|
|
twUp, gaindBStartUp + gaindBFactorUp * n,
|
|
|
@@ -665,7 +656,7 @@ void Oversampling<SampleType>::reset() noexcept |
|
|
|
}
|
|
|
|
|
|
|
|
template <typename SampleType>
|
|
|
|
typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp (const dsp::AudioBlock<SampleType> &inputBlock) noexcept
|
|
|
|
typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp (const dsp::AudioBlock<SampleType>& inputBlock) noexcept
|
|
|
|
{
|
|
|
|
jassert (! engines.isEmpty());
|
|
|
|
|
|
|
@@ -685,7 +676,7 @@ typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp |
|
|
|
}
|
|
|
|
|
|
|
|
template <typename SampleType>
|
|
|
|
void Oversampling<SampleType>::processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) noexcept
|
|
|
|
void Oversampling<SampleType>::processSamplesDown (dsp::AudioBlock<SampleType>& outputBlock) noexcept
|
|
|
|
{
|
|
|
|
jassert (! engines.isEmpty());
|
|
|
|
|
|
|
|