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;
395 int start1, size1, start2, size2;
400 jassert (size1 + size2 > 0);
420 int start1, size1, start2, size2;
421 abstractFifo.
prepareToWrite (numEntries, start1, size1, start2, size2);
425 jassert (numEntries > 0 && size1 + size2 > 0);
429 for (
auto i = 0; i < size1; ++i)
432 fifoRequestsParameter.
setUnchecked (start1 + i, parameters[i]);
438 for (
auto i = 0; i < size2; ++i)
440 fifoRequestsType.
setUnchecked (start2 + i, types[i + size1]);
441 fifoRequestsParameter.
setUnchecked (start2 + i, parameters[i + size1]);
451 int start1, size1, start2, size2;
452 abstractFifo.
prepareToRead (1, start1, size1, start2, size2);
456 type = fifoRequestsType[start1];
457 parameter = fifoRequestsParameter[start1];
462 type = fifoRequestsType[start2];
463 parameter = fifoRequestsParameter[start2];
487 auto numRequests = 0;
492 ChangeRequest type = ChangeRequest::changeEngine;
498 requestsParameter.
setUnchecked (numRequests, parameter);
504 for (
auto i = 0; i < (int) ChangeRequest::numChangeRequestTypes; ++i)
508 for (
auto n = numRequests; --n >= 0;)
510 if (requestsType[n] == (ChangeRequest) i)
515 requestsType.
setUnchecked (n, ChangeRequest::changeIgnore);
522 for (
auto n = 0; n < numRequests; ++n)
524 switch (requestsType[n])
526 case ChangeRequest::changeEngine:
530 case ChangeRequest::changeSampleRate:
532 double newSampleRate = requestsParameter[n];
534 if (currentInfo.sampleRate != newSampleRate)
537 currentInfo.sampleRate = newSampleRate;
541 case ChangeRequest::changeMaximumBufferSize:
543 int newMaximumBufferSize = requestsParameter[n];
545 if (currentInfo.maximumBufferSize != (
size_t) newMaximumBufferSize)
548 currentInfo.maximumBufferSize = (size_t) newMaximumBufferSize;
552 case ChangeRequest::changeSource:
554 auto* arrayParameters = requestsParameter[n].getArray();
555 auto newSourceType =
static_cast<SourceType
> (
static_cast<int> (arrayParameters->getUnchecked (0)));
557 if (currentInfo.sourceType != newSourceType)
558 changeLevel = jmax (2, changeLevel);
560 if (newSourceType == SourceType::sourceBinaryData)
562 auto& prm = arrayParameters->getRawDataPointer()[1];
563 auto* newMemoryBlock = prm.getBinaryData();
565 auto* newPtr = newMemoryBlock->getData();
566 auto newSize = (int) newMemoryBlock->getSize();
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();
576 else if (newSourceType == SourceType::sourceAudioFile)
578 File newFile (arrayParameters->getUnchecked (1).toString());
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;
588 else if (newSourceType == SourceType::sourceAudioBuffer)
590 double originalSampleRate (arrayParameters->getUnchecked (1));
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:
615 bool newWantsStereo = requestsParameter[n];
617 if (currentInfo.wantsStereo != newWantsStereo)
618 changeLevel = jmax (0, changeLevel);
620 currentInfo.wantsStereo = newWantsStereo;
624 case ChangeRequest::changeTrimming:
626 bool newWantsTrimming = requestsParameter[n];
628 if (currentInfo.wantsTrimming != newWantsTrimming)
629 changeLevel = jmax (1, changeLevel);
631 currentInfo.wantsTrimming = newWantsTrimming;
635 case ChangeRequest::changeNormalisation:
637 bool newWantsNormalisation = requestsParameter[n];
639 if (currentInfo.wantsNormalisation != newWantsNormalisation)
640 changeLevel = jmax (1, changeLevel);
642 currentInfo.wantsNormalisation = newWantsNormalisation;
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));
728 if (mustInterpolate ==
false)
730 for (
size_t channel = 0; channel < numChannels; ++channel)
737 for (
size_t channel = 0; channel < numChannels; ++channel)
744 changeVolumes[channel].
applyGain (buffer.getChannelPointer (0), (
int) numSamples);
747 engines[(int) channel + 2]->
processSamples (interPtr, interPtr, numSamples);
748 changeVolumes[channel + 2].
applyGain (interPtr, (
int) numSamples);
750 buffer += interpolated;
753 if (input.
getNumChannels() > 1 && currentInfo.wantsStereo ==
false)
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)
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)