50 SourceType sourceType = SourceType::sourceNone;
52 const void* sourceData;
54 File fileImpulseResponse;
56 double originalSampleRate;
58 int originalNumChannels = 1;
62 bool wantsStereo =
true;
63 bool wantsTrimming =
true;
64 bool wantsNormalisation =
true;
68 double sampleRate = 0;
69 size_t maximumBufferSize = 0;
76 bufferOverlap.clear();
77 bufferTempOutput.clear();
79 for (
auto i = 0; i < buffersInputSegments.size(); ++i)
80 buffersInputSegments.getReference (i).clear();
89 blockSize = (size_t) nextPowerOfTwo ((
int) info.maximumBufferSize);
91 FFTSize = blockSize > 128 ? 2 * blockSize
94 numSegments = ((size_t) info.finalSize) / (FFTSize - blockSize) + 1u;
96 numInputSegments = (blockSize > 128 ? numSegments : 3 * numSegments);
98 FFTobject.reset (
new FFT (roundToInt (std::log2 (FFTSize))));
100 bufferInput.setSize (1, static_cast<int> (FFTSize));
101 bufferOutput.setSize (1, static_cast<int> (FFTSize * 2));
102 bufferTempOutput.setSize (1, static_cast<int> (FFTSize * 2));
103 bufferOverlap.setSize (1, static_cast<int> (FFTSize));
105 buffersInputSegments.clear();
106 buffersImpulseSegments.clear();
107 bufferOutput.clear();
109 for (
size_t i = 0; i < numInputSegments; ++i)
112 newInputSegment.
setSize (1, static_cast<int> (FFTSize * 2));
113 buffersInputSegments.add (newInputSegment);
116 for (
auto i = 0u; i < numSegments; ++i)
119 newImpulseSegment.
setSize (1, static_cast<int> (FFTSize * 2));
120 buffersImpulseSegments.add (newImpulseSegment);
123 std::unique_ptr<FFT> FFTTempObject (
new FFT (roundToInt (std::log2 (FFTSize))));
127 for (
size_t n = 0; n < numSegments; ++n)
129 buffersImpulseSegments.getReference (static_cast<int> (n)).clear();
131 auto* impulseResponse = buffersImpulseSegments.getReference (static_cast<int> (n)).getWritePointer (0);
134 impulseResponse[0] = 1.0f;
136 for (
size_t i = 0; i < FFTSize - blockSize; ++i)
137 if (i + n * (FFTSize - blockSize) < (
size_t) info.finalSize)
138 impulseResponse[i] = channelData[i + n * (FFTSize - blockSize)];
140 FFTTempObject->performRealOnlyForwardTransform (impulseResponse);
152 if (FFTSize != other.FFTSize)
154 FFTobject.reset (
new FFT (roundToInt (std::log2 (other.FFTSize))));
155 FFTSize = other.FFTSize;
158 currentSegment = other.currentSegment;
159 numInputSegments = other.numInputSegments;
160 numSegments = other.numSegments;
161 blockSize = other.blockSize;
162 inputDataPos = other.inputDataPos;
164 bufferInput = other.bufferInput;
165 bufferTempOutput = other.bufferTempOutput;
166 bufferOutput = other.bufferOutput;
168 buffersInputSegments = other.buffersInputSegments;
169 buffersImpulseSegments = other.buffersImpulseSegments;
170 bufferOverlap = other.bufferOverlap;
182 size_t numSamplesProcessed = 0;
184 auto indexStep = numInputSegments / numSegments;
186 auto* inputData = bufferInput.getWritePointer (0);
187 auto* outputTempData = bufferTempOutput.getWritePointer (0);
188 auto* outputData = bufferOutput.getWritePointer (0);
189 auto* overlapData = bufferOverlap.getWritePointer (0);
191 while (numSamplesProcessed < numSamples)
193 const bool inputDataWasEmpty = (inputDataPos == 0);
194 auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
199 auto* inputSegmentData = buffersInputSegments.getReference (static_cast<int> (currentSegment)).getWritePointer (0);
203 FFTobject->performRealOnlyForwardTransform (inputSegmentData);
207 if (inputDataWasEmpty)
211 auto index = currentSegment;
213 for (
size_t i = 1; i < numSegments; ++i)
217 if (index >= numInputSegments)
218 index -= numInputSegments;
221 buffersImpulseSegments.getReference (static_cast<int> (i)).getWritePointer (0),
229 buffersImpulseSegments.getReference (0).getWritePointer (0),
234 FFTobject->performRealOnlyInverseTransform (outputData);
237 for (
size_t i = 0; i < numSamplesToProcess; ++i)
238 output[i + numSamplesProcessed] = outputData[inputDataPos + i] + overlapData[inputDataPos + i];
241 inputDataPos += numSamplesToProcess;
243 if (inputDataPos == blockSize)
257 currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
260 numSamplesProcessed += numSamplesToProcess;
267 auto FFTSizeDiv2 = FFTSize / 2;
269 for (
size_t i = 0; i < FFTSizeDiv2; i++)
270 samples[i] = samples[2 * i];
272 samples[FFTSizeDiv2] = 0;
274 for (
size_t i = 1; i < FFTSizeDiv2; i++)
275 samples[i + FFTSizeDiv2] = -samples[2 * (FFTSize - i) + 1];
281 auto FFTSizeDiv2 = FFTSize / 2;
289 output[FFTSize] += input[FFTSize] * impulse[FFTSize];
298 auto FFTSizeDiv2 = FFTSize / 2;
300 for (
size_t i = 1; i < FFTSizeDiv2; i++)
302 samples[2 * (FFTSize - i)] = samples[i];
303 samples[2 * (FFTSize - i) + 1] = -samples[FFTSizeDiv2 + i];
308 for (
size_t i = 1; i < FFTSizeDiv2; i++)
310 samples[2 * i] = samples[2 * (FFTSize - i)];
311 samples[2 * i + 1] = -samples[2 * (FFTSize - i) + 1];
316 std::unique_ptr<FFT> FFTobject;
319 size_t currentSegment = 0, numInputSegments = 0, numSegments = 0, blockSize = 0, inputDataPos = 0;
324 bool isReady =
false;
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;
396 abstractFifo.prepareToWrite (1, start1, size1, start2, size2);
400 jassert (size1 + size2 > 0);
404 fifoRequestsType.setUnchecked (start1, type);
405 fifoRequestsParameter.setUnchecked (start1, parameter);
410 fifoRequestsType.setUnchecked (start2, type);
411 fifoRequestsParameter.setUnchecked (start2, parameter);
414 abstractFifo.finishedWrite (size1 + size2);
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)
431 fifoRequestsType.setUnchecked (start1 + i, types[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]);
445 abstractFifo.finishedWrite (size1 + size2);
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];
466 abstractFifo.finishedRead (size1 + size2);
472 return abstractFifo.getNumReady();
484 if (getNumRemainingEntries() == 0 || isThreadRunning() || mustInterpolate)
487 auto numRequests = 0;
490 while (getNumRemainingEntries() > 0 && numRequests < fifoSize)
492 ChangeRequest type = ChangeRequest::changeEngine;
495 readFromFifo (type, parameter);
497 requestsType.setUnchecked (numRequests, type);
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;
675 copyBufferToTemporaryLocation (newBuffer);
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)
703 temporaryBuffer.copyFrom (channel, 0, block.
getChannelPointer ((
size_t) channel), (int) currentInfo.originalSize);
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);
746 auto* interPtr = interpolationBuffer.getWritePointer (0);
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)
770 if (input.
getNumChannels() > 1 && currentInfo.wantsStereo ==
false)
775 const int64 maximumTimeInSamples = 10 * 96000;
784 if (changeLevel == 2)
786 loadImpulseResponse();
788 if (isThreadRunning() && threadShouldExit())
792 processImpulseResponse();
794 if (isThreadRunning() && threadShouldExit())
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);
826 if (isThreadRunning() && threadShouldExit())
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)
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)
918 auto* channelData = impulseResponseOriginal.getWritePointer (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();
951 resamplingSource.
prepareToPlay ((
int) currentInfo.finalSize, currentInfo.sampleRate);
955 info.
numSamples = (int) currentInfo.finalSize;
956 info.
buffer = &impulseResponse;
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)
994 mustInterpolate =
false;
998 for (
auto i = 0; i < 2; ++i)
1000 engines[i + 2]->initializeConvolutionEngine (currentInfo, i);
1001 engines[i + 2]->reset();
1003 if (isThreadRunning() && threadShouldExit())
1007 for (
auto i = 0; i < 2; ++i)
1009 changeVolumes[i].setTargetValue (1.0f);
1010 changeVolumes[i].reset (currentInfo.sampleRate, 0.05);
1011 changeVolumes[i].setTargetValue (0.0f);
1013 changeVolumes[i + 2].setTargetValue (0.0f);
1014 changeVolumes[i + 2].reset (currentInfo.sampleRate, 0.05);
1015 changeVolumes[i + 2].setTargetValue (1.0f);
1019 mustInterpolate =
true;
1025 static constexpr
int fifoSize = 1024;
1034 int changeLevel = 0;
1051 bool mustInterpolate =
false;
1054 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (
Pimpl)
1061 pimpl.reset (
new Pimpl());
1062 pimpl->addToFifo (Convolution::Pimpl::ChangeRequest::changeEngine,
juce::var (0));
1070 bool wantsStereo,
bool wantsTrimming,
size_t size,
1071 bool wantsNormalisation)
1073 if (sourceData ==
nullptr)
1076 auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
1077 auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
1079 Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
1080 Pimpl::ChangeRequest::changeImpulseResponseSize,
1081 Pimpl::ChangeRequest::changeStereo,
1082 Pimpl::ChangeRequest::changeTrimming,
1083 Pimpl::ChangeRequest::changeNormalisation };
1087 sourceParameter.
add (
juce::var ((
int) ConvolutionEngine::ProcessingInformation::SourceType::sourceBinaryData));
1088 sourceParameter.
add (
juce::var (sourceData, sourceDataSize));
1091 juce::var (static_cast<int64> (wantedSize)),
1096 pimpl->addToFifo (types, parameters, 5);
1100 bool wantsTrimming,
size_t size,
bool wantsNormalisation)
1105 auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
1106 auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
1108 Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
1109 Pimpl::ChangeRequest::changeImpulseResponseSize,
1110 Pimpl::ChangeRequest::changeStereo,
1111 Pimpl::ChangeRequest::changeTrimming,
1112 Pimpl::ChangeRequest::changeNormalisation };
1116 sourceParameter.
add (
juce::var ((
int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioFile));
1120 juce::var (static_cast<int64> (wantedSize)),
1125 pimpl->addToFifo (types, parameters, 5);
1129 double bufferSampleRate,
bool wantsStereo,
bool wantsTrimming,
bool wantsNormalisation,
size_t size)
1131 copyAndLoadImpulseResponseFromBlock (
AudioBlock<float> (buffer), bufferSampleRate,
1132 wantsStereo, wantsTrimming, wantsNormalisation, size);
1136 bool wantsStereo,
bool wantsTrimming,
bool wantsNormalisation,
size_t size)
1138 jassert (bufferSampleRate > 0);
1143 auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
1144 auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
1146 pimpl->copyBufferToTemporaryLocation (block);
1148 Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
1149 Pimpl::ChangeRequest::changeImpulseResponseSize,
1150 Pimpl::ChangeRequest::changeStereo,
1151 Pimpl::ChangeRequest::changeTrimming,
1152 Pimpl::ChangeRequest::changeNormalisation };
1155 sourceParameter.
add (
juce::var ((
int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer));
1159 juce::var (static_cast<int64> (wantedSize)),
1164 pimpl->addToFifo (types, parameters, 5);
1169 jassert (isPositiveAndBelow (spec.
numChannels, static_cast<uint32> (3)));
1171 Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSampleRate,
1172 Pimpl::ChangeRequest::changeMaximumBufferSize };
1177 pimpl->addToFifo (types, parameters, 2);
1180 for (
size_t channel = 0; channel < spec.
numChannels; ++channel)
1182 volumeDry[channel].reset (spec.
sampleRate, 0.05);
1183 volumeWet[channel].reset (spec.
sampleRate, 0.05);
1206 jassert (isPositiveAndBelow (input.
getNumChannels(),
static_cast<size_t> (3)));
1211 auto dry = dryBuffer.getSubsetChannelBlock (0, numChannels);
1213 if (volumeDry[0].isSmoothing())
1215 dry.copyFrom (input);
1217 for (
size_t channel = 0; channel < numChannels; ++channel)
1218 volumeDry[channel].applyGain (dry.getChannelPointer (channel), (int) numSamples);
1220 pimpl->processSamples (input, output);
1222 for (
size_t channel = 0; channel < numChannels; ++channel)
1223 volumeWet[channel].applyGain (output.
getChannelPointer (channel), (int) numSamples);
1229 if (! currentIsBypassed)
1230 pimpl->processSamples (input, output);
1232 if (isBypassed != currentIsBypassed)
1234 currentIsBypassed = isBypassed;
1236 for (
size_t channel = 0; channel < numChannels; ++channel)
1238 volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
1239 volumeDry[channel].reset (sampleRate, 0.05);
1240 volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1242 volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1243 volumeWet[channel].reset (sampleRate, 0.05);
1244 volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
AudioBlock & copyFrom(const AudioBlock< OtherSampleType > &src) noexcept
Copies the values in src to this block.
void initializeConvolutionEngine(ProcessingInformation &info, int channel)
Initalize all the states and objects to perform the convolution.
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Tells the source to prepare for playing.
int numSamples
The number of samples in the buffer which the callback is expected to fill with data.
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Called repeatedly to fetch subsequent blocks of audio data.
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
Converts a dBFS value to its equivalent gain level.
uint32 numChannels
The number of channels that the process() method will be expected to handle.
static void JUCE_CALLTYPE add(float *dest, float amountToAdd, int numValues) noexcept
Adds a fixed value to the destination values.
Manages all the changes requested by the main convolution engine, to minimize the number of calls of ...
void reset()
Resets the convolution engines states.
static void JUCE_CALLTYPE copy(float *dest, const float *src, int numValues) noexcept
Copies a vector of floats.
A variant class, that can be used to hold a range of primitive values.
This class is the convolution engine itself, processing only one channel at a time of input signal...
void add(const ElementType &newElement)
Appends a new element at the end of the array.
double sampleRate
The sample rate that will be used for the data that is sent to the processor.
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
AudioBlock getSingleChannelBlock(size_t channel) const noexcept
Returns an AudioBlock that represents one of the channels in this block.
static void JUCE_CALLTYPE fill(float *dest, float valueToFill, int numValues) noexcept
Copies a repeated value into a vector of floats.
void initProcessing(int maximumBufferSize)
Inits the size of the interpolation buffer.
void loadImpulseResponse(const void *sourceData, size_t sourceDataSize, bool wantsStereo, bool wantsTrimming, size_t size, bool wantsNormalisation=true)
This function loads an impulse response audio file from memory, added in a JUCE project with the Proj...
uint32 maximumBlockSize
The maximum number of samples that will be in the blocks sent to process() method.
A type of AudioSource that takes an input source and changes its sample rate.
void convolutionProcessingAndAccumulate(const float *input, const float *impulse, float *output)
Does the convolution operation itself only on half of the frequency domain samples.
void processFifo()
This function processes all the change requests to remove all the the redundant ones, and to tell what kind of initialization must be done.
void copyStateFromOtherEngine(const ConvolutionEngine &other)
Copy the states of another engine.
constexpr size_t getNumSamples() const noexcept
Returns the number of samples referenced by this block.
int getNumRemainingEntries() const noexcept
Returns the number of requests that still need to be processed.
A simple spin-lock class that can be used as a simple, low-overhead mutex for uncontended situations...
void processSamples(const float *input, float *output, size_t numSamples)
Performs the uniform partitioned convolution using FFT.
Encapsulates the logic required to implement a lock-free FIFO.
void processSamples(const AudioBlock< const float > &input, AudioBlock< float > &output)
Convolution processing handling interpolation between previous and new states of the convolution engi...
void reset() noexcept
Resets the processing pipeline, ready to start a new stream of data.
static void JUCE_CALLTYPE subtractWithMultiply(float *dest, const float *src, float multiplier, int numValues) noexcept
Multiplies each source value by the given multiplier, then subtracts it to the destination value...
An AudioSource which takes some float audio data as an input.
void updateSymmetricFrequencyDomainData(float *samples) noexcept
Undo the re-organization of samples from the function prepareForConvolution.
bool existsAsFile() const
Checks whether the file exists and is a file rather than a directory.
void readFromFifo(ChangeRequest &type, juce::var ¶meter)
Reads requests from the fifo.
Represents a local file or directory.
Holds a resizable array of primitive or copy-by-value objects.
int startSample
The first sample in the buffer from which the callback is expected to write data. ...
void copyAndLoadImpulseResponseFromBuffer(AudioBuffer< float > &buffer, double bufferSampleRate, bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size)
This function loads an impulse response from an audio buffer, which is copied before doing anything e...
void addToFifo(ChangeRequest type, juce::var parameter)
Adds a new change request.
AudioBuffer< float > * buffer
The destination buffer to fill with audio data.
Performs a fast fourier transform.
void setResamplingRatio(double samplesInPerOutputSample)
Changes the resampling ratio.
This structure is passed into a DSP algorithm's prepare() method, and contains information about vari...
static void JUCE_CALLTYPE addWithMultiply(float *dest, const float *src, float multiplier, int numValues) noexcept
Multiplies each source value by the given multiplier, then adds it to the destination value...
~Convolution()
Destructor.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
Convolution()
Initialises an object for performing convolution in the frequency domain.
void addToFifo(ChangeRequest *types, juce::var *parameters, int numEntries)
Adds a new array of change requests.
void setSample(int destChannel, int destSample, Type newValue) noexcept
Sets a sample in the buffer.
SampleType * getChannelPointer(size_t channel) const noexcept
Returns a raw pointer into one of the channels in this block.
void prepareForConvolution(float *samples) noexcept
After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functio...
An array designed for holding objects.
Used by AudioSource::getNextAudioBlock().
constexpr size_t getNumChannels() const noexcept
Returns the number of channels referenced by this block.
Automatically locks and unlocks a mutex object.
void copyBufferToTemporaryLocation(dsp::AudioBlock< float > block)
This function copies a buffer to a temporary location, so that any external audio source can be proce...
const String & getFullPathName() const noexcept
Returns the complete, absolute path of this file.
void clear() noexcept
Clears all the samples in all channels.
void prepare(const ProcessSpec &)
Must be called before loading any impulse response, to provide to the convolution the maximumBufferSi...
void copyAndLoadImpulseResponseFromBlock(AudioBlock< float > block, double bufferSampleRate, bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size)
This function loads an impulse response from an audio block, which is copied before doing anything el...