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;
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;
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)
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)
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)
1205 jassert (input.getNumChannels() == output.getNumChannels());
1206 jassert (isPositiveAndBelow (input.getNumChannels(),
static_cast<size_t> (3)));
1208 auto numChannels = jmin (input.getNumChannels(), (
size_t) 2);
1209 auto numSamples = jmin (input.getNumSamples(), output.getNumSamples());
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);
void reset() noexcept
Clears the buffer positions, so that it appears empty.
void prepareToWrite(int numToWrite, int &startIndex1, int &blockSize1, int &startIndex2, int &blockSize2) const noexcept
Returns the location within the buffer at which an incoming block of data should be written.
void prepareToRead(int numWanted, int &startIndex1, int &blockSize1, int &startIndex2, int &blockSize2) const noexcept
Returns the location within the buffer from which the next block of data should be read.
void finishedRead(int numRead) noexcept
Called after reading from the FIFO, to indicate that this many items have now been consumed.
void finishedWrite(int numWritten) noexcept
Called after writing from the FIFO, to indicate that this many items have been added.
int getNumReady() const noexcept
Returns the number of items that can currently be read from the buffer.
Holds a resizable array of primitive or copy-by-value objects.
void setUnchecked(int indexToChange, ParameterType newValue)
Replaces an element with a new value without doing any bounds-checking.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
void resize(int targetNumItems)
This will enlarge or shrink the array to the given number of elements, by adding or removing items fr...
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
void clear() noexcept
Clears all the samples in all channels.
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
Copies samples from another buffer to this one.
void setSample(int destChannel, int destSample, Type newValue) noexcept
Sets a sample in the buffer.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer's channels.
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
Converts a dBFS value to its equivalent gain level.
Represents a local file or directory.
bool existsAsFile() const
Checks whether the file exists and is a file rather than a directory.
const String & getFullPathName() const noexcept
Returns the complete, absolute path of this file.
static void JUCE_CALLTYPE fill(float *dest, float valueToFill, int numValues) noexcept
Copies a repeated value into a vector of floats.
static void JUCE_CALLTYPE copy(float *dest, const float *src, int numValues) noexcept
Copies a vector of floats.
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.
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.
static void JUCE_CALLTYPE add(float *dest, float amountToAdd, int numValues) noexcept
Adds a fixed value to the destination values.
Automatically locks and unlocks a mutex object.
void applyGain(FloatType *samples, int numSamples) noexcept
Applies a smoothed gain to a stream of samples S[i] *= gain.
void reset(double sampleRate, double rampLengthInSeconds) noexcept
Reset to a new sample rate and ramp length.
GenericScopedLock< SpinLock > ScopedLockType
Provides the type of scoped lock to use for locking a SpinLock.
void startThread()
Starts the thread running.
Thread(const String &threadName, size_t threadStackSize=0)
Creates a thread.
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
bool stopThread(int timeOutMilliseconds)
Attempts to stop the thread running.
bool isThreadRunning() const
Returns true if the thread is currently active.
AudioBlock getSubBlock(size_t newOffset, size_t newLength) const noexcept
Return a new AudioBlock pointing to a sub-block inside this block.
constexpr size_t getNumChannels() const noexcept
Returns the number of channels referenced by this block.
SampleType * getChannelPointer(size_t channel) const noexcept
Returns a raw pointer into one of the channels in this block.
AudioBlock & copyFrom(const AudioBlock< OtherSampleType > &src) noexcept
Copies the values in src to this block.
AudioBlock getSingleChannelBlock(size_t channel) const noexcept
Returns an AudioBlock that represents one of the channels in this block.
constexpr size_t getNumSamples() const noexcept
Returns the number of samples referenced by this block.
AudioBlock & clear() noexcept
Clears the memory referenced by this AudioBlock.
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...
~Convolution()
Destructor.
Convolution()
Initialises an object for performing convolution in the frequency domain.
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...
void reset() noexcept
Resets the processing pipeline, ready to start a new stream of 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...
Performs a fast fourier transform.
void reset(double sampleRate, double rampLengthInSeconds) noexcept
Reset to a new sample rate and ramp length.
void setTargetValue(FloatType newValue) noexcept
Set a new target value.
A variant class, that can be used to hold a range of primitive values.
uint32 numChannels
The number of channels that the process() method will be expected to handle.
double sampleRate
The sample rate that will be used for the data that is sent to the processor.
uint32 maximumBlockSize
The maximum number of samples that will be in the blocks sent to process() method.
This structure is passed into a DSP algorithm's prepare() method, and contains information about vari...
This class is the convolution engine itself, processing only one channel at a time of input signal.
void updateSymmetricFrequencyDomainData(float *samples) noexcept
Undo the re-organization of samples from the function prepareForConvolution.
void initializeConvolutionEngine(ProcessingInformation &info, int channel)
Initalize all the states and objects to perform the convolution.
void prepareForConvolution(float *samples) noexcept
After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functio...
void convolutionProcessingAndAccumulate(const float *input, const float *impulse, float *output)
Does the convolution operation itself only on half of the frequency domain samples.
void processSamples(const float *input, float *output, size_t numSamples)
Performs the uniform partitioned convolution using FFT.
void copyStateFromOtherEngine(const ConvolutionEngine &other)
Copy the states of another engine.
Manages all the changes requested by the main convolution engine, to minimize the number of calls of ...
void initProcessing(int maximumBufferSize)
Inits the size of the interpolation buffer.
void readFromFifo(ChangeRequest &type, juce::var ¶meter)
Reads requests from the fifo.
void addToFifo(ChangeRequest type, juce::var parameter)
Adds a new change request.
void addToFifo(ChangeRequest *types, juce::var *parameters, int numEntries)
Adds a new array of change requests.
void reset()
Resets the convolution engines states.
int getNumRemainingEntries() const noexcept
Returns the number of requests that still need to be processed.
void copyBufferToTemporaryLocation(dsp::AudioBlock< float > block)
This function copies a buffer to a temporary location, so that any external audio source can be proce...
void processFifo()
This function processes all the change requests to remove all the the redundant ones,...
void processSamples(const AudioBlock< const float > &input, AudioBlock< float > &output)
Convolution processing handling interpolation between previous and new states of the convolution engi...