From 7a34790388b240cf6c7675880d47c41f00f71fcb Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 6 Sep 2017 10:04:12 +0100 Subject: [PATCH] DSP: Made trimming of the convolution impulse response optional --- .../Source/PluginProcessor.cpp | 4 +- .../DSPDemo/JuceLibraryCode/BinaryData.cpp | 6 +- examples/DSPDemo/JuceLibraryCode/BinaryData.h | 2 +- .../DSPDemo/Source/Demos/ConvolutionDemo.cpp | 4 +- .../juce_dsp/frequency/juce_Convolution.cpp | 121 +++++++++++------- modules/juce_dsp/frequency/juce_Convolution.h | 9 +- 6 files changed, 91 insertions(+), 55 deletions(-) diff --git a/examples/DSP module plugin demo/Source/PluginProcessor.cpp b/examples/DSP module plugin demo/Source/PluginProcessor.cpp index 17ea3d6788..bd531c000c 100644 --- a/examples/DSP module plugin demo/Source/PluginProcessor.cpp +++ b/examples/DSP module plugin demo/Source/PluginProcessor.cpp @@ -240,9 +240,9 @@ void DspModulePluginDemoAudioProcessor::updateParameters() auto maxSize = static_cast (roundDoubleToInt (8192 * getSampleRate() / 44100)); if (type == 0) - convolution.loadImpulseResponse (BinaryData::Impulse1_wav, BinaryData::Impulse1_wavSize, false, maxSize); + convolution.loadImpulseResponse (BinaryData::Impulse1_wav, BinaryData::Impulse1_wavSize, false, true, maxSize); else - convolution.loadImpulseResponse (BinaryData::Impulse2_wav, BinaryData::Impulse2_wavSize, false, maxSize); + convolution.loadImpulseResponse (BinaryData::Impulse2_wav, BinaryData::Impulse2_wavSize, false, true, maxSize); } cabinetIsBypassed = ! cabinetSimParam->get(); diff --git a/examples/DSPDemo/JuceLibraryCode/BinaryData.cpp b/examples/DSPDemo/JuceLibraryCode/BinaryData.cpp index a8727331fc..83708c82d6 100644 --- a/examples/DSPDemo/JuceLibraryCode/BinaryData.cpp +++ b/examples/DSPDemo/JuceLibraryCode/BinaryData.cpp @@ -1565,11 +1565,11 @@ static const unsigned char temp_binary_data_3[] = " if (cabinetTypeParameter->getCurrentSelectedID() == 2)\r\n" " convolution.loadImpulseResponse (BinaryData::guitar_amp_wav,\r\n" " BinaryData::guitar_amp_wavSize,\r\n" -" false, maxSize);\r\n" +" false, true, maxSize);\r\n" " else\r\n" " convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav,\r\n" " BinaryData::cassette_recorder_wavSize,\r\n" -" false, maxSize);\r\n" +" false, true, maxSize);\r\n" " }\r\n" " }\r\n" " }\r\n" @@ -2325,7 +2325,7 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw case 0x409ff6ec: numBytes = 37902; return cassette_recorder_wav; case 0x69523d16: numBytes = 628; return EditorColourScheme_xml; case 0x700ccf3c: numBytes = 90246; return guitar_amp_wav; - case 0x5922ccdf: numBytes = 2999; return ConvolutionDemo_cpp; + case 0x5922ccdf: numBytes = 3011; return ConvolutionDemo_cpp; case 0x14aa0aae: numBytes = 2674; return FIRFilterDemo_cpp; case 0xab621a06: numBytes = 1809; return GainDemo_cpp; case 0x06a7a4b1: numBytes = 2819; return IIRFilterDemo_cpp; diff --git a/examples/DSPDemo/JuceLibraryCode/BinaryData.h b/examples/DSPDemo/JuceLibraryCode/BinaryData.h index 51444ccfed..fe5ad62cfd 100644 --- a/examples/DSPDemo/JuceLibraryCode/BinaryData.h +++ b/examples/DSPDemo/JuceLibraryCode/BinaryData.h @@ -18,7 +18,7 @@ namespace BinaryData const int guitar_amp_wavSize = 90246; extern const char* ConvolutionDemo_cpp; - const int ConvolutionDemo_cppSize = 2999; + const int ConvolutionDemo_cppSize = 3011; extern const char* FIRFilterDemo_cpp; const int FIRFilterDemo_cppSize = 2674; diff --git a/examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp b/examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp index 148b55194f..36aa11b4ad 100644 --- a/examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp +++ b/examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp @@ -65,11 +65,11 @@ struct ConvolutionDemo if (cabinetTypeParameter->getCurrentSelectedID() == 2) convolution.loadImpulseResponse (BinaryData::guitar_amp_wav, BinaryData::guitar_amp_wavSize, - false, maxSize); + false, true, maxSize); else convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav, BinaryData::cassette_recorder_wavSize, - false, maxSize); + false, true, maxSize); } } } diff --git a/modules/juce_dsp/frequency/juce_Convolution.cpp b/modules/juce_dsp/frequency/juce_Convolution.cpp index ad07352a24..8dd3f21b2e 100644 --- a/modules/juce_dsp/frequency/juce_Convolution.cpp +++ b/modules/juce_dsp/frequency/juce_Convolution.cpp @@ -54,6 +54,7 @@ struct ConvolutionEngine double sampleRate = 0; bool wantsStereo; + bool wantsTrimming; size_t impulseResponseSize; size_t maximumBufferSize = 0; }; @@ -335,6 +336,7 @@ public: changeSource, changeImpulseResponseSize, changeStereo, + changeTrimming, numChangeRequestTypes }; @@ -588,6 +590,17 @@ public: } break; + case ChangeRequest::changeTrimming: + { + bool newWantsTrimming = requestParameters[n]; + + if (currentInfo.wantsTrimming != newWantsTrimming) + changeLevel = jmax(1, changeLevel); + + currentInfo.wantsTrimming = newWantsTrimming; + } + break; + default: jassertfalse; break; @@ -732,6 +745,7 @@ private: void processImpulseResponse() { + if (currentInfo.sourceType == SourceType::sourceBinaryData) { copyAudioStreamInAudioBuffer (new MemoryInputStream (currentInfo.sourceData, currentInfo.sourceDataSize, false)); @@ -743,7 +757,7 @@ private: else if (currentInfo.sourceType == SourceType::sourceAudioBuffer) { copyBufferFromTemporaryLocation(); - trimAndResampleImpulseResponse (temporaryBuffer.getNumChannels(), currentInfo.bufferSampleRate); + trimAndResampleImpulseResponse (temporaryBuffer.getNumChannels(), currentInfo.bufferSampleRate, currentInfo.wantsTrimming); } if (isThreadRunning() && threadShouldExit()) @@ -751,19 +765,19 @@ private: if (currentInfo.wantsStereo) { - normalizeImpulseResponse (currentInfo.buffer->getWritePointer(0), currentInfo.buffer->getNumSamples()); - normalizeImpulseResponse (currentInfo.buffer->getWritePointer(1), currentInfo.buffer->getNumSamples()); + normalizeImpulseResponse (currentInfo.buffer->getWritePointer(0), currentInfo.buffer->getNumSamples(), 1.0); + normalizeImpulseResponse (currentInfo.buffer->getWritePointer(1), currentInfo.buffer->getNumSamples(), 1.0); } else { - normalizeImpulseResponse (currentInfo.buffer->getWritePointer (0), currentInfo.buffer->getNumSamples()); + normalizeImpulseResponse (currentInfo.buffer->getWritePointer (0), currentInfo.buffer->getNumSamples(), 1.0); } } /** Converts the data from an audio file into a stereo audio buffer of floats, and performs resampling if necessary. */ - void copyAudioStreamInAudioBuffer (InputStream* stream) + double copyAudioStreamInAudioBuffer (InputStream* stream) { AudioFormatManager manager; manager.registerBasicFormats(); @@ -778,54 +792,65 @@ private: impulseResponseOriginal.clear(); formatReader->read (&(impulseResponseOriginal), 0, impulseResponseOriginal.getNumSamples(), 0, true, numChannels > 1); - trimAndResampleImpulseResponse (numChannels, formatReader->sampleRate); + return trimAndResampleImpulseResponse (numChannels, formatReader->sampleRate, currentInfo.wantsTrimming); } + else + return 0.0; } - void trimAndResampleImpulseResponse (int numChannels, double bufferSampleRate) + double trimAndResampleImpulseResponse (int numChannels, double bufferSampleRate, bool mustTrim) { auto thresholdTrim = Decibels::decibelsToGain (-80.0f); - auto indexStart = impulseResponseOriginal.getNumSamples() - 1; - auto indexEnd = 0; + auto indexStart = 0; + auto indexEnd = impulseResponseOriginal.getNumSamples() - 1; - for (auto channel = 0; channel < numChannels; ++channel) + if (mustTrim) { - auto localIndexStart = 0; - auto localIndexEnd = impulseResponseOriginal.getNumSamples() - 1; + indexStart = impulseResponseOriginal.getNumSamples() - 1; + indexEnd = 0; + + for (auto channel = 0; channel < numChannels; ++channel) + { + auto localIndexStart = 0; + auto localIndexEnd = impulseResponseOriginal.getNumSamples() - 1; - auto* channelData = impulseResponseOriginal.getReadPointer (channel); + auto* channelData = impulseResponseOriginal.getReadPointer (channel); - while (localIndexStart < impulseResponseOriginal.getNumSamples() - 1 - && channelData[localIndexStart] <= thresholdTrim - && channelData[localIndexStart] >= -thresholdTrim) - ++localIndexStart; + while (localIndexStart < impulseResponseOriginal.getNumSamples() - 1 + && channelData[localIndexStart] <= thresholdTrim + && channelData[localIndexStart] >= -thresholdTrim) + ++localIndexStart; - while (localIndexEnd >= 0 - && channelData[localIndexEnd] <= thresholdTrim - && channelData[localIndexEnd] >= -thresholdTrim) - --localIndexEnd; + while (localIndexEnd >= 0 + && channelData[localIndexEnd] <= thresholdTrim + && channelData[localIndexEnd] >= -thresholdTrim) + --localIndexEnd; - indexStart = jmin (indexStart, localIndexStart); - indexEnd = jmax (indexEnd, localIndexEnd); - } + indexStart = jmin (indexStart, localIndexStart); + indexEnd = jmax (indexEnd, localIndexEnd); + } - if (indexStart > 0) - { - for (auto channel = 0; channel < numChannels; ++channel) + if (indexStart > 0) { - auto* channelData = impulseResponseOriginal.getWritePointer (channel); + for (auto channel = 0; channel < numChannels; ++channel) + { + auto* channelData = impulseResponseOriginal.getWritePointer (channel); - for (auto i = 0; i < indexEnd - indexStart + 1; ++i) - channelData[i] = channelData[i + indexStart]; + for (auto i = 0; i < indexEnd - indexStart + 1; ++i) + channelData[i] = channelData[i + indexStart]; - for (auto i = indexEnd - indexStart + 1; i < impulseResponseOriginal.getNumSamples() - 1; ++i) - channelData[i] = 0.0f; + for (auto i = indexEnd - indexStart + 1; i < impulseResponseOriginal.getNumSamples() - 1; ++i) + channelData[i] = 0.0f; + } } } + double factorReading; + if (currentInfo.sampleRate == bufferSampleRate) { // No resampling + factorReading = 1.0; auto impulseSize = jmin (static_cast (currentInfo.impulseResponseSize), indexEnd - indexStart + 1); impulseResponse.setSize (2, impulseSize); @@ -837,7 +862,7 @@ private: else { // Resampling - auto factorReading = bufferSampleRate / currentInfo.sampleRate; + factorReading = bufferSampleRate / currentInfo.sampleRate; auto impulseSize = jmin (static_cast (currentInfo.impulseResponseSize), roundDoubleToInt ((indexEnd - indexStart + 1) / factorReading)); impulseResponse.setSize (2, impulseSize); @@ -860,16 +885,18 @@ private: // Filling the second channel with the first if necessary if (numChannels == 1) impulseResponse.copyFrom (1, 0, impulseResponse, 0, 0, impulseResponse.getNumSamples()); + + return factorReading; } - void normalizeImpulseResponse (float* samples, int numSamples) const + void normalizeImpulseResponse (float* samples, int numSamples, double factorResampling) const { auto magnitude = 0.0f; for (int i = 0; i < numSamples; ++i) magnitude += samples[i] * samples[i]; - auto magnitudeInv = 1.0f / (4.0f * std::sqrt (magnitude)); + auto magnitudeInv = 1.0f / (4.0f * std::sqrt (magnitude)) * 0.5f * static_cast (factorResampling); for (int i = 0; i < numSamples; ++i) samples[i] *= magnitudeInv; @@ -965,14 +992,15 @@ Convolution::~Convolution() { } -void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceDataSize, bool wantsStereo, size_t size) +void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceDataSize, bool wantsStereo, bool wantsTrimming, size_t size) { if (sourceData == nullptr) return; Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo }; + Pimpl::ChangeRequest::changeStereo, + Pimpl::ChangeRequest::changeTrimming }; Array sourceParameter; @@ -981,19 +1009,21 @@ void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceData juce::var parameters[] = { juce::var (sourceParameter), juce::var (static_cast (size)), - juce::var (wantsStereo) }; + juce::var (wantsStereo), + juce::var (wantsTrimming) }; pimpl->addToFifo (types, parameters, 3); } -void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wantsStereo, size_t size) +void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wantsStereo, bool wantsTrimming, size_t size) { if (! fileImpulseResponse.existsAsFile()) return; Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo }; + Pimpl::ChangeRequest::changeStereo, + Pimpl::ChangeRequest::changeTrimming }; Array sourceParameter; @@ -1002,13 +1032,14 @@ void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wan juce::var parameters[] = { juce::var (sourceParameter), juce::var (static_cast (size)), - juce::var (wantsStereo) }; + juce::var (wantsStereo), + juce::var (wantsTrimming) }; pimpl->addToFifo (types, parameters, 3); } void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer& buffer, - double bufferSampleRate, bool wantsStereo, size_t size) + double bufferSampleRate, bool wantsStereo, bool wantsTrimming, size_t size) { jassert (bufferSampleRate > 0); @@ -1019,7 +1050,8 @@ void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo }; + Pimpl::ChangeRequest::changeStereo, + Pimpl::ChangeRequest::changeTrimming }; Array sourceParameter; sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer)); @@ -1027,7 +1059,8 @@ void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer juce::var parameters[] = { juce::var (sourceParameter), juce::var (static_cast (size)), - juce::var (wantsStereo) }; + juce::var (wantsStereo), + juce::var (wantsTrimming) }; pimpl->addToFifo (types, parameters, 3); } diff --git a/modules/juce_dsp/frequency/juce_Convolution.h b/modules/juce_dsp/frequency/juce_Convolution.h index 5525e99b7c..e440a41ab5 100644 --- a/modules/juce_dsp/frequency/juce_Convolution.h +++ b/modules/juce_dsp/frequency/juce_Convolution.h @@ -87,10 +87,11 @@ public: @param sourceData the block of data to use as the stream's source @param sourceDataSize the number of bytes in the source data block @param wantsStereo requests to load both stereo channels or only one mono channel + @param wantsTrimming requests to trim the start and the end of the impulse response @param size the expected size for the impulse response after loading */ void loadImpulseResponse (const void* sourceData, size_t sourceDataSize, - bool wantsStereo, size_t size); + bool wantsStereo, bool wantsTrimming, size_t size); /** This function loads an impulse response from an audio file on any drive. It can load any of the audio formats registered in JUCE, and performs some @@ -98,10 +99,11 @@ public: @param fileImpulseResponse the location of the audio file @param wantsStereo requests to load both stereo channels or only one mono channel + @param wantsTrimming requests to trim the start and the end of the impulse response @param size the expected size for the impulse response after loading */ void loadImpulseResponse (const File& fileImpulseResponse, - bool wantsStereo, size_t size); + bool wantsStereo, bool wantsTrimming, size_t size); /** This function loads an impulse response from an audio buffer, which is copied before doing anything else. Performs some resampling and @@ -110,10 +112,11 @@ public: @param buffer the AudioBuffer to use @param bufferSampleRate the sampleRate of the data in the AudioBuffer @param wantsStereo requests to load both stereo channels or only one mono channel + @param wantsTrimming requests to trim the start and the end of the impulse response @param size the expected size for the impulse response after loading */ void copyAndLoadImpulseResponseFromBuffer (const AudioBuffer& buffer, double bufferSampleRate, - bool wantsStereo, size_t size); + bool wantsStereo, bool wantsTrimming, size_t size); private: //==============================================================================