339 enum class ChangeRequest
343 changeMaximumBufferSize,
345 changeImpulseResponseSize,
350 numChangeRequestTypes
353 using SourceType = ConvolutionEngine::ProcessingInformation::SourceType;
356 Pimpl() :
Thread (
"Convolution"), abstractFifo (fifoSize)
358 abstractFifo.
reset();
359 fifoRequestsType.
resize (fifoSize);
360 fifoRequestsParameter.
resize (fifoSize);
362 requestsType.
resize (fifoSize);
363 requestsParameter.
resize (fifoSize);
365 for (
auto i = 0; i < 4; ++i)
368 currentInfo.maximumBufferSize = 0;
369 currentInfo.buffer = &impulseResponse;
371 temporaryBuffer.
setSize (2,
static_cast<int> (maximumTimeInSamples),
false,
false,
true);
372 impulseResponseOriginal.
setSize (2,
static_cast<int> (maximumTimeInSamples),
false,
false,
true);
373 impulseResponse.
setSize (2,
static_cast<int> (maximumTimeInSamples),
false,
false,
true);
387 interpolationBuffer.
setSize (1, maximumBufferSize,
false,
false,
true);
388 mustInterpolate =
false;
429 for (
auto i = 0; i <
size1; ++i)
438 for (
auto i = 0; i <
size2; ++i)
456 type = fifoRequestsType[
start1];
457 parameter = fifoRequestsParameter[
start1];
462 type = fifoRequestsType[
start2];
463 parameter = fifoRequestsParameter[
start2];
492 ChangeRequest type = ChangeRequest::changeEngine;
504 for (
auto i = 0; i < (
int) ChangeRequest::numChangeRequestTypes; ++i)
510 if (requestsType[n] == (ChangeRequest) i)
515 requestsType.
setUnchecked (n, ChangeRequest::changeIgnore);
524 switch (requestsType[n])
526 case ChangeRequest::changeEngine:
530 case ChangeRequest::changeSampleRate:
541 case ChangeRequest::changeMaximumBufferSize:
552 case ChangeRequest::changeSource:
558 changeLevel = jmax (2, changeLevel);
568 if (currentInfo.sourceData !=
newPtr || currentInfo.sourceDataSize !=
newSize)
569 changeLevel = jmax (2, changeLevel);
571 currentInfo.sourceType = SourceType::sourceBinaryData;
572 currentInfo.sourceData =
newPtr;
573 currentInfo.sourceDataSize =
newSize;
574 currentInfo.fileImpulseResponse =
File();
580 if (currentInfo.fileImpulseResponse !=
newFile)
581 changeLevel = jmax (2, changeLevel);
583 currentInfo.sourceType = SourceType::sourceAudioFile;
584 currentInfo.fileImpulseResponse =
newFile;
585 currentInfo.sourceData =
nullptr;
586 currentInfo.sourceDataSize = 0;
591 changeLevel = jmax (2, changeLevel);
593 currentInfo.sourceType = SourceType::sourceAudioBuffer;
594 currentInfo.originalSampleRate = originalSampleRate;
595 currentInfo.fileImpulseResponse =
File();
596 currentInfo.sourceData =
nullptr;
597 currentInfo.sourceDataSize = 0;
602 case ChangeRequest::changeImpulseResponseSize:
604 int64
newSize = requestsParameter[n];
606 if (currentInfo.wantedSize !=
newSize)
607 changeLevel = jmax (1, changeLevel);
609 currentInfo.wantedSize =
newSize;
613 case ChangeRequest::changeStereo:
618 changeLevel = jmax (0, changeLevel);
624 case ChangeRequest::changeTrimming:
629 changeLevel = jmax (1, changeLevel);
635 case ChangeRequest::changeNormalisation:
640 changeLevel = jmax (1, changeLevel);
646 case ChangeRequest::changeIgnore:
655 if (currentInfo.sourceType == SourceType::sourceNone)
657 currentInfo.sourceType = SourceType::sourceAudioBuffer;
659 if (currentInfo.sampleRate == 0)
660 currentInfo.sampleRate = 44100;
662 if (currentInfo.maximumBufferSize == 0)
663 currentInfo.maximumBufferSize = 128;
665 currentInfo.originalSampleRate = currentInfo.sampleRate;
666 currentInfo.wantedSize = 1;
667 currentInfo.fileImpulseResponse =
File();
668 currentInfo.sourceData =
nullptr;
669 currentInfo.sourceDataSize = 0;
679 if (changeLevel == 3)
681 loadImpulseResponse();
682 processImpulseResponse();
683 initializeConvolutionEngines();
685 else if (changeLevel > 0)
699 currentInfo.originalNumChannels = (block.
getNumChannels() > 1 ? 2 : 1);
700 currentInfo.originalSize = (
int) jmin ((
size_t) maximumTimeInSamples, block.
getNumSamples());
702 for (
auto channel = 0; channel < currentInfo.originalNumChannels; ++channel)
710 for (
auto*
e : engines)
713 mustInterpolate =
false;
725 size_t numChannels = jmin (input.getNumChannels(), (
size_t) (currentInfo.wantsStereo ? 2 : 1));
726 size_t numSamples = jmin (input.getNumSamples(), output.getNumSamples());
728 if (mustInterpolate ==
false)
730 for (
size_t channel = 0; channel < numChannels; ++channel)
731 engines[(
int) channel]->processSamples (input.getChannelPointer (channel), output.getChannelPointer (channel), numSamples);
737 for (
size_t channel = 0; channel < numChannels; ++channel)
739 auto&& buffer = output.getSingleChannelBlock (channel);
741 interpolationBuffer.
copyFrom (0, 0, input.getChannelPointer (channel), (
int) numSamples);
743 engines[(
int) channel]->
processSamples (input.getChannelPointer (channel), buffer.getChannelPointer (0), numSamples);
744 changeVolumes[channel].
applyGain (buffer.getChannelPointer (0), (
int) numSamples);
753 if (input.getNumChannels() > 1 && currentInfo.wantsStereo ==
false)
755 auto&& buffer = output.getSingleChannelBlock (1);
757 changeVolumes[1].
applyGain (buffer.getChannelPointer (0), (
int) numSamples);
758 changeVolumes[3].
applyGain (buffer.getChannelPointer (0), (
int) numSamples);
761 if (changeVolumes[0].isSmoothing() ==
false)
763 mustInterpolate =
false;
765 for (
auto channel = 0; channel < 2; ++channel)
766 engines[channel]->copyStateFromOtherEngine (*engines[channel + 2]);
770 if (input.getNumChannels() > 1 && currentInfo.wantsStereo ==
false)
771 output.getSingleChannelBlock (1).copyFrom (output.getSingleChannelBlock (0));
775 const int64 maximumTimeInSamples = 10 * 96000;
784 if (changeLevel == 2)
786 loadImpulseResponse();
792 processImpulseResponse();
797 initializeConvolutionEngines();
801 void loadImpulseResponse()
803 if (currentInfo.sourceType == SourceType::sourceBinaryData)
805 if (! (copyAudioStreamInAudioBuffer (
new MemoryInputStream (currentInfo.sourceData, (
size_t) currentInfo.sourceDataSize,
false))))
808 else if (currentInfo.sourceType == SourceType::sourceAudioFile)
810 if (! (copyAudioStreamInAudioBuffer (
new FileInputStream (currentInfo.fileImpulseResponse))))
813 else if (currentInfo.sourceType == SourceType::sourceAudioBuffer)
815 copyBufferFromTemporaryLocation();
822 void processImpulseResponse()
824 trimAndResampleImpulseResponse (currentInfo.originalNumChannels, currentInfo.originalSampleRate, currentInfo.wantsTrimming);
829 if (currentInfo.wantsNormalisation)
831 if (currentInfo.originalNumChannels > 1)
833 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (
int) currentInfo.finalSize, 1.0);
834 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (1), (
int) currentInfo.finalSize, 1.0);
838 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (
int) currentInfo.finalSize, 1.0);
842 if (currentInfo.originalNumChannels == 1)
843 currentInfo.buffer->copyFrom (1, 0, *currentInfo.buffer, 0, 0, (
int) currentInfo.finalSize);
849 bool copyAudioStreamInAudioBuffer (InputStream* stream)
851 AudioFormatManager manager;
852 manager.registerBasicFormats();
853 std::unique_ptr<AudioFormatReader> formatReader (manager.createReaderFor (stream));
855 if (formatReader !=
nullptr)
857 currentInfo.originalNumChannels = formatReader->numChannels > 1 ? 2 : 1;
858 currentInfo.originalSampleRate = formatReader->sampleRate;
859 currentInfo.originalSize =
static_cast<int> (jmin (maximumTimeInSamples, formatReader->lengthInSamples));
861 impulseResponseOriginal.
clear();
862 formatReader->read (&(impulseResponseOriginal), 0, (int) currentInfo.originalSize, 0,
true, currentInfo.originalNumChannels > 1);
873 void copyBufferFromTemporaryLocation()
877 for (
auto channel = 0; channel < currentInfo.originalNumChannels; ++channel)
878 impulseResponseOriginal.
copyFrom (channel, 0, temporaryBuffer, channel, 0, (
int) currentInfo.originalSize);
882 void trimAndResampleImpulseResponse (
int numChannels,
double srcSampleRate,
bool mustTrim)
886 auto indexEnd = currentInfo.originalSize - 1;
890 indexStart = currentInfo.originalSize - 1;
893 for (
auto channel = 0; channel < numChannels; ++channel)
895 auto localIndexStart = 0;
896 auto localIndexEnd = currentInfo.originalSize - 1;
898 auto* channelData = impulseResponseOriginal.
getReadPointer (channel);
900 while (localIndexStart < currentInfo.originalSize - 1
901 && channelData[localIndexStart] <= thresholdTrim
902 && channelData[localIndexStart] >= -thresholdTrim)
905 while (localIndexEnd >= 0
906 && channelData[localIndexEnd] <= thresholdTrim
907 && channelData[localIndexEnd] >= -thresholdTrim)
910 indexStart = jmin (indexStart, localIndexStart);
911 indexEnd = jmax (indexEnd, localIndexEnd);
916 for (
auto channel = 0; channel < numChannels; ++channel)
920 for (
auto i = 0; i < indexEnd - indexStart + 1; ++i)
921 channelData[i] = channelData[i + indexStart];
923 for (
auto i = indexEnd - indexStart + 1; i < currentInfo.originalSize - 1; ++i)
924 channelData[i] = 0.0f;
929 if (currentInfo.sampleRate == srcSampleRate)
932 currentInfo.finalSize = jmin (
static_cast<int> (currentInfo.wantedSize), indexEnd - indexStart + 1);
934 impulseResponse.
clear();
936 for (
auto channel = 0; channel < numChannels; ++channel)
937 impulseResponse.
copyFrom (channel, 0, impulseResponseOriginal, channel, 0, (
int) currentInfo.finalSize);
942 auto factorReading = srcSampleRate / currentInfo.sampleRate;
943 currentInfo.finalSize = jmin (
static_cast<int> (currentInfo.wantedSize), roundToInt ((indexEnd - indexStart + 1) / factorReading));
945 impulseResponse.
clear();
947 MemoryAudioSource memorySource (impulseResponseOriginal,
false);
948 ResamplingAudioSource resamplingSource (&memorySource,
false, (
int) numChannels);
950 resamplingSource.setResamplingRatio (factorReading);
951 resamplingSource.prepareToPlay ((
int) currentInfo.finalSize, currentInfo.sampleRate);
953 AudioSourceChannelInfo info;
954 info.startSample = 0;
955 info.numSamples = (int) currentInfo.finalSize;
956 info.buffer = &impulseResponse;
958 resamplingSource.getNextAudioBlock (info);
962 if (numChannels == 1)
963 impulseResponse.
copyFrom (1, 0, impulseResponse, 0, 0, (
int) currentInfo.finalSize);
967 void normaliseImpulseResponse (
float* samples,
int numSamples,
double factorResampling)
const
969 auto magnitude = 0.0f;
971 for (
auto i = 0; i < numSamples; ++i)
972 magnitude += samples[i] * samples[i];
974 auto magnitudeInv = 1.0f / (4.0f * std::sqrt (magnitude)) * 0.5f *
static_cast <float> (factorResampling);
976 for (
auto i = 0; i < numSamples; ++i)
977 samples[i] *= magnitudeInv;
984 void initializeConvolutionEngines()
986 if (currentInfo.maximumBufferSize == 0)
989 if (changeLevel == 3)
991 for (
auto i = 0; i < 2; ++i)
992 engines[i]->initializeConvolutionEngine (currentInfo, i);
994 mustInterpolate =
false;
998 for (
auto i = 0; i < 2; ++i)
1000 engines[i + 2]->initializeConvolutionEngine (currentInfo, i);
1001 engines[i + 2]->reset();
1007 for (
auto i = 0; i < 2; ++i)
1010 changeVolumes[i].
reset (currentInfo.sampleRate, 0.05);
1014 changeVolumes[i + 2].
reset (currentInfo.sampleRate, 0.05);
1019 mustInterpolate =
true;
1025 static constexpr int fifoSize = 1024;
1026 AbstractFifo abstractFifo;
1028 Array<ChangeRequest> fifoRequestsType;
1029 Array<juce::var> fifoRequestsParameter;
1031 Array<ChangeRequest> requestsType;
1032 Array<juce::var> requestsParameter;
1034 int changeLevel = 0;
1037 ConvolutionEngine::ProcessingInformation currentInfo;
1039 AudioBuffer<float> temporaryBuffer;
1040 SpinLock processLock;
1042 AudioBuffer<float> impulseResponseOriginal;
1043 AudioBuffer<float> impulseResponse;
1046 OwnedArray<ConvolutionEngine> engines;
1048 AudioBuffer<float> interpolationBuffer;
1049 LogRampedValue<float> changeVolumes[4];
1051 bool mustInterpolate =
false;
1054 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)