Browse Source

DSP: Made trimming of the convolution impulse response optional

tags/2021-05-28
hogliux 8 years ago
parent
commit
7a34790388
6 changed files with 91 additions and 55 deletions
  1. +2
    -2
      examples/DSP module plugin demo/Source/PluginProcessor.cpp
  2. +3
    -3
      examples/DSPDemo/JuceLibraryCode/BinaryData.cpp
  3. +1
    -1
      examples/DSPDemo/JuceLibraryCode/BinaryData.h
  4. +2
    -2
      examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp
  5. +77
    -44
      modules/juce_dsp/frequency/juce_Convolution.cpp
  6. +6
    -3
      modules/juce_dsp/frequency/juce_Convolution.h

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

@@ -240,9 +240,9 @@ void DspModulePluginDemoAudioProcessor::updateParameters()
auto maxSize = static_cast<size_t> (roundDoubleToInt (8192 * getSampleRate() / 44100)); auto maxSize = static_cast<size_t> (roundDoubleToInt (8192 * getSampleRate() / 44100));
if (type == 0) 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 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(); cabinetIsBypassed = ! cabinetSimParam->get();


+ 3
- 3
examples/DSPDemo/JuceLibraryCode/BinaryData.cpp View File

@@ -1565,11 +1565,11 @@ static const unsigned char temp_binary_data_3[] =
" if (cabinetTypeParameter->getCurrentSelectedID() == 2)\r\n" " if (cabinetTypeParameter->getCurrentSelectedID() == 2)\r\n"
" convolution.loadImpulseResponse (BinaryData::guitar_amp_wav,\r\n" " convolution.loadImpulseResponse (BinaryData::guitar_amp_wav,\r\n"
" BinaryData::guitar_amp_wavSize,\r\n" " BinaryData::guitar_amp_wavSize,\r\n"
" false, maxSize);\r\n"
" false, true, maxSize);\r\n"
" else\r\n" " else\r\n"
" convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav,\r\n" " convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav,\r\n"
" BinaryData::cassette_recorder_wavSize,\r\n" " BinaryData::cassette_recorder_wavSize,\r\n"
" false, maxSize);\r\n"
" false, true, maxSize);\r\n"
" }\r\n" " }\r\n"
" }\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 0x409ff6ec: numBytes = 37902; return cassette_recorder_wav;
case 0x69523d16: numBytes = 628; return EditorColourScheme_xml; case 0x69523d16: numBytes = 628; return EditorColourScheme_xml;
case 0x700ccf3c: numBytes = 90246; return guitar_amp_wav; 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 0x14aa0aae: numBytes = 2674; return FIRFilterDemo_cpp;
case 0xab621a06: numBytes = 1809; return GainDemo_cpp; case 0xab621a06: numBytes = 1809; return GainDemo_cpp;
case 0x06a7a4b1: numBytes = 2819; return IIRFilterDemo_cpp; case 0x06a7a4b1: numBytes = 2819; return IIRFilterDemo_cpp;


+ 1
- 1
examples/DSPDemo/JuceLibraryCode/BinaryData.h View File

@@ -18,7 +18,7 @@ namespace BinaryData
const int guitar_amp_wavSize = 90246; const int guitar_amp_wavSize = 90246;
extern const char* ConvolutionDemo_cpp; extern const char* ConvolutionDemo_cpp;
const int ConvolutionDemo_cppSize = 2999;
const int ConvolutionDemo_cppSize = 3011;
extern const char* FIRFilterDemo_cpp; extern const char* FIRFilterDemo_cpp;
const int FIRFilterDemo_cppSize = 2674; const int FIRFilterDemo_cppSize = 2674;


+ 2
- 2
examples/DSPDemo/Source/Demos/ConvolutionDemo.cpp View File

@@ -65,11 +65,11 @@ struct ConvolutionDemo
if (cabinetTypeParameter->getCurrentSelectedID() == 2) if (cabinetTypeParameter->getCurrentSelectedID() == 2)
convolution.loadImpulseResponse (BinaryData::guitar_amp_wav, convolution.loadImpulseResponse (BinaryData::guitar_amp_wav,
BinaryData::guitar_amp_wavSize, BinaryData::guitar_amp_wavSize,
false, maxSize);
false, true, maxSize);
else else
convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav, convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav,
BinaryData::cassette_recorder_wavSize, BinaryData::cassette_recorder_wavSize,
false, maxSize);
false, true, maxSize);
} }
} }
} }


+ 77
- 44
modules/juce_dsp/frequency/juce_Convolution.cpp View File

@@ -54,6 +54,7 @@ struct ConvolutionEngine
double sampleRate = 0; double sampleRate = 0;
bool wantsStereo; bool wantsStereo;
bool wantsTrimming;
size_t impulseResponseSize; size_t impulseResponseSize;
size_t maximumBufferSize = 0; size_t maximumBufferSize = 0;
}; };
@@ -335,6 +336,7 @@ public:
changeSource, changeSource,
changeImpulseResponseSize, changeImpulseResponseSize,
changeStereo, changeStereo,
changeTrimming,
numChangeRequestTypes numChangeRequestTypes
}; };
@@ -588,6 +590,17 @@ public:
} }
break; break;
case ChangeRequest::changeTrimming:
{
bool newWantsTrimming = requestParameters[n];
if (currentInfo.wantsTrimming != newWantsTrimming)
changeLevel = jmax(1, changeLevel);
currentInfo.wantsTrimming = newWantsTrimming;
}
break;
default: default:
jassertfalse; jassertfalse;
break; break;
@@ -732,6 +745,7 @@ private:
void processImpulseResponse() void processImpulseResponse()
{ {
if (currentInfo.sourceType == SourceType::sourceBinaryData) if (currentInfo.sourceType == SourceType::sourceBinaryData)
{ {
copyAudioStreamInAudioBuffer (new MemoryInputStream (currentInfo.sourceData, currentInfo.sourceDataSize, false)); copyAudioStreamInAudioBuffer (new MemoryInputStream (currentInfo.sourceData, currentInfo.sourceDataSize, false));
@@ -743,7 +757,7 @@ private:
else if (currentInfo.sourceType == SourceType::sourceAudioBuffer) else if (currentInfo.sourceType == SourceType::sourceAudioBuffer)
{ {
copyBufferFromTemporaryLocation(); copyBufferFromTemporaryLocation();
trimAndResampleImpulseResponse (temporaryBuffer.getNumChannels(), currentInfo.bufferSampleRate);
trimAndResampleImpulseResponse (temporaryBuffer.getNumChannels(), currentInfo.bufferSampleRate, currentInfo.wantsTrimming);
} }
if (isThreadRunning() && threadShouldExit()) if (isThreadRunning() && threadShouldExit())
@@ -751,19 +765,19 @@ private:
if (currentInfo.wantsStereo) 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 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 /** Converts the data from an audio file into a stereo audio buffer of floats, and
performs resampling if necessary. performs resampling if necessary.
*/ */
void copyAudioStreamInAudioBuffer (InputStream* stream)
double copyAudioStreamInAudioBuffer (InputStream* stream)
{ {
AudioFormatManager manager; AudioFormatManager manager;
manager.registerBasicFormats(); manager.registerBasicFormats();
@@ -778,54 +792,65 @@ private:
impulseResponseOriginal.clear(); impulseResponseOriginal.clear();
formatReader->read (&(impulseResponseOriginal), 0, impulseResponseOriginal.getNumSamples(), 0, true, numChannels > 1); 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 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) if (currentInfo.sampleRate == bufferSampleRate)
{ {
// No resampling // No resampling
factorReading = 1.0;
auto impulseSize = jmin (static_cast<int> (currentInfo.impulseResponseSize), indexEnd - indexStart + 1); auto impulseSize = jmin (static_cast<int> (currentInfo.impulseResponseSize), indexEnd - indexStart + 1);
impulseResponse.setSize (2, impulseSize); impulseResponse.setSize (2, impulseSize);
@@ -837,7 +862,7 @@ private:
else else
{ {
// Resampling // Resampling
auto factorReading = bufferSampleRate / currentInfo.sampleRate;
factorReading = bufferSampleRate / currentInfo.sampleRate;
auto impulseSize = jmin (static_cast<int> (currentInfo.impulseResponseSize), roundDoubleToInt ((indexEnd - indexStart + 1) / factorReading)); auto impulseSize = jmin (static_cast<int> (currentInfo.impulseResponseSize), roundDoubleToInt ((indexEnd - indexStart + 1) / factorReading));
impulseResponse.setSize (2, impulseSize); impulseResponse.setSize (2, impulseSize);
@@ -860,16 +885,18 @@ private:
// Filling the second channel with the first if necessary // Filling the second channel with the first if necessary
if (numChannels == 1) if (numChannels == 1)
impulseResponse.copyFrom (1, 0, impulseResponse, 0, 0, impulseResponse.getNumSamples()); 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; auto magnitude = 0.0f;
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
magnitude += samples[i] * samples[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 <float> (factorResampling);
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
samples[i] *= magnitudeInv; 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) if (sourceData == nullptr)
return; return;
Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
Pimpl::ChangeRequest::changeImpulseResponseSize, Pimpl::ChangeRequest::changeImpulseResponseSize,
Pimpl::ChangeRequest::changeStereo };
Pimpl::ChangeRequest::changeStereo,
Pimpl::ChangeRequest::changeTrimming };
Array<juce::var> sourceParameter; Array<juce::var> sourceParameter;
@@ -981,19 +1009,21 @@ void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceData
juce::var parameters[] = { juce::var (sourceParameter), juce::var parameters[] = { juce::var (sourceParameter),
juce::var (static_cast<int64> (size)), juce::var (static_cast<int64> (size)),
juce::var (wantsStereo) };
juce::var (wantsStereo),
juce::var (wantsTrimming) };
pimpl->addToFifo (types, parameters, 3); 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()) if (! fileImpulseResponse.existsAsFile())
return; return;
Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
Pimpl::ChangeRequest::changeImpulseResponseSize, Pimpl::ChangeRequest::changeImpulseResponseSize,
Pimpl::ChangeRequest::changeStereo };
Pimpl::ChangeRequest::changeStereo,
Pimpl::ChangeRequest::changeTrimming };
Array<juce::var> sourceParameter; Array<juce::var> sourceParameter;
@@ -1002,13 +1032,14 @@ void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wan
juce::var parameters[] = { juce::var (sourceParameter), juce::var parameters[] = { juce::var (sourceParameter),
juce::var (static_cast<int64> (size)), juce::var (static_cast<int64> (size)),
juce::var (wantsStereo) };
juce::var (wantsStereo),
juce::var (wantsTrimming) };
pimpl->addToFifo (types, parameters, 3); pimpl->addToFifo (types, parameters, 3);
} }
void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>& buffer, void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>& buffer,
double bufferSampleRate, bool wantsStereo, size_t size)
double bufferSampleRate, bool wantsStereo, bool wantsTrimming, size_t size)
{ {
jassert (bufferSampleRate > 0); jassert (bufferSampleRate > 0);
@@ -1019,7 +1050,8 @@ void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>
Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
Pimpl::ChangeRequest::changeImpulseResponseSize, Pimpl::ChangeRequest::changeImpulseResponseSize,
Pimpl::ChangeRequest::changeStereo };
Pimpl::ChangeRequest::changeStereo,
Pimpl::ChangeRequest::changeTrimming };
Array<juce::var> sourceParameter; Array<juce::var> sourceParameter;
sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer)); sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer));
@@ -1027,7 +1059,8 @@ void Convolution::copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>
juce::var parameters[] = { juce::var (sourceParameter), juce::var parameters[] = { juce::var (sourceParameter),
juce::var (static_cast<int64> (size)), juce::var (static_cast<int64> (size)),
juce::var (wantsStereo) };
juce::var (wantsStereo),
juce::var (wantsTrimming) };
pimpl->addToFifo (types, parameters, 3); pimpl->addToFifo (types, parameters, 3);
} }


+ 6
- 3
modules/juce_dsp/frequency/juce_Convolution.h View File

@@ -87,10 +87,11 @@ public:
@param sourceData the block of data to use as the stream's source @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 sourceDataSize the number of bytes in the source data block
@param wantsStereo requests to load both stereo channels or only one mono channel @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 @param size the expected size for the impulse response after loading
*/ */
void loadImpulseResponse (const void* sourceData, size_t sourceDataSize, 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 /** 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 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 fileImpulseResponse the location of the audio file
@param wantsStereo requests to load both stereo channels or only one mono channel @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 @param size the expected size for the impulse response after loading
*/ */
void loadImpulseResponse (const File& fileImpulseResponse, 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 /** This function loads an impulse response from an audio buffer, which is
copied before doing anything else. Performs some resampling and copied before doing anything else. Performs some resampling and
@@ -110,10 +112,11 @@ public:
@param buffer the AudioBuffer to use @param buffer the AudioBuffer to use
@param bufferSampleRate the sampleRate of the data in the AudioBuffer @param bufferSampleRate the sampleRate of the data in the AudioBuffer
@param wantsStereo requests to load both stereo channels or only one mono channel @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 @param size the expected size for the impulse response after loading
*/ */
void copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>& buffer, double bufferSampleRate, void copyAndLoadImpulseResponseFromBuffer (const AudioBuffer<float>& buffer, double bufferSampleRate,
bool wantsStereo, size_t size);
bool wantsStereo, bool wantsTrimming, size_t size);
private: private:
//============================================================================== //==============================================================================


Loading…
Cancel
Save