Browse Source

DSP: Fixed multiple issues with the DSP Oversampling class and updated DSP module plug-in demo code accordingly

tags/2021-05-28
hogliux 8 years ago
parent
commit
f1d6298206
10 changed files with 345 additions and 232 deletions
  1. +6
    -0
      examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_SharedCode.vcxproj
  2. +6
    -0
      examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_StandalonePlugin.vcxproj
  3. +6
    -0
      examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_VST.vcxproj
  4. +2
    -2
      examples/DSP module plugin demo/DSP module plugin demo.jucer
  5. +22
    -2
      examples/DSP module plugin demo/Source/PluginEditor.cpp
  6. +2
    -2
      examples/DSP module plugin demo/Source/PluginEditor.h
  7. +36
    -8
      examples/DSP module plugin demo/Source/PluginProcessor.cpp
  8. +4
    -0
      examples/DSP module plugin demo/Source/PluginProcessor.h
  9. +251
    -211
      modules/juce_dsp/processors/juce_Oversampling.cpp
  10. +10
    -7
      modules/juce_dsp/processors/juce_Oversampling.h

+ 6
- 0
examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_SharedCode.vcxproj View File

@@ -101,6 +101,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl> <Midl>
@@ -143,6 +146,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\Source\PluginProcessor.cpp"/> <ClCompile Include="..\..\Source\PluginProcessor.cpp"/>


+ 6
- 0
examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_StandalonePlugin.vcxproj View File

@@ -104,6 +104,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl> <Midl>
@@ -147,6 +150,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\JuceLibraryCode\include_juce_audio_plugin_client_Standalone.cpp"/> <ClCompile Include="..\..\JuceLibraryCode\include_juce_audio_plugin_client_Standalone.cpp"/>


+ 6
- 0
examples/DSP module plugin demo/Builds/VisualStudio2015/DSPModulePluginDemo_VST.vcxproj View File

@@ -104,6 +104,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl> <Midl>
@@ -147,6 +150,9 @@
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
<OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile> <OutputFile>$(IntDir)\DSPModulePluginDemo.bsc</OutputFile>
</Bscmake> </Bscmake>
<PostBuildEvent>
<Command>if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\JuceLibraryCode\include_juce_audio_plugin_client_VST2.cpp"/> <ClCompile Include="..\..\JuceLibraryCode\include_juce_audio_plugin_client_VST2.cpp"/>


+ 2
- 2
examples/DSP module plugin demo/DSP module plugin demo.jucer View File

@@ -59,10 +59,10 @@
<CONFIGURATIONS> <CONFIGURATIONS>
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64" <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
isDebug="1" optimisation="1" targetName="DSPModulePluginDemo" isDebug="1" optimisation="1" targetName="DSPModulePluginDemo"
warningsAreErrors="0" postbuildCommand=""/>
warningsAreErrors="0" postbuildCommand="if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;"/>
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64" <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
isDebug="0" optimisation="3" targetName="DSPModulePluginDemo" isDebug="0" optimisation="3" targetName="DSPModulePluginDemo"
useRuntimeLibDLL="0" postbuildCommand=""/>
useRuntimeLibDLL="0" postbuildCommand="if &quot;$(ProjectName)&quot;==&quot;$(SolutionName)_VST&quot; copy /Y &quot;$(TargetDir)\$(TargetName).dll&quot; &quot;c:\vstplugins\musical entropy\$(TargetName).dll&quot;"/>
</CONFIGURATIONS> </CONFIGURATIONS>
<MODULEPATHS> <MODULEPATHS>
<MODULEPATH id="juce_core" path="../../modules"/> <MODULEPATH id="juce_core" path="../../modules"/>


+ 22
- 2
examples/DSP module plugin demo/Source/PluginEditor.cpp View File

@@ -123,6 +123,10 @@ DspModulePluginDemoAudioProcessorEditor::DspModulePluginDemoAudioProcessorEditor
cabinetSimButton.addListener (this); cabinetSimButton.addListener (this);
cabinetSimButton.setButtonText (processor.cabinetSimParam->name); cabinetSimButton.setButtonText (processor.cabinetSimParam->name);
addAndMakeVisible (oversamplingButton);
oversamplingButton.addListener (this);
oversamplingButton.setButtonText (processor.oversamplingParam->name);
//============================================================================== //==============================================================================
setSize (600, 400); setSize (600, 400);
} }
@@ -172,9 +176,13 @@ void DspModulePluginDemoAudioProcessorEditor::resized()
//============================================================================== //==============================================================================
auto buttonSlice = bounds.removeFromTop (30); auto buttonSlice = bounds.removeFromTop (30);
cabinetSimButton.setSize (200, bounds.getHeight());
cabinetSimButton.setSize (200, buttonSlice.getHeight());
cabinetSimButton.setCentrePosition (buttonSlice.getCentre()); cabinetSimButton.setCentrePosition (buttonSlice.getCentre());
bounds.removeFromTop (15);
bounds.removeFromTop(5);
buttonSlice = bounds.removeFromTop (30);
oversamplingButton.setSize(200, buttonSlice.getHeight());
oversamplingButton.setCentrePosition(buttonSlice.getCentre());
} }
//============================================================================== //==============================================================================
void DspModulePluginDemoAudioProcessorEditor::comboBoxChanged (ComboBox* box) void DspModulePluginDemoAudioProcessorEditor::comboBoxChanged (ComboBox* box)
@@ -198,3 +206,15 @@ void DspModulePluginDemoAudioProcessorEditor::comboBoxChanged (ComboBox* box)
processor.cabinetTypeParam->operator= (index); processor.cabinetTypeParam->operator= (index);
} }
} }
void DspModulePluginDemoAudioProcessorEditor::buttonClicked (Button* button)
{
if (button == &cabinetSimButton)
{
processor.cabinetSimParam->operator= (cabinetSimButton.getToggleState());
}
else if (button == &oversamplingButton)
{
processor.oversamplingParam->operator= (oversamplingButton.getToggleState());
}
}

+ 2
- 2
examples/DSP module plugin demo/Source/PluginEditor.h View File

@@ -86,7 +86,7 @@ public:
private: private:
//============================================================================== //==============================================================================
void comboBoxChanged (ComboBox*) override; void comboBoxChanged (ComboBox*) override;
void buttonClicked (Button*) override { processor.cabinetSimParam->operator= (cabinetSimButton.getToggleState()); }
void buttonClicked (Button*) override;
//============================================================================== //==============================================================================
DspModulePluginDemoAudioProcessor& processor; DspModulePluginDemoAudioProcessor& processor;
@@ -94,7 +94,7 @@ private:
ScopedPointer<ParameterSlider> inputVolumeSlider, outputVolumeSlider, ScopedPointer<ParameterSlider> inputVolumeSlider, outputVolumeSlider,
lowPassFilterFreqSlider, highPassFilterFreqSlider; lowPassFilterFreqSlider, highPassFilterFreqSlider;
ComboBox stereoBox, slopeBox, waveshaperBox, cabinetTypeBox; ComboBox stereoBox, slopeBox, waveshaperBox, cabinetTypeBox;
ToggleButton cabinetSimButton;
ToggleButton cabinetSimButton, oversamplingButton;
Label inputVolumeLabel, outputVolumeLabel, lowPassFilterFreqLabel, Label inputVolumeLabel, outputVolumeLabel, lowPassFilterFreqLabel,
highPassFilterFreqLabel, stereoLabel, slopeLabel, waveshaperLabel, highPassFilterFreqLabel, stereoLabel, slopeLabel, waveshaperLabel,


+ 36
- 8
examples/DSP module plugin demo/Source/PluginProcessor.cpp View File

@@ -38,6 +38,9 @@ DspModulePluginDemoAudioProcessor::DspModulePluginDemoAudioProcessor()
waveShapers { {std::tanh}, {dsp::FastMathApproximations::tanh} }, waveShapers { {std::tanh}, {dsp::FastMathApproximations::tanh} },
clipping { clip } clipping { clip }
{ {
// Oversampling 2 times with IIR filtering
oversampling = new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, false);
addParameter (inputVolumeParam = new AudioParameterFloat ("INPUT", "Input Volume", { 0.f, 60.f, 0.f, 1.0f }, 0.f, "dB")); addParameter (inputVolumeParam = new AudioParameterFloat ("INPUT", "Input Volume", { 0.f, 60.f, 0.f, 1.0f }, 0.f, "dB"));
addParameter (highPassFilterFreqParam = new AudioParameterFloat ("HPFREQ", "Pre Highpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20.f, "Hz")); addParameter (highPassFilterFreqParam = new AudioParameterFloat ("HPFREQ", "Pre Highpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20.f, "Hz"));
addParameter (lowPassFilterFreqParam = new AudioParameterFloat ("LPFREQ", "Post Lowpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20000.f, "Hz")); addParameter (lowPassFilterFreqParam = new AudioParameterFloat ("LPFREQ", "Post Lowpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20000.f, "Hz"));
@@ -50,12 +53,11 @@ DspModulePluginDemoAudioProcessor::DspModulePluginDemoAudioProcessor()
"Cassette recorder cabinet" }, 0)); "Cassette recorder cabinet" }, 0));
addParameter (cabinetSimParam = new AudioParameterBool ("CABSIM", "Cabinet Sim", false)); addParameter (cabinetSimParam = new AudioParameterBool ("CABSIM", "Cabinet Sim", false));
addParameter (oversamplingParam = new AudioParameterBool ("OVERS", "Oversampling", false));
addParameter (outputVolumeParam = new AudioParameterFloat ("OUTPUT", "Output Volume", { -40.f, 40.f, 0.f, 1.0f }, 0.f, "dB")); addParameter (outputVolumeParam = new AudioParameterFloat ("OUTPUT", "Output Volume", { -40.f, 40.f, 0.f, 1.0f }, 0.f, "dB"));
cabinetType.set (0); cabinetType.set (0);
} }
DspModulePluginDemoAudioProcessor::~DspModulePluginDemoAudioProcessor() DspModulePluginDemoAudioProcessor::~DspModulePluginDemoAudioProcessor()
@@ -82,8 +84,6 @@ void DspModulePluginDemoAudioProcessor::prepareToPlay (double sampleRate, int sa
auto channels = static_cast<uint32> (jmin (getMainBusNumInputChannels(), getMainBusNumOutputChannels())); auto channels = static_cast<uint32> (jmin (getMainBusNumInputChannels(), getMainBusNumOutputChannels()));
dsp::ProcessSpec spec { sampleRate, static_cast<uint32> (samplesPerBlock), channels }; dsp::ProcessSpec spec { sampleRate, static_cast<uint32> (samplesPerBlock), channels };
updateParameters();
lowPassFilter.prepare (spec); lowPassFilter.prepare (spec);
highPassFilter.prepare (spec); highPassFilter.prepare (spec);
@@ -92,6 +92,11 @@ void DspModulePluginDemoAudioProcessor::prepareToPlay (double sampleRate, int sa
convolution.prepare (spec); convolution.prepare (spec);
cabinetType.set (-1); cabinetType.set (-1);
oversampling->initProcessing (static_cast<size_t> (samplesPerBlock));
updateParameters();
reset();
} }
void DspModulePluginDemoAudioProcessor::reset() void DspModulePluginDemoAudioProcessor::reset()
@@ -99,6 +104,7 @@ void DspModulePluginDemoAudioProcessor::reset()
lowPassFilter.reset(); lowPassFilter.reset();
highPassFilter.reset(); highPassFilter.reset();
convolution.reset(); convolution.reset();
oversampling->reset();
} }
void DspModulePluginDemoAudioProcessor::releaseResources() void DspModulePluginDemoAudioProcessor::releaseResources()
@@ -115,20 +121,34 @@ void DspModulePluginDemoAudioProcessor::process (dsp::ProcessContextReplacing<fl
// Note : try frequencies around 700 Hz // Note : try frequencies around 700 Hz
highPassFilter.process (context); highPassFilter.process (context);
// Upsampling
dsp::AudioBlock<float> oversampledBlock;
setLatencySamples (audioCurrentlyOversampled ? roundFloatToInt (oversampling->getLatencyInSamples()) : 0);
if (audioCurrentlyOversampled)
oversampledBlock = oversampling->processSamplesUp (context.getInputBlock());
dsp::ProcessContextReplacing<float> waveshaperContext = audioCurrentlyOversampled ? dsp::ProcessContextReplacing<float> (oversampledBlock) : context;
// Waveshaper processing, for distortion generation, thanks to the input gain // Waveshaper processing, for distortion generation, thanks to the input gain
// The fast tanh can be used instead of std::tanh to reduce the CPU load // The fast tanh can be used instead of std::tanh to reduce the CPU load
auto waveshaperIndex = waveshaperParam->getIndex(); auto waveshaperIndex = waveshaperParam->getIndex();
if (isPositiveAndBelow (waveshaperIndex, (int) numWaveShapers) ) if (isPositiveAndBelow (waveshaperIndex, (int) numWaveShapers) )
{ {
waveShapers[waveshaperIndex].process (context);
waveShapers[waveshaperIndex].process (waveshaperContext);
if (waveshaperIndex == 1) if (waveshaperIndex == 1)
clipping.process(context);
clipping.process (waveshaperContext);
context.getOutputBlock() *= 0.7f;
waveshaperContext.getOutputBlock() *= 0.7f;
} }
// Downsampling
if (audioCurrentlyOversampled)
oversampling->processSamplesDown (context.getOutputBlock());
// Post-lowpass filtering // Post-lowpass filtering
lowPassFilter.process (context); lowPassFilter.process (context);
@@ -209,13 +229,20 @@ bool DspModulePluginDemoAudioProcessor::producesMidi() const
//============================================================================== //==============================================================================
void DspModulePluginDemoAudioProcessor::updateParameters() void DspModulePluginDemoAudioProcessor::updateParameters()
{ {
auto newOversampling = oversamplingParam->get();
if (newOversampling != audioCurrentlyOversampled)
{
audioCurrentlyOversampled = newOversampling;
oversampling->reset();
}
//==============================================================================
auto inputdB = Decibels::decibelsToGain (inputVolumeParam->get()); auto inputdB = Decibels::decibelsToGain (inputVolumeParam->get());
auto outputdB = Decibels::decibelsToGain (outputVolumeParam->get()); auto outputdB = Decibels::decibelsToGain (outputVolumeParam->get());
if (inputVolume.getGainLinear() != inputdB) inputVolume.setGainLinear (inputdB); if (inputVolume.getGainLinear() != inputdB) inputVolume.setGainLinear (inputdB);
if (outputVolume.getGainLinear() != outputdB) outputVolume.setGainLinear (outputdB); if (outputVolume.getGainLinear() != outputdB) outputVolume.setGainLinear (outputdB);
dsp::IIR::Coefficients<float>::Ptr newHighPassCoeffs, newLowPassCoeffs;
auto newSlopeType = slopeParam->getIndex(); auto newSlopeType = slopeParam->getIndex();
if (newSlopeType == 0) if (newSlopeType == 0)
@@ -246,6 +273,7 @@ void DspModulePluginDemoAudioProcessor::updateParameters()
} }
cabinetIsBypassed = ! cabinetSimParam->get(); cabinetIsBypassed = ! cabinetSimParam->get();
} }
//============================================================================== //==============================================================================


+ 4
- 0
examples/DSP module plugin demo/Source/PluginProcessor.h View File

@@ -88,6 +88,7 @@ public:
AudioParameterChoice* cabinetTypeParam; AudioParameterChoice* cabinetTypeParam;
AudioParameterBool* cabinetSimParam; AudioParameterBool* cabinetSimParam;
AudioParameterBool* oversamplingParam;
private: private:
//============================================================================== //==============================================================================
@@ -103,6 +104,9 @@ private:
dsp::Gain<float> inputVolume, outputVolume; dsp::Gain<float> inputVolume, outputVolume;
ScopedPointer<dsp::Oversampling<float>> oversampling;
bool audioCurrentlyOversampled = false;
Atomic<int> cabinetType; Atomic<int> cabinetType;
bool cabinetIsBypassed = false; bool cabinetIsBypassed = false;


+ 251
- 211
modules/juce_dsp/processors/juce_Oversampling.cpp View File

@@ -34,7 +34,12 @@ class OversamplingEngine
{ {
public: public:
//=============================================================================== //===============================================================================
OversamplingEngine (size_t newFactor) { factor = newFactor; }
OversamplingEngine (size_t newNumChannels, size_t newFactor)
{
numChannels = newNumChannels;
factor = newFactor;
}
virtual ~OversamplingEngine() {} virtual ~OversamplingEngine() {}
//=============================================================================== //===============================================================================
@@ -43,7 +48,7 @@ public:
virtual void initProcessing (size_t maximumNumberOfSamplesBeforeOversampling) virtual void initProcessing (size_t maximumNumberOfSamplesBeforeOversampling)
{ {
buffer.setSize (1, static_cast<int> (maximumNumberOfSamplesBeforeOversampling * factor));
buffer.setSize (static_cast<int> (numChannels), static_cast<int> (maximumNumberOfSamplesBeforeOversampling * factor), false, false, true);
} }
virtual void reset() virtual void reset()
@@ -51,16 +56,19 @@ public:
buffer.clear(); buffer.clear();
} }
SampleType* getProcessedSamples() { return buffer.getWritePointer (0); }
size_t getNumProcessedSamples() { return static_cast<size_t> (buffer.getNumSamples()); }
dsp::AudioBlock<SampleType> getProcessedSamples (size_t numSamples)
{
return dsp::AudioBlock<SampleType> (buffer).getSubBlock (0, numSamples);
}
virtual void processSamplesUp (SampleType *samples, size_t numSamples) = 0;
virtual void processSamplesDown (SampleType *samples, size_t numSamples) = 0;
virtual void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) = 0;
virtual void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) = 0;
protected: protected:
//=============================================================================== //===============================================================================
AudioBuffer<SampleType> buffer; AudioBuffer<SampleType> buffer;
size_t factor; size_t factor;
size_t numChannels;
}; };
@@ -73,7 +81,7 @@ class OversamplingDummy : public OversamplingEngine<SampleType>
{ {
public: public:
//=============================================================================== //===============================================================================
OversamplingDummy() : OversamplingEngine<SampleType>(1) {}
OversamplingDummy (size_t numChannels) : OversamplingEngine<SampleType> (numChannels, 1) {}
~OversamplingDummy() {} ~OversamplingDummy() {}
//=============================================================================== //===============================================================================
@@ -82,20 +90,22 @@ public:
return 0.f; return 0.f;
} }
void processSamplesUp (SampleType *samples, size_t numSamples) override
void processSamplesUp (dsp::AudioBlock<SampleType> &inputBlock) override
{ {
auto bufferSamples = this->buffer.getWritePointer (0);
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()));
for (size_t i = 0; i < numSamples; i++)
bufferSamples[i] = samples[i];
for (size_t channel = 0; channel < inputBlock.getNumChannels(); channel++)
OversamplingEngine<SampleType>::buffer.copyFrom (static_cast<int> (channel), 0,
inputBlock.getChannelPointer (channel), static_cast<int> (inputBlock.getNumSamples()));
} }
void processSamplesDown (SampleType *samples, size_t numSamples) override
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) override
{ {
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (0);
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()));
for (size_t i = 0; i < numSamples; i++)
samples[i] = bufferSamples[i];
outputBlock.copy (OversamplingEngine<SampleType>::getProcessedSamples (outputBlock.getNumSamples()));
} }
private: private:
@@ -104,8 +114,7 @@ private:
}; };
//=============================================================================== //===============================================================================
/**
Oversampling engine class performing 2 times oversampling using the Filter
/** Oversampling engine class performing 2 times oversampling using the Filter
Design FIR Equiripple method. The resulting filter is linear phase, Design FIR Equiripple method. The resulting filter is linear phase,
symmetric, and has every two samples but the middle one equal to zero, symmetric, and has every two samples but the middle one equal to zero,
leading to specific processing optimizations. leading to specific processing optimizations.
@@ -115,21 +124,26 @@ class Oversampling2TimesEquirippleFIR : public OversamplingEngine<SampleType>
{ {
public: public:
//=============================================================================== //===============================================================================
Oversampling2TimesEquirippleFIR (SampleType normalizedTransitionWidthUp,
Oversampling2TimesEquirippleFIR (size_t numChannels,
SampleType normalizedTransitionWidthUp,
SampleType stopbandAttenuationdBUp, SampleType stopbandAttenuationdBUp,
SampleType normalizedTransitionWidthDown, SampleType normalizedTransitionWidthDown,
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (2)
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (numChannels, 2)
{ {
coefficientsUp = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp); coefficientsUp = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp);
coefficientsDown = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown); coefficientsDown = *dsp::FilterDesign<SampleType>::designFIRLowpassHalfBandEquirippleMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown);
auto N = coefficientsDown.getFilterOrder() + 1;
auto N = coefficientsUp.getFilterOrder() + 1;
stateUp.setSize (static_cast<int> (numChannels), static_cast<int> (N));
N = coefficientsDown.getFilterOrder() + 1;
auto Ndiv2 = N / 2; auto Ndiv2 = N / 2;
auto Ndiv4 = Ndiv2 / 2; auto Ndiv4 = Ndiv2 / 2;
stateUp.setSize (1, static_cast<int> (coefficientsUp.getFilterOrder() + 1));
stateDown.setSize (1, static_cast<int> (N));
stateDown2.setSize (1, static_cast<int> (Ndiv4));
stateDown.setSize (static_cast<int> (numChannels), static_cast<int> (N));
stateDown2.setSize (static_cast<int> (numChannels), static_cast<int> (Ndiv4 + 1));
position.resize (static_cast<int> (numChannels));
} }
~Oversampling2TimesEquirippleFIR() {} ~Oversampling2TimesEquirippleFIR() {}
@@ -137,7 +151,7 @@ public:
//=============================================================================== //===============================================================================
SampleType getLatencyInSamples() override SampleType getLatencyInSamples() override
{ {
return static_cast<SampleType> (coefficientsUp.getFilterOrder() + coefficientsDown.getFilterOrder());
return static_cast<SampleType> (coefficientsUp.getFilterOrder() + coefficientsDown.getFilterOrder()) * 0.5f;
} }
void reset() override void reset() override
@@ -148,83 +162,103 @@ public:
stateDown.clear(); stateDown.clear();
stateDown2.clear(); stateDown2.clear();
position = 0;
position.fill (0);
} }
void processSamplesUp (SampleType *samples, size_t numSamples) 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()));
// Initialization // Initialization
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (0);
auto fir = coefficientsUp.getRawCoefficients(); auto fir = coefficientsUp.getRawCoefficients();
auto buf = stateUp.getWritePointer (0);
auto N = coefficientsUp.getFilterOrder() + 1; auto N = coefficientsUp.getFilterOrder() + 1;
auto Ndiv2 = N / 2; auto Ndiv2 = N / 2;
auto numSamples = inputBlock.getNumSamples();
// Processing // Processing
for (size_t i = 0; i < numSamples; i++)
for (size_t channel = 0; channel < inputBlock.getNumChannels(); channel++)
{ {
// Input
buf[N - 1] = 2 * samples[i];
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (static_cast<int> (channel));
auto buf = stateUp.getWritePointer (static_cast<int> (channel));
auto samples = inputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; i++)
{
// Input
buf[N - 1] = 2 * samples[i];
// 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];
// 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];
// Outputs
bufferSamples[i << 1] = out;
bufferSamples[(i << 1) + 1] = buf[Ndiv2 + 1] * fir[Ndiv2];
// Outputs
bufferSamples[i << 1] = out;
bufferSamples[(i << 1) + 1] = buf[Ndiv2 + 1] * fir[Ndiv2];
// Shift data
for (size_t k = 0; k < N - 2; k+=2)
buf[k] = buf[k + 2];
// Shift data
for (size_t k = 0; k < N - 2; k += 2)
buf[k] = buf[k + 2];
}
} }
} }
void processSamplesDown (SampleType *samples, size_t numSamples) 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()));
// Initialization // Initialization
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (0);
auto fir = coefficientsDown.getRawCoefficients(); auto fir = coefficientsDown.getRawCoefficients();
auto buf = stateDown.getWritePointer (0);
auto buf2 = stateDown2.getWritePointer (0);
auto N = coefficientsDown.getFilterOrder() + 1; auto N = coefficientsDown.getFilterOrder() + 1;
auto Ndiv2 = N / 2; auto Ndiv2 = N / 2;
auto Ndiv4 = Ndiv2 / 2; auto Ndiv4 = Ndiv2 / 2;
auto numSamples = outputBlock.getNumSamples();
// Processing // Processing
for (size_t i = 0; i < numSamples; i++)
for (size_t channel = 0; channel < outputBlock.getNumChannels(); channel++)
{ {
// Input
buf[N - 1] = bufferSamples[2 * i];
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (static_cast<int> (channel));
auto buf = stateDown.getWritePointer (static_cast<int> (channel));
auto buf2 = stateDown2.getWritePointer (static_cast<int> (channel));
auto samples = outputBlock.getChannelPointer (channel);
auto pos = position.getUnchecked (static_cast<int> (channel));
for (size_t i = 0; i < numSamples; i++)
{
// Input
buf[N - 1] = bufferSamples[i << 1];
// 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];
// 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];
// Output
out += buf2[position] * fir[Ndiv2];
buf2[position] = bufferSamples[2 * i + 1];
// Output
out += buf2[pos] * fir[Ndiv2];
buf2[pos] = bufferSamples[(i << 1) + 1];
samples[i] = out;
samples[i] = out;
// Shift data
for (size_t k = 0; k < N - 2; k++)
buf[k] = buf[k + 2];
// Shift data
for (size_t k = 0; k < N - 2; k++)
buf[k] = buf[k + 2];
// Circular buffer
pos = (pos == 0 ? Ndiv4 : pos - 1);
}
// Circular buffer
position = (position == 0 ? Ndiv4 - 1 : position - 1);
position.setUnchecked (static_cast<int> (channel), pos);
} }
} }
private: private:
//=============================================================================== //===============================================================================
dsp::FIR::Coefficients<SampleType> coefficientsUp, coefficientsDown; dsp::FIR::Coefficients<SampleType> coefficientsUp, coefficientsDown;
AudioBuffer<SampleType> stateUp, stateDown, stateDown2; AudioBuffer<SampleType> stateUp, stateDown, stateDown2;
size_t position;
Array<size_t> position;
//=============================================================================== //===============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesEquirippleFIR) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesEquirippleFIR)
@@ -241,10 +275,11 @@ class Oversampling2TimesPolyphaseIIR : public OversamplingEngine<SampleType>
{ {
public: public:
//=============================================================================== //===============================================================================
Oversampling2TimesPolyphaseIIR (SampleType normalizedTransitionWidthUp,
Oversampling2TimesPolyphaseIIR (size_t numChannels,
SampleType normalizedTransitionWidthUp,
SampleType stopbandAttenuationdBUp, SampleType stopbandAttenuationdBUp,
SampleType normalizedTransitionWidthDown, SampleType normalizedTransitionWidthDown,
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (2)
SampleType stopbandAttenuationdBDown) : OversamplingEngine<SampleType> (2, numChannels)
{ {
auto structureUp = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp); auto structureUp = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp);
dsp::IIR::Coefficients<SampleType> coeffsUp = getCoefficients (structureUp); dsp::IIR::Coefficients<SampleType> coeffsUp = getCoefficients (structureUp);
@@ -266,8 +301,9 @@ public:
for (auto i = 1; i < structureDown.delayedPath.size(); i++) for (auto i = 1; i < structureDown.delayedPath.size(); i++)
coefficientsDown.add (structureDown.delayedPath[i].coefficients[0]); coefficientsDown.add (structureDown.delayedPath[i].coefficients[0]);
v1Up.resize (coefficientsUp.size());
v1Down.resize (coefficientsDown.size());
v1Up.setSize (static_cast<int> (numChannels), coefficientsUp.size());
v1Down.setSize (static_cast<int> (numChannels), coefficientsDown.size());
delayDown.resize (static_cast<int> (numChannels));
} }
~Oversampling2TimesPolyphaseIIR() {} ~Oversampling2TimesPolyphaseIIR() {}
@@ -282,95 +318,113 @@ public:
{ {
OversamplingEngine<SampleType>::reset(); OversamplingEngine<SampleType>::reset();
v1Up.fill (0);
v1Down.fill (0);
delayDown = 0;
v1Up.clear();
v1Down.clear();
delayDown.fill (0);
} }
void processSamplesUp (SampleType *samples, size_t numSamples) 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()));
// Initialization // Initialization
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (0);
auto coeffs = coefficientsUp.getRawDataPointer(); auto coeffs = coefficientsUp.getRawDataPointer();
auto lv1 = v1Up.getRawDataPointer();
auto numStages = coefficientsUp.size(); auto numStages = coefficientsUp.size();
auto delayedStages = numStages / 2; auto delayedStages = numStages / 2;
auto directStages = numStages - delayedStages; auto directStages = numStages - delayedStages;
auto numSamples = inputBlock.getNumSamples();
// Processing // Processing
for (size_t i = 0; i < numSamples; i++)
for (size_t channel = 0; channel < inputBlock.getNumChannels(); channel++)
{ {
// Direct path cascaded allpass filters
auto input = samples[i];
for (auto n = 0; n < directStages; n++)
{
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
}
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (static_cast<int> (channel));
auto lv1 = v1Up.getWritePointer (static_cast<int> (channel));
auto samples = inputBlock.getChannelPointer (channel);
// Output
bufferSamples[i << 1] = input;
// Delayed path cascaded allpass filters
input = samples[i];
for (auto n = directStages; n < numStages; n++)
for (size_t i = 0; i < numSamples; i++)
{ {
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
// Direct path cascaded allpass filters
auto input = samples[i];
for (auto n = 0; n < directStages; n++)
{
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
}
// Output
bufferSamples[i << 1] = input;
// Delayed path cascaded allpass filters
input = samples[i];
for (auto n = directStages; n < numStages; n++)
{
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
}
// Output
bufferSamples[(i << 1) + 1] = input;
} }
// Output
bufferSamples[(i << 1) + 1] = input;
} }
// Snap To Zero // Snap To Zero
snapToZero (true); snapToZero (true);
} }
void processSamplesDown (SampleType *samples, size_t numSamples) 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()));
// Initialization // Initialization
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (0);
auto coeffs = coefficientsDown.getRawDataPointer(); auto coeffs = coefficientsDown.getRawDataPointer();
auto lv1 = v1Down.getRawDataPointer();
auto numStages = coefficientsDown.size(); auto numStages = coefficientsDown.size();
auto delayedStages = numStages / 2; auto delayedStages = numStages / 2;
auto directStages = numStages - delayedStages; auto directStages = numStages - delayedStages;
auto numSamples = outputBlock.getNumSamples();
// Processing // Processing
for (size_t i = 0; i < numSamples; i++)
for (size_t channel = 0; channel < outputBlock.getNumChannels(); channel++)
{ {
// Direct path cascaded allpass filters
auto input = bufferSamples[i << 1];
for (auto n = 0; n < directStages; n++)
{
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
}
auto directOut = input;
auto bufferSamples = OversamplingEngine<SampleType>::buffer.getWritePointer (static_cast<int> (channel));
auto lv1 = v1Down.getWritePointer (static_cast<int> (channel));
auto samples = outputBlock.getChannelPointer (channel);
auto delay = delayDown.getUnchecked (static_cast<int> (channel));
// Delayed path cascaded allpass filters
input = bufferSamples[(i << 1) + 1];
for (auto n = directStages; n < numStages; n++)
for (size_t i = 0; i < numSamples; i++)
{ {
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
// Direct path cascaded allpass filters
auto input = bufferSamples[i << 1];
for (auto n = 0; n < directStages; n++)
{
auto alpha = coeffs[n];
auto output = alpha * input + lv1[n];
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];
auto output = alpha * input + lv1[n];
lv1[n] = input - alpha * output;
input = output;
}
// Output
samples[i] = (delay + directOut) * static_cast<SampleType> (0.5);
delay = input;
} }
// Output
samples[i] = (delayDown + directOut) * static_cast<SampleType> (0.5);
delayDown = input;
delayDown.setUnchecked (static_cast<int> (channel), delay);
} }
// Snap To Zero // Snap To Zero
@@ -381,19 +435,25 @@ public:
{ {
if (snapUpProcessing) if (snapUpProcessing)
{ {
auto lv1 = v1Up.getRawDataPointer();
auto numStages = coefficientsUp.size();
for (auto channel = 0; channel < OversamplingEngine<SampleType>::buffer.getNumChannels(); channel++)
{
auto lv1 = v1Up.getWritePointer (channel);
auto numStages = coefficientsUp.size();
for (auto n = 0; n < numStages; n++)
JUCE_SNAP_TO_ZERO (lv1[n]);
for (auto n = 0; n < numStages; n++)
JUCE_SNAP_TO_ZERO (lv1[n]);
}
} }
else else
{ {
auto lv1 = v1Down.getRawDataPointer();
auto numStages = coefficientsDown.size();
for (auto channel = 0; channel < OversamplingEngine<SampleType>::buffer.getNumChannels(); channel++)
{
auto lv1 = v1Down.getWritePointer (channel);
auto numStages = coefficientsDown.size();
for (auto n = 0; n < numStages; n++)
JUCE_SNAP_TO_ZERO (lv1[n]);
for (auto n = 0; n < numStages; n++)
JUCE_SNAP_TO_ZERO (lv1[n]);
}
} }
} }
@@ -478,8 +538,8 @@ private:
Array<SampleType> coefficientsUp, coefficientsDown; Array<SampleType> coefficientsUp, coefficientsDown;
SampleType latency; SampleType latency;
Array<SampleType> v1Up, v1Down;
SampleType delayDown;
AudioBuffer<SampleType> v1Up, v1Down;
Array<SampleType> delayDown;
//=============================================================================== //===============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesPolyphaseIIR) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling2TimesPolyphaseIIR)
@@ -492,43 +552,53 @@ Oversampling<SampleType>::Oversampling (size_t newNumChannels, size_t newFactor,
{ {
jassert (newFactor >= 0 && newFactor <= 4 && newNumChannels > 0); jassert (newFactor >= 0 && newFactor <= 4 && newNumChannels > 0);
factorOversampling = (size_t) 1 << newFactor;
factorOversampling = static_cast<size_t> (1) << newFactor;
isMaximumQuality = newMaxQuality; isMaximumQuality = newMaxQuality;
type = newType; type = newType;
numChannels = newNumChannels; numChannels = newNumChannels;
if (newFactor == 0) if (newFactor == 0)
{ {
for (size_t channel = 0; channel < numChannels; channel++)
engines.add (new OversamplingDummy<SampleType>());
numStages = 1; numStages = 1;
engines.add (new OversamplingDummy<SampleType> (numChannels));
} }
else if (type == FilterType::filterHalfBandPolyphaseIIR) else if (type == FilterType::filterHalfBandPolyphaseIIR)
{ {
numStages = newFactor; numStages = newFactor;
for (size_t channel = 0; channel < numChannels; channel++)
for (size_t n = 0; n < numStages; n++)
{
auto tw1 = (isMaximumQuality ? 0.10f : 0.12f);
auto tw2 = (isMaximumQuality ? 0.12f : 0.15f);
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);
engines.add (new Oversampling2TimesPolyphaseIIR<SampleType> (tw1, -75.f + 10.f * n, tw2, -70.f + 10.f * n));
}
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);
engines.add (new Oversampling2TimesPolyphaseIIR<SampleType> (numChannels,
twUp, gaindBStartUp + gaindBFactorUp * n,
twDown, gaindBStartDown + gaindBFactorDown * n));
}
} }
else if (type == FilterType::filterHalfBandFIREquiripple) else if (type == FilterType::filterHalfBandFIREquiripple)
{ {
numStages = newFactor; numStages = newFactor;
for (size_t channel = 0; channel < numChannels; channel++)
for (size_t n = 0; n < numStages; n++)
{
auto tw1 = (isMaximumQuality ? 0.10f : 0.12f);
auto tw2 = (isMaximumQuality ? 0.12f : 0.15f);
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);
engines.add (new Oversampling2TimesEquirippleFIR<SampleType> (tw1, -90.f + 10.f * n, tw2, -70.f + 10.f * n));
}
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);
engines.add (new Oversampling2TimesEquirippleFIR<SampleType> (numChannels,
twUp, gaindBStartUp + gaindBFactorUp * n,
twDown, gaindBStartDown + gaindBFactorDown * n));
}
} }
} }
@@ -550,7 +620,7 @@ SampleType Oversampling<SampleType>::getLatencyInSamples() noexcept
auto& engine = *engines[static_cast<int> (n)]; auto& engine = *engines[static_cast<int> (n)];
order *= engine.getFactor(); order *= engine.getFactor();
latency += engine.getLatencyInSamples() / std::pow (static_cast<SampleType> (2), static_cast<SampleType> (order));
latency += engine.getLatencyInSamples() / static_cast<SampleType> (order);
} }
return latency; return latency;
@@ -568,18 +638,14 @@ void Oversampling<SampleType>::initProcessing (size_t maximumNumberOfSamplesBefo
{ {
jassert (engines.size() > 0); jassert (engines.size() > 0);
for (size_t channel = 0; channel < numChannels; channel++)
{
auto currentNumSamples = maximumNumberOfSamplesBeforeOversampling;
auto offset = numStages * channel;
auto currentNumSamples = maximumNumberOfSamplesBeforeOversampling;
for (size_t n = 0; n < numStages; n++)
{
auto& engine = *engines[static_cast<int> (n + offset)];
for (size_t n = 0; n < numStages; n++)
{
auto& engine = *engines[static_cast<int> (n)];
engine.initProcessing (currentNumSamples);
currentNumSamples *= engine.getFactor();
}
engine.initProcessing (currentNumSamples);
currentNumSamples *= engine.getFactor();
} }
isReady = true; isReady = true;
@@ -597,75 +663,49 @@ void Oversampling<SampleType>::reset() noexcept
} }
template <typename SampleType> template <typename SampleType>
typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::getProcessedSamples()
typename dsp::AudioBlock<SampleType> Oversampling<SampleType>::processSamplesUp (const dsp::AudioBlock<SampleType> &inputBlock) noexcept
{ {
jassert (engines.size() > 0); jassert (engines.size() > 0);
Array<SampleType*> arrayChannels;
for (size_t channel = 0; channel < numChannels; channel++)
arrayChannels.add (engines[static_cast<int> (((channel + 1) * numStages) - 1)]->getProcessedSamples());
auto numSamples = engines[static_cast<int> (numStages - 1)]->getNumProcessedSamples();
auto block = dsp::AudioBlock<SampleType> (arrayChannels.getRawDataPointer(), numChannels, numSamples);
return block;
}
template <typename SampleType>
void Oversampling<SampleType>::processSamplesUp (dsp::AudioBlock<SampleType> &block) noexcept
{
jassert (engines.size() > 0 && block.getNumChannels() <= numChannels);
if (! isReady) if (! isReady)
return;
for (size_t channel = 0; channel < jmin (numChannels, block.getNumChannels()); channel++)
{
SampleType* dataSamples = block.getChannelPointer (channel);
auto currentNumSamples = block.getNumSamples();
auto offset = numStages * channel;
return dsp::AudioBlock<SampleType>();
for (size_t n = 0; n < numStages; n++)
{
auto& engine = *engines[static_cast<int> (n + offset)];
engine.processSamplesUp (dataSamples, currentNumSamples);
dsp::AudioBlock<SampleType> audioBlock = inputBlock;
currentNumSamples *= engine.getFactor();
dataSamples = engine.getProcessedSamples();
}
for (size_t n = 0; n < numStages; n++)
{
auto& engine = *engines[static_cast<int> (n)];
engine.processSamplesUp (audioBlock);
audioBlock = engine.getProcessedSamples (audioBlock.getNumSamples() * engine.getFactor());
} }
return audioBlock;
} }
template <typename SampleType> template <typename SampleType>
void Oversampling<SampleType>::processSamplesDown (dsp::AudioBlock<SampleType> &block) noexcept
void Oversampling<SampleType>::processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) noexcept
{ {
jassert (engines.size() > 0 && block.getNumChannels() <= numChannels);
jassert (engines.size() > 0);
if (! isReady) if (! isReady)
return; return;
for (size_t channel = 0; channel < jmin (numChannels, block.getNumChannels()); channel++)
{
auto currentNumSamples = block.getNumSamples();
auto offset = numStages * channel;
for (size_t n = 0; n < numStages - 1; n++)
currentNumSamples *= engines[static_cast<int> (n + offset)]->getFactor();
auto currentNumSamples = outputBlock.getNumSamples();
for (size_t n = numStages - 1; n > 0; n--)
{
auto& engine = *engines[static_cast<int> (n + offset)];
for (size_t n = 0; n < numStages - 1; n++)
currentNumSamples *= engines[static_cast<int> (n)]->getFactor();
auto dataSamples = engines[static_cast<int> (n + offset - 1)]->getProcessedSamples();
engine.processSamplesDown (dataSamples, currentNumSamples);
for (size_t n = numStages - 1; n > 0; n--)
{
auto& engine = *engines[static_cast<int> (n)];
currentNumSamples /= engine.getFactor();
}
auto audioBlock = engines[static_cast<int> (n - 1)]->getProcessedSamples (currentNumSamples);
engine.processSamplesDown (audioBlock);
engines[static_cast<int> (offset)]->processSamplesDown (block.getChannelPointer (channel), currentNumSamples);
currentNumSamples /= engine.getFactor();
} }
engines[static_cast<int> (0)]->processSamplesDown (outputBlock);
} }
template class Oversampling<float>; template class Oversampling<float>;


+ 10
- 7
modules/juce_dsp/processors/juce_Oversampling.h View File

@@ -87,6 +87,9 @@ public:
in your main processor to compensate the additional latency involved with in your main processor to compensate the additional latency involved with
the oversampling, for example with a dry / wet functionality, and to report the oversampling, for example with a dry / wet functionality, and to report
the latency to the DAW. the latency to the DAW.
Note : the latency might not be integer, so you might need to round its value
or to compensate it properly in your processing code.
*/ */
SampleType getLatencyInSamples() noexcept; SampleType getLatencyInSamples() noexcept;
@@ -102,21 +105,21 @@ public:
/** Resets the processing pipeline, ready to oversample a new stream of data. */ /** Resets the processing pipeline, ready to oversample a new stream of data. */
void reset() noexcept; void reset() noexcept;
/** Must be called to perform the upsampling, prior to any oversampled processing. */
void processSamplesUp (dsp::AudioBlock<SampleType> &block) noexcept;
/** Must be called to perform the upsampling, prior to any oversampled processing.
/** Can be called to access to the oversampled input signal, to perform any non-
linear processing which needs the higher sample rate. Don't forget to set
the sample rate of that processing to N times the original sample rate.
Returns an AudioBlock referencing the oversampled input signal, which must be
used to perform the non-linear processing which needs the higher sample rate.
Don't forget to set the sample rate of that processing to N times the original
sample rate.
*/ */
dsp::AudioBlock<SampleType> getProcessedSamples();
dsp::AudioBlock<SampleType> processSamplesUp (const dsp::AudioBlock<SampleType> &inputBlock) noexcept;
/** Must be called to perform the downsampling, after the upsampling and the /** Must be called to perform the downsampling, after the upsampling and the
non-linear processing. The output signal is probably delayed by the internal non-linear processing. The output signal is probably delayed by the internal
latency of the whole oversampling behaviour, so don't forget to take this latency of the whole oversampling behaviour, so don't forget to take this
into account. into account.
*/ */
void processSamplesDown (dsp::AudioBlock<SampleType> &block) noexcept;
void processSamplesDown (dsp::AudioBlock<SampleType> &outputBlock) noexcept;
private: private:
//=============================================================================== //===============================================================================


Loading…
Cancel
Save