| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE 6 technical preview.
 -    Copyright (c) 2017 - ROLI Ltd.
 - 
 -    You may use this code under the terms of the GPL v3
 -    (see www.gnu.org/licenses).
 - 
 -    For this technical preview, this file is not subject to commercial licensing.
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - namespace dsp
 - {
 - 
 - /** This class is the convolution engine itself, processing only one channel at
 -     a time of input signal.
 - */
 - struct ConvolutionEngine
 - {
 -     ConvolutionEngine() = default;
 - 
 -     //==============================================================================
 -     struct ProcessingInformation
 -     {
 -         enum class SourceType
 -         {
 -             sourceBinaryData,
 -             sourceAudioFile,
 -             sourceAudioBuffer,
 -             sourceNone
 -         };
 - 
 -         SourceType sourceType = SourceType::sourceNone;
 - 
 -         const void* sourceData;
 -         int sourceDataSize;
 -         File fileImpulseResponse;
 - 
 -         double originalSampleRate;
 -         int originalSize = 0;
 -         int originalNumChannels = 1;
 - 
 -         AudioBuffer<float>* buffer;
 - 
 -         bool wantsStereo = true;
 -         bool wantsTrimming = true;
 -         bool wantsNormalisation = true;
 -         int64 wantedSize = 0;
 -         int finalSize = 0;
 - 
 -         double sampleRate = 0;
 -         size_t maximumBufferSize = 0;
 -     };
 - 
 -     //==============================================================================
 -     void reset()
 -     {
 -         bufferInput.clear();
 -         bufferOverlap.clear();
 -         bufferTempOutput.clear();
 - 
 -         for (auto i = 0; i < buffersInputSegments.size(); ++i)
 -             buffersInputSegments.getReference (i).clear();
 - 
 -         currentSegment = 0;
 -         inputDataPos = 0;
 -     }
 - 
 -     /** Initalize all the states and objects to perform the convolution. */
 -     void initializeConvolutionEngine (ProcessingInformation& info, int channel)
 -     {
 -         blockSize = (size_t) nextPowerOfTwo ((int) info.maximumBufferSize);
 - 
 -         FFTSize = blockSize > 128 ? 2 * blockSize
 -                                   : 4 * blockSize;
 - 
 -         numSegments = ((size_t) info.finalSize) / (FFTSize - blockSize) + 1u;
 - 
 -         numInputSegments = (blockSize > 128 ? numSegments : 3 * numSegments);
 - 
 -         FFTobject = std::make_unique<FFT> (roundToInt (std::log2 (FFTSize)));
 - 
 -         bufferInput.setSize      (1, static_cast<int> (FFTSize));
 -         bufferOutput.setSize     (1, static_cast<int> (FFTSize * 2));
 -         bufferTempOutput.setSize (1, static_cast<int> (FFTSize * 2));
 -         bufferOverlap.setSize    (1, static_cast<int> (FFTSize));
 - 
 -         buffersInputSegments.clear();
 -         buffersImpulseSegments.clear();
 -         bufferOutput.clear();
 - 
 -         for (size_t i = 0; i < numInputSegments; ++i)
 -         {
 -             AudioBuffer<float> newInputSegment;
 -             newInputSegment.setSize (1, static_cast<int> (FFTSize * 2));
 -             buffersInputSegments.add (newInputSegment);
 -         }
 - 
 -         for (auto i = 0u; i < numSegments; ++i)
 -         {
 -             AudioBuffer<float> newImpulseSegment;
 -             newImpulseSegment.setSize (1, static_cast<int> (FFTSize * 2));
 -             buffersImpulseSegments.add (newImpulseSegment);
 -         }
 - 
 -         std::unique_ptr<FFT> FFTTempObject (new FFT (roundToInt (std::log2 (FFTSize))));
 - 
 -         auto* channelData = info.buffer->getWritePointer (channel);
 - 
 -         for (size_t n = 0; n < numSegments; ++n)
 -         {
 -             buffersImpulseSegments.getReference (static_cast<int> (n)).clear();
 - 
 -             auto* impulseResponse = buffersImpulseSegments.getReference (static_cast<int> (n)).getWritePointer (0);
 - 
 -             if (n == 0)
 -                 impulseResponse[0] = 1.0f;
 - 
 -             for (size_t i = 0; i < FFTSize - blockSize; ++i)
 -                 if (i + n * (FFTSize - blockSize) < (size_t) info.finalSize)
 -                     impulseResponse[i] = channelData[i + n * (FFTSize - blockSize)];
 - 
 -             FFTTempObject->performRealOnlyForwardTransform (impulseResponse);
 -             prepareForConvolution (impulseResponse);
 -         }
 - 
 -         reset();
 - 
 -         isReady = true;
 -     }
 - 
 -     /** Copy the states of another engine. */
 -     void copyStateFromOtherEngine (const ConvolutionEngine& other)
 -     {
 -         if (FFTSize != other.FFTSize)
 -         {
 -             FFTobject.reset (new FFT (roundToInt (std::log2 (other.FFTSize))));
 -             FFTSize = other.FFTSize;
 -         }
 - 
 -         currentSegment      = other.currentSegment;
 -         numInputSegments    = other.numInputSegments;
 -         numSegments         = other.numSegments;
 -         blockSize           = other.blockSize;
 -         inputDataPos        = other.inputDataPos;
 - 
 -         bufferInput         = other.bufferInput;
 -         bufferTempOutput    = other.bufferTempOutput;
 -         bufferOutput        = other.bufferOutput;
 - 
 -         buffersInputSegments    = other.buffersInputSegments;
 -         buffersImpulseSegments  = other.buffersImpulseSegments;
 -         bufferOverlap           = other.bufferOverlap;
 - 
 -         isReady = true;
 -     }
 - 
 -     /** Performs the uniform partitioned convolution using FFT. */
 -     void processSamples (const float* input, float* output, size_t numSamples)
 -     {
 -         if (! isReady)
 -             return;
 - 
 -         // Overlap-add, zero latency convolution algorithm with uniform partitioning
 -         size_t numSamplesProcessed = 0;
 - 
 -         auto indexStep = numInputSegments / numSegments;
 - 
 -         auto* inputData      = bufferInput.getWritePointer (0);
 -         auto* outputTempData = bufferTempOutput.getWritePointer (0);
 -         auto* outputData     = bufferOutput.getWritePointer (0);
 -         auto* overlapData    = bufferOverlap.getWritePointer (0);
 - 
 -         while (numSamplesProcessed < numSamples)
 -         {
 -             const bool inputDataWasEmpty = (inputDataPos == 0);
 -             auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos);
 - 
 -             // copy the input samples
 -             FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast<int> (numSamplesToProcess));
 - 
 -             auto* inputSegmentData = buffersInputSegments.getReference (static_cast<int> (currentSegment)).getWritePointer (0);
 -             FloatVectorOperations::copy (inputSegmentData, inputData, static_cast<int> (FFTSize));
 - 
 -             // Forward FFT
 -             FFTobject->performRealOnlyForwardTransform (inputSegmentData);
 -             prepareForConvolution (inputSegmentData);
 - 
 -             // Complex multiplication
 -             if (inputDataWasEmpty)
 -             {
 -                 FloatVectorOperations::fill (outputTempData, 0, static_cast<int> (FFTSize + 1));
 - 
 -                 auto index = currentSegment;
 - 
 -                 for (size_t i = 1; i < numSegments; ++i)
 -                 {
 -                     index += indexStep;
 - 
 -                     if (index >= numInputSegments)
 -                         index -= numInputSegments;
 - 
 -                     convolutionProcessingAndAccumulate (buffersInputSegments.getReference (static_cast<int> (index)).getWritePointer (0),
 -                                                         buffersImpulseSegments.getReference (static_cast<int> (i)).getWritePointer (0),
 -                                                         outputTempData);
 -                 }
 -             }
 - 
 -             FloatVectorOperations::copy (outputData, outputTempData, static_cast<int> (FFTSize + 1));
 - 
 -             convolutionProcessingAndAccumulate (buffersInputSegments.getReference (static_cast<int> (currentSegment)).getWritePointer (0),
 -                                                 buffersImpulseSegments.getReference (0).getWritePointer (0),
 -                                                 outputData);
 - 
 -             // Inverse FFT
 -             updateSymmetricFrequencyDomainData (outputData);
 -             FFTobject->performRealOnlyInverseTransform (outputData);
 - 
 -             // Add overlap
 -             for (size_t i = 0; i < numSamplesToProcess; ++i)
 -                 output[i + numSamplesProcessed] = outputData[inputDataPos + i] + overlapData[inputDataPos + i];
 - 
 -             // Input buffer full => Next block
 -             inputDataPos += numSamplesToProcess;
 - 
 -             if (inputDataPos == blockSize)
 -             {
 -                 // Input buffer is empty again now
 -                 FloatVectorOperations::fill (inputData, 0.0f, static_cast<int> (FFTSize));
 - 
 -                 inputDataPos = 0;
 - 
 -                 // Extra step for segSize > blockSize
 -                 FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast<int> (FFTSize - 2 * blockSize));
 - 
 -                 // Save the overlap
 -                 FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast<int> (FFTSize - blockSize));
 - 
 -                 // Update current segment
 -                 currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
 -             }
 - 
 -             numSamplesProcessed += numSamplesToProcess;
 -         }
 -     }
 - 
 -     /** After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls. */
 -     void prepareForConvolution (float *samples) noexcept
 -     {
 -         auto FFTSizeDiv2 = FFTSize / 2;
 - 
 -         for (size_t i = 0; i < FFTSizeDiv2; i++)
 -             samples[i] = samples[2 * i];
 - 
 -         samples[FFTSizeDiv2] = 0;
 - 
 -         for (size_t i = 1; i < FFTSizeDiv2; i++)
 -             samples[i + FFTSizeDiv2] = -samples[2 * (FFTSize - i) + 1];
 -     }
 - 
 -     /** Does the convolution operation itself only on half of the frequency domain samples. */
 -     void convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output)
 -     {
 -         auto FFTSizeDiv2 = FFTSize / 2;
 - 
 -         FloatVectorOperations::addWithMultiply      (output, input, impulse, static_cast<int> (FFTSizeDiv2));
 -         FloatVectorOperations::subtractWithMultiply (output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
 - 
 -         FloatVectorOperations::addWithMultiply      (&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), static_cast<int> (FFTSizeDiv2));
 -         FloatVectorOperations::addWithMultiply      (&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, static_cast<int> (FFTSizeDiv2));
 - 
 -         output[FFTSize] += input[FFTSize] * impulse[FFTSize];
 -     }
 - 
 -     /** Undo the re-organization of samples from the function prepareForConvolution.
 -         Then, takes the conjugate of the frequency domain first half of samples, to fill the
 -         second half, so that the inverse transform will return real samples in the time domain.
 -     */
 -     void updateSymmetricFrequencyDomainData (float* samples) noexcept
 -     {
 -         auto FFTSizeDiv2 = FFTSize / 2;
 - 
 -         for (size_t i = 1; i < FFTSizeDiv2; i++)
 -         {
 -             samples[2 * (FFTSize - i)] = samples[i];
 -             samples[2 * (FFTSize - i) + 1] = -samples[FFTSizeDiv2 + i];
 -         }
 - 
 -         samples[1] = 0.f;
 - 
 -         for (size_t i = 1; i < FFTSizeDiv2; i++)
 -         {
 -             samples[2 * i] = samples[2 * (FFTSize - i)];
 -             samples[2 * i + 1] = -samples[2 * (FFTSize - i) + 1];
 -         }
 -     }
 - 
 -     //==============================================================================
 -     std::unique_ptr<FFT> FFTobject;
 - 
 -     size_t FFTSize = 0;
 -     size_t currentSegment = 0, numInputSegments = 0, numSegments = 0, blockSize = 0, inputDataPos = 0;
 - 
 -     AudioBuffer<float> bufferInput, bufferOutput, bufferTempOutput, bufferOverlap;
 -     Array<AudioBuffer<float>> buffersInputSegments, buffersImpulseSegments;
 - 
 -     bool isReady = false;
 - 
 -     //==============================================================================
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConvolutionEngine)
 - };
 - 
 - 
 - 
 - //==============================================================================
 - /** Manages all the changes requested by the main convolution engine, to minimize
 -     the number of calls of the convolution engine initialization, and the potential
 -     consequences of multiple quick calls to the function Convolution::loadImpulseResponse.
 - */
 - struct Convolution::Pimpl  : private Thread
 - {
 -     enum class ChangeRequest
 -     {
 -         changeEngine = 0,
 -         changeSampleRate,
 -         changeMaximumBufferSize,
 -         changeSource,
 -         changeImpulseResponseSize,
 -         changeStereo,
 -         changeTrimming,
 -         changeNormalisation,
 -         changeIgnore,
 -         numChangeRequestTypes
 -     };
 - 
 -     using SourceType = ConvolutionEngine::ProcessingInformation::SourceType;
 - 
 -     //==============================================================================
 -     Pimpl()  : Thread ("Convolution"), abstractFifo (fifoSize)
 -     {
 -         abstractFifo.reset();
 -         fifoRequestsType.resize (fifoSize);
 -         fifoRequestsParameter.resize (fifoSize);
 - 
 -         requestsType.resize (fifoSize);
 -         requestsParameter.resize (fifoSize);
 - 
 -         for (auto i = 0; i < 4; ++i)
 -             engines.add (new ConvolutionEngine());
 - 
 -         currentInfo.maximumBufferSize = 0;
 -         currentInfo.buffer = &impulseResponse;
 - 
 -         temporaryBuffer.setSize (2, static_cast<int> (maximumTimeInSamples), false, false, true);
 -         impulseResponseOriginal.setSize (2, static_cast<int> (maximumTimeInSamples), false, false, true);
 -         impulseResponse.setSize (2, static_cast<int> (maximumTimeInSamples), false, false, true);
 -     }
 - 
 -     ~Pimpl() override
 -     {
 -         stopThread (10000);
 -     }
 - 
 -     //==============================================================================
 -     /** Inits the size of the interpolation buffer. */
 -     void initProcessing (int maximumBufferSize)
 -     {
 -         stopThread (1000);
 - 
 -         interpolationBuffer.setSize (1, maximumBufferSize, false, false, true);
 -         mustInterpolate = false;
 -     }
 - 
 -     //==============================================================================
 -     /** Adds a new change request. */
 -     void addToFifo (ChangeRequest type, juce::var parameter)
 -     {
 -         int start1, size1, start2, size2;
 -         abstractFifo.prepareToWrite (1, start1, size1, start2, size2);
 - 
 -         // If you hit this assertion then you have requested more impulse response
 -         // changes than the Convolution class can handle.
 -         jassert (size1 + size2 > 0);
 - 
 -         if (size1 > 0)
 -         {
 -             fifoRequestsType.setUnchecked (start1, type);
 -             fifoRequestsParameter.setUnchecked (start1, parameter);
 -         }
 - 
 -         if (size2 > 0)
 -         {
 -             fifoRequestsType.setUnchecked (start2, type);
 -             fifoRequestsParameter.setUnchecked (start2, parameter);
 -         }
 - 
 -         abstractFifo.finishedWrite (size1 + size2);
 -     }
 - 
 -     /** Adds a new array of change requests. */
 -     void addToFifo (ChangeRequest* types, juce::var* parameters, int numEntries)
 -     {
 -         int start1, size1, start2, size2;
 -         abstractFifo.prepareToWrite (numEntries, start1, size1, start2, size2);
 - 
 -         // If you hit this assertion then you have requested more impulse response
 -         // changes than the Convolution class can handle.
 -         jassert (numEntries > 0 && size1 + size2 > 0);
 - 
 -         if (size1 > 0)
 -         {
 -             for (auto i = 0; i < size1; ++i)
 -             {
 -                 fifoRequestsType.setUnchecked (start1 + i, types[i]);
 -                 fifoRequestsParameter.setUnchecked (start1 + i, parameters[i]);
 -             }
 -         }
 - 
 -         if (size2 > 0)
 -         {
 -             for (auto i = 0; i < size2; ++i)
 -             {
 -                 fifoRequestsType.setUnchecked (start2 + i, types[i + size1]);
 -                 fifoRequestsParameter.setUnchecked (start2 + i, parameters[i + size1]);
 -             }
 -         }
 - 
 -         abstractFifo.finishedWrite (size1 + size2);
 -     }
 - 
 -     /** Reads requests from the fifo. */
 -     void readFromFifo (ChangeRequest& type, juce::var& parameter)
 -     {
 -         int start1, size1, start2, size2;
 -         abstractFifo.prepareToRead (1, start1, size1, start2, size2);
 - 
 -         if (size1 > 0)
 -         {
 -             type = fifoRequestsType[start1];
 -             parameter = fifoRequestsParameter[start1];
 -         }
 - 
 -         if (size2 > 0)
 -         {
 -             type = fifoRequestsType[start2];
 -             parameter = fifoRequestsParameter[start2];
 -         }
 - 
 -         abstractFifo.finishedRead (size1 + size2);
 -     }
 - 
 -     /** Returns the number of requests that still need to be processed. */
 -     int getNumRemainingEntries() const noexcept
 -     {
 -         return abstractFifo.getNumReady();
 -     }
 - 
 -     //==============================================================================
 -     /** This function processes all the change requests to remove all the the
 -         redundant ones, and to tell what kind of initialization must be done.
 - 
 -         Depending on the results, the convolution engines might be reset, or
 -         simply updated, or they might not need any change at all.
 -     */
 -     void processFifo()
 -     {
 -         if (getNumRemainingEntries() == 0 || isThreadRunning() || mustInterpolate)
 -             return;
 - 
 -         auto numRequests = 0;
 - 
 -         // retrieve the information from the FIFO for processing
 -         while (getNumRemainingEntries() > 0 && numRequests < fifoSize)
 -         {
 -             ChangeRequest type = ChangeRequest::changeEngine;
 -             juce::var parameter;
 - 
 -             readFromFifo (type, parameter);
 - 
 -             requestsType.setUnchecked (numRequests, type);
 -             requestsParameter.setUnchecked (numRequests, parameter);
 - 
 -             numRequests++;
 -         }
 - 
 -         // remove any useless messages
 -         for (auto i = 0; i < (int) ChangeRequest::numChangeRequestTypes; ++i)
 -         {
 -             bool exists = false;
 - 
 -             for (auto n = numRequests; --n >= 0;)
 -             {
 -                 if (requestsType[n] == (ChangeRequest) i)
 -                 {
 -                     if (! exists)
 -                         exists = true;
 -                     else
 -                         requestsType.setUnchecked (n, ChangeRequest::changeIgnore);
 -                 }
 -             }
 -         }
 - 
 -         changeLevel = 0;
 - 
 -         for (auto n = 0; n < numRequests; ++n)
 -         {
 -             switch (requestsType[n])
 -             {
 -                 case ChangeRequest::changeEngine:
 -                     changeLevel = 3;
 -                     break;
 - 
 -                 case ChangeRequest::changeSampleRate:
 -                 {
 -                     double newSampleRate = requestsParameter[n];
 - 
 -                     if (currentInfo.sampleRate != newSampleRate)
 -                         changeLevel = 3;
 - 
 -                     currentInfo.sampleRate = newSampleRate;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeMaximumBufferSize:
 -                 {
 -                     int newMaximumBufferSize = requestsParameter[n];
 - 
 -                     if (currentInfo.maximumBufferSize != (size_t) newMaximumBufferSize)
 -                         changeLevel = 3;
 - 
 -                     currentInfo.maximumBufferSize = (size_t) newMaximumBufferSize;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeSource:
 -                 {
 -                     auto* arrayParameters = requestsParameter[n].getArray();
 -                     auto newSourceType = static_cast<SourceType> (static_cast<int> (arrayParameters->getUnchecked (0)));
 - 
 -                     if (currentInfo.sourceType != newSourceType)
 -                         changeLevel = jmax (2, changeLevel);
 - 
 -                     if (newSourceType == SourceType::sourceBinaryData)
 -                     {
 -                         auto& prm = arrayParameters->getRawDataPointer()[1];
 -                         auto* newMemoryBlock = prm.getBinaryData();
 - 
 -                         auto* newPtr = newMemoryBlock->getData();
 -                         auto newSize = (int) newMemoryBlock->getSize();
 - 
 -                         if (currentInfo.sourceData != newPtr || currentInfo.sourceDataSize != newSize)
 -                             changeLevel = jmax (2, changeLevel);
 - 
 -                         currentInfo.sourceType = SourceType::sourceBinaryData;
 -                         currentInfo.sourceData = newPtr;
 -                         currentInfo.sourceDataSize = newSize;
 -                         currentInfo.fileImpulseResponse = File();
 -                     }
 -                     else if (newSourceType == SourceType::sourceAudioFile)
 -                     {
 -                         File newFile (arrayParameters->getUnchecked (1).toString());
 - 
 -                         if (currentInfo.fileImpulseResponse != newFile)
 -                             changeLevel = jmax (2, changeLevel);
 - 
 -                         currentInfo.sourceType = SourceType::sourceAudioFile;
 -                         currentInfo.fileImpulseResponse = newFile;
 -                         currentInfo.sourceData = nullptr;
 -                         currentInfo.sourceDataSize = 0;
 -                     }
 -                     else if (newSourceType == SourceType::sourceAudioBuffer)
 -                     {
 -                         double originalSampleRate (arrayParameters->getUnchecked (1));
 -                         changeLevel = jmax (2, changeLevel);
 - 
 -                         currentInfo.sourceType = SourceType::sourceAudioBuffer;
 -                         currentInfo.originalSampleRate = originalSampleRate;
 -                         currentInfo.fileImpulseResponse = File();
 -                         currentInfo.sourceData = nullptr;
 -                         currentInfo.sourceDataSize = 0;
 -                     }
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeImpulseResponseSize:
 -                 {
 -                     int64 newSize = requestsParameter[n];
 - 
 -                     if (currentInfo.wantedSize != newSize)
 -                         changeLevel = jmax (1, changeLevel);
 - 
 -                     currentInfo.wantedSize = newSize;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeStereo:
 -                 {
 -                     bool newWantsStereo = requestsParameter[n];
 - 
 -                     if (currentInfo.wantsStereo != newWantsStereo)
 -                         changeLevel = jmax (0, changeLevel);
 - 
 -                     currentInfo.wantsStereo = newWantsStereo;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeTrimming:
 -                 {
 -                     bool newWantsTrimming = requestsParameter[n];
 - 
 -                     if (currentInfo.wantsTrimming != newWantsTrimming)
 -                         changeLevel = jmax (1, changeLevel);
 - 
 -                     currentInfo.wantsTrimming = newWantsTrimming;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::changeNormalisation:
 -                 {
 -                     bool newWantsNormalisation = requestsParameter[n];
 - 
 -                     if (currentInfo.wantsNormalisation != newWantsNormalisation)
 -                         changeLevel = jmax (1, changeLevel);
 - 
 -                     currentInfo.wantsNormalisation = newWantsNormalisation;
 -                 }
 -                 break;
 - 
 -                 case ChangeRequest::numChangeRequestTypes:
 -                 case ChangeRequest::changeIgnore:
 -                     break;
 - 
 -                 default:
 -                     jassertfalse;
 -                     break;
 -             }
 -         }
 - 
 -         if (currentInfo.sourceType == SourceType::sourceNone)
 -         {
 -             currentInfo.sourceType = SourceType::sourceAudioBuffer;
 - 
 -             if (currentInfo.sampleRate == 0)
 -                 currentInfo.sampleRate = 44100;
 - 
 -             if (currentInfo.maximumBufferSize == 0)
 -                 currentInfo.maximumBufferSize = 128;
 - 
 -             currentInfo.originalSampleRate = currentInfo.sampleRate;
 -             currentInfo.wantedSize = 1;
 -             currentInfo.fileImpulseResponse = File();
 -             currentInfo.sourceData = nullptr;
 -             currentInfo.sourceDataSize = 0;
 - 
 -             AudioBuffer<float> newBuffer;
 -             newBuffer.setSize (1, 1);
 -             newBuffer.setSample (0, 0, 1.f);
 - 
 -             copyBufferToTemporaryLocation (newBuffer);
 -         }
 - 
 -         // action depending on the change level
 -         if (changeLevel == 3)
 -         {
 -             loadImpulseResponse();
 -             processImpulseResponse();
 -             initializeConvolutionEngines();
 -         }
 -         else if (changeLevel > 0)
 -         {
 -             startThread();
 -         }
 -     }
 - 
 -     //==============================================================================
 -     /** This function copies a buffer to a temporary location, so that any external
 -         audio source can be processed then in the dedicated thread.
 -     */
 -     void copyBufferToTemporaryLocation (dsp::AudioBlock<float> block)
 -     {
 -         const SpinLock::ScopedLockType sl (processLock);
 - 
 -         currentInfo.originalNumChannels = (block.getNumChannels() > 1 ? 2 : 1);
 -         currentInfo.originalSize = (int) jmin ((size_t) maximumTimeInSamples, block.getNumSamples());
 - 
 -         for (auto channel = 0; channel < currentInfo.originalNumChannels; ++channel)
 -             temporaryBuffer.copyFrom (channel, 0, block.getChannelPointer ((size_t) channel), (int) currentInfo.originalSize);
 -     }
 - 
 -     //==============================================================================
 -     /** Resets the convolution engines states. */
 -     void reset()
 -     {
 -         for (auto* e : engines)
 -             e->reset();
 - 
 -         mustInterpolate = false;
 - 
 -         processFifo();
 -     }
 - 
 -     /** Convolution processing handling interpolation between previous and new states
 -         of the convolution engines.
 -     */
 -     void processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output)
 -     {
 -         processFifo();
 - 
 -         size_t numChannels = jmin (input.getNumChannels(), (size_t) (currentInfo.wantsStereo ? 2 : 1));
 -         size_t numSamples  = jmin (input.getNumSamples(), output.getNumSamples());
 - 
 -         if (mustInterpolate == false)
 -         {
 -             for (size_t channel = 0; channel < numChannels; ++channel)
 -                 engines[(int) channel]->processSamples (input.getChannelPointer (channel), output.getChannelPointer (channel), numSamples);
 -         }
 -         else
 -         {
 -             auto interpolated = dsp::AudioBlock<float> (interpolationBuffer).getSubBlock (0, numSamples);
 - 
 -             for (size_t channel = 0; channel < numChannels; ++channel)
 -             {
 -                 auto&& buffer = output.getSingleChannelBlock (channel);
 - 
 -                 interpolationBuffer.copyFrom (0, 0, input.getChannelPointer (channel), (int) numSamples);
 - 
 -                 engines[(int) channel]->processSamples (input.getChannelPointer (channel), buffer.getChannelPointer (0), numSamples);
 -                 changeVolumes[channel].applyGain (buffer.getChannelPointer (0), (int) numSamples);
 - 
 -                 auto* interPtr = interpolationBuffer.getWritePointer (0);
 -                 engines[(int) channel + 2]->processSamples (interPtr, interPtr, numSamples);
 -                 changeVolumes[channel + 2].applyGain (interPtr, (int) numSamples);
 - 
 -                 buffer += interpolated;
 -             }
 - 
 -             if (input.getNumChannels() > 1 && currentInfo.wantsStereo == false)
 -             {
 -                 auto&& buffer = output.getSingleChannelBlock (1);
 - 
 -                 changeVolumes[1].applyGain (buffer.getChannelPointer (0), (int) numSamples);
 -                 changeVolumes[3].applyGain (buffer.getChannelPointer (0), (int) numSamples);
 -             }
 - 
 -             if (changeVolumes[0].isSmoothing() == false)
 -             {
 -                 mustInterpolate = false;
 - 
 -                 for (auto channel = 0; channel < 2; ++channel)
 -                     engines[channel]->copyStateFromOtherEngine (*engines[channel + 2]);
 -             }
 -         }
 - 
 -         if (input.getNumChannels() > 1 && currentInfo.wantsStereo == false)
 -             output.getSingleChannelBlock (1).copyFrom (output.getSingleChannelBlock (0));
 -     }
 - 
 -     //==============================================================================
 -     const int64 maximumTimeInSamples = 10 * 96000;
 - 
 - private:
 -     //==============================================================================
 -     /** This the thread run function which does the preparation of data depending
 -         on the requested change level.
 -     */
 -     void run() override
 -     {
 -         if (changeLevel == 2)
 -         {
 -             loadImpulseResponse();
 - 
 -             if (isThreadRunning() && threadShouldExit())
 -                 return;
 -         }
 - 
 -         processImpulseResponse();
 - 
 -         if (isThreadRunning() && threadShouldExit())
 -             return;
 - 
 -         initializeConvolutionEngines();
 -     }
 - 
 -     /** Loads the impulse response from the requested audio source. */
 -     void loadImpulseResponse()
 -     {
 -         if (currentInfo.sourceType == SourceType::sourceBinaryData)
 -         {
 -             copyAudioStreamInAudioBuffer (std::make_unique<MemoryInputStream> (currentInfo.sourceData, (size_t) currentInfo.sourceDataSize, false));
 -         }
 -         else if (currentInfo.sourceType == SourceType::sourceAudioFile)
 -         {
 -             copyAudioStreamInAudioBuffer (std::make_unique<FileInputStream> (currentInfo.fileImpulseResponse));
 -         }
 -         else if (currentInfo.sourceType == SourceType::sourceAudioBuffer)
 -         {
 -             copyBufferFromTemporaryLocation();
 -         }
 -     }
 - 
 -     /** Processes the impulse response data with the requested treatments
 -         and resampling if needed.
 -     */
 -     void processImpulseResponse()
 -     {
 -         trimAndResampleImpulseResponse (currentInfo.originalNumChannels, currentInfo.originalSampleRate, currentInfo.wantsTrimming);
 - 
 -         if (isThreadRunning() && threadShouldExit())
 -             return;
 - 
 -         if (currentInfo.wantsNormalisation)
 -         {
 -             if (currentInfo.originalNumChannels > 1)
 -             {
 -                 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (int) currentInfo.finalSize, 1.0);
 -                 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (1), (int) currentInfo.finalSize, 1.0);
 -             }
 -             else
 -             {
 -                 normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (int) currentInfo.finalSize, 1.0);
 -             }
 -         }
 - 
 -         if (currentInfo.originalNumChannels == 1)
 -             currentInfo.buffer->copyFrom (1, 0, *currentInfo.buffer, 0, 0, (int) currentInfo.finalSize);
 -     }
 - 
 -     /** Converts the data from an audio file into a stereo audio buffer of floats, and
 -         performs resampling if necessary.
 -     */
 -     bool copyAudioStreamInAudioBuffer (std::unique_ptr<InputStream> stream)
 -     {
 -         AudioFormatManager manager;
 -         manager.registerBasicFormats();
 -         std::unique_ptr<AudioFormatReader> formatReader (manager.createReaderFor (std::move (stream)));
 - 
 -         if (formatReader != nullptr)
 -         {
 -             currentInfo.originalNumChannels = formatReader->numChannels > 1 ? 2 : 1;
 -             currentInfo.originalSampleRate = formatReader->sampleRate;
 -             currentInfo.originalSize = static_cast<int> (jmin (maximumTimeInSamples, formatReader->lengthInSamples));
 - 
 -             impulseResponseOriginal.clear();
 -             formatReader->read (&(impulseResponseOriginal), 0, (int) currentInfo.originalSize, 0, true, currentInfo.originalNumChannels > 1);
 - 
 -             return true;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     /** Copies a buffer from a temporary location to the impulseResponseOriginal
 -         buffer for the sourceAudioBuffer.
 -     */
 -     void copyBufferFromTemporaryLocation()
 -     {
 -         const SpinLock::ScopedLockType sl (processLock);
 - 
 -         for (auto channel = 0; channel < currentInfo.originalNumChannels; ++channel)
 -             impulseResponseOriginal.copyFrom (channel, 0, temporaryBuffer, channel, 0, (int) currentInfo.originalSize);
 -     }
 - 
 -     /** Trim and resample the impulse response if needed. */
 -     void trimAndResampleImpulseResponse (int numChannels, double srcSampleRate, bool mustTrim)
 -     {
 -         auto thresholdTrim = Decibels::decibelsToGain (-80.0f);
 -         auto indexStart = 0;
 -         auto indexEnd = currentInfo.originalSize - 1;
 - 
 -         if (mustTrim)
 -         {
 -             indexStart = currentInfo.originalSize - 1;
 -             indexEnd = 0;
 - 
 -             for (auto channel = 0; channel < numChannels; ++channel)
 -             {
 -                 auto localIndexStart = 0;
 -                 auto localIndexEnd = currentInfo.originalSize - 1;
 - 
 -                 auto* channelData = impulseResponseOriginal.getReadPointer (channel);
 - 
 -                 while (localIndexStart < currentInfo.originalSize - 1
 -                         && channelData[localIndexStart] <= thresholdTrim
 -                         && channelData[localIndexStart] >= -thresholdTrim)
 -                     ++localIndexStart;
 - 
 -                 while (localIndexEnd >= 0
 -                         && channelData[localIndexEnd] <= thresholdTrim
 -                         && channelData[localIndexEnd] >= -thresholdTrim)
 -                     --localIndexEnd;
 - 
 -                 indexStart = jmin (indexStart, localIndexStart);
 -                 indexEnd = jmax (indexEnd, localIndexEnd);
 -             }
 - 
 -             if (indexStart > 0)
 -             {
 -                 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 = indexEnd - indexStart + 1; i < currentInfo.originalSize - 1; ++i)
 -                         channelData[i] = 0.0f;
 -                 }
 -             }
 -         }
 - 
 -         if (currentInfo.sampleRate == srcSampleRate)
 -         {
 -             // No resampling
 -             currentInfo.finalSize = jmin (static_cast<int> (currentInfo.wantedSize), indexEnd - indexStart + 1);
 - 
 -             impulseResponse.clear();
 - 
 -             for (auto channel = 0; channel < numChannels; ++channel)
 -                 impulseResponse.copyFrom (channel, 0, impulseResponseOriginal, channel, 0, (int) currentInfo.finalSize);
 -         }
 -         else
 -         {
 -             // Resampling
 -             auto factorReading = srcSampleRate / currentInfo.sampleRate;
 -             currentInfo.finalSize = jmin (static_cast<int> (currentInfo.wantedSize), roundToInt ((indexEnd - indexStart + 1) / factorReading));
 - 
 -             impulseResponse.clear();
 - 
 -             MemoryAudioSource memorySource (impulseResponseOriginal, false);
 -             ResamplingAudioSource resamplingSource (&memorySource, false, (int) numChannels);
 - 
 -             resamplingSource.setResamplingRatio (factorReading);
 -             resamplingSource.prepareToPlay ((int) currentInfo.finalSize, currentInfo.sampleRate);
 - 
 -             AudioSourceChannelInfo info;
 -             info.startSample = 0;
 -             info.numSamples = (int) currentInfo.finalSize;
 -             info.buffer = &impulseResponse;
 - 
 -             resamplingSource.getNextAudioBlock (info);
 -         }
 - 
 -         // Filling the second channel with the first if necessary
 -         if (numChannels == 1)
 -             impulseResponse.copyFrom (1, 0, impulseResponse, 0, 0, (int) currentInfo.finalSize);
 -     }
 - 
 -     /** Normalisation of the impulse response based on its energy. */
 -     void normaliseImpulseResponse (float* samples, int numSamples, double factorResampling) const
 -     {
 -         auto magnitude = 0.0f;
 - 
 -         for (auto i = 0; i < numSamples; ++i)
 -             magnitude += samples[i] * samples[i];
 - 
 -         auto magnitudeInv = 1.0f / (4.0f * std::sqrt (magnitude)) * 0.5f * static_cast <float> (factorResampling);
 - 
 -         for (auto i = 0; i < numSamples; ++i)
 -             samples[i] *= magnitudeInv;
 -     }
 - 
 -     // ================================================================================================================
 -     /** Initializes the convolution engines depending on the provided sizes
 -         and performs the FFT on the impulse responses.
 -     */
 -     void initializeConvolutionEngines()
 -     {
 -         if (currentInfo.maximumBufferSize == 0)
 -             return;
 - 
 -         if (changeLevel == 3)
 -         {
 -             for (auto i = 0; i < 2; ++i)
 -                 engines[i]->initializeConvolutionEngine (currentInfo, i);
 - 
 -             mustInterpolate = false;
 -         }
 -         else
 -         {
 -             for (auto i = 0; i < 2; ++i)
 -             {
 -                 engines[i + 2]->initializeConvolutionEngine (currentInfo, i);
 -                 engines[i + 2]->reset();
 - 
 -                 if (isThreadRunning() && threadShouldExit())
 -                     return;
 -             }
 - 
 -             for (auto i = 0; i < 2; ++i)
 -             {
 -                 changeVolumes[i].setTargetValue (1.0f);
 -                 changeVolumes[i].reset (currentInfo.sampleRate, 0.05);
 -                 changeVolumes[i].setTargetValue (0.0f);
 - 
 -                 changeVolumes[i + 2].setTargetValue (0.0f);
 -                 changeVolumes[i + 2].reset (currentInfo.sampleRate, 0.05);
 -                 changeVolumes[i + 2].setTargetValue (1.0f);
 - 
 -             }
 - 
 -             mustInterpolate = true;
 -         }
 -     }
 - 
 - 
 -     //==============================================================================
 -     static constexpr int fifoSize = 1024;           // the size of the fifo which handles all the change requests
 -     AbstractFifo abstractFifo;                      // the abstract fifo
 - 
 -     Array<ChangeRequest> fifoRequestsType;          // an array of ChangeRequest
 -     Array<juce::var> fifoRequestsParameter;         // an array of change parameters
 - 
 -     Array<ChangeRequest> requestsType;              // an array of ChangeRequest
 -     Array<juce::var> requestsParameter;             // an array of change parameters
 - 
 -     int changeLevel = 0;                            // the current level of requested change in the convolution engine
 - 
 -     //==============================================================================
 -     ConvolutionEngine::ProcessingInformation currentInfo;  // the information about the impulse response to load
 - 
 -     AudioBuffer<float> temporaryBuffer;             // a temporary buffer that is used when the function copyAndLoadImpulseResponse is called in the main API
 -     SpinLock processLock;                           // a necessary lock to use with this temporary buffer
 - 
 -     AudioBuffer<float> impulseResponseOriginal;     // a buffer with the original impulse response
 -     AudioBuffer<float> impulseResponse;             // a buffer with the impulse response trimmed, resampled, resized and normalised
 - 
 -     //==============================================================================
 -     OwnedArray<ConvolutionEngine> engines;          // the 4 convolution engines being used
 - 
 -     AudioBuffer<float> interpolationBuffer;         // a buffer to do the interpolation between the convolution engines 0-1 and 2-3
 -     LogRampedValue<float> changeVolumes[4];         // the volumes for each convolution engine during interpolation
 - 
 -     bool mustInterpolate = false;                   // tells if the convolution engines outputs must be currently interpolated
 - 
 -     //==============================================================================
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
 - };
 - 
 - 
 - //==============================================================================
 - Convolution::Convolution()
 - {
 -     pimpl.reset (new Pimpl());
 -     pimpl->addToFifo (Convolution::Pimpl::ChangeRequest::changeEngine, juce::var (0));
 - }
 - 
 - Convolution::~Convolution()
 - {
 - }
 - 
 - void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceDataSize,
 -                                        bool wantsStereo, bool wantsTrimming, size_t size,
 -                                        bool wantsNormalisation)
 - {
 -     if (sourceData == nullptr)
 -         return;
 - 
 -     auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
 -     auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
 - 
 -     Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
 -                                      Pimpl::ChangeRequest::changeImpulseResponseSize,
 -                                      Pimpl::ChangeRequest::changeStereo,
 -                                      Pimpl::ChangeRequest::changeTrimming,
 -                                      Pimpl::ChangeRequest::changeNormalisation };
 - 
 -     Array<juce::var> sourceParameter;
 - 
 -     sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceBinaryData));
 -     sourceParameter.add (juce::var (sourceData, sourceDataSize));
 - 
 -     juce::var parameters[] = { juce::var (sourceParameter),
 -                                juce::var (static_cast<int64> (wantedSize)),
 -                                juce::var (wantsStereo),
 -                                juce::var (wantsTrimming),
 -                                juce::var (wantsNormalisation) };
 - 
 -     pimpl->addToFifo (types, parameters, 5);
 - }
 - 
 - void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wantsStereo,
 -                                        bool wantsTrimming, size_t size, bool wantsNormalisation)
 - {
 -     if (! fileImpulseResponse.existsAsFile())
 -         return;
 - 
 -     auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
 -     auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
 - 
 -     Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
 -                                      Pimpl::ChangeRequest::changeImpulseResponseSize,
 -                                      Pimpl::ChangeRequest::changeStereo,
 -                                      Pimpl::ChangeRequest::changeTrimming,
 -                                      Pimpl::ChangeRequest::changeNormalisation };
 - 
 -     Array<juce::var> sourceParameter;
 - 
 -     sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioFile));
 -     sourceParameter.add (juce::var (fileImpulseResponse.getFullPathName()));
 - 
 -     juce::var parameters[] = { juce::var (sourceParameter),
 -                                juce::var (static_cast<int64> (wantedSize)),
 -                                juce::var (wantsStereo),
 -                                juce::var (wantsTrimming),
 -                                juce::var (wantsNormalisation) };
 - 
 -     pimpl->addToFifo (types, parameters, 5);
 - }
 - 
 - void Convolution::copyAndLoadImpulseResponseFromBuffer (AudioBuffer<float>& buffer,
 -                                                         double bufferSampleRate, bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size)
 - {
 -     copyAndLoadImpulseResponseFromBlock (AudioBlock<float> (buffer), bufferSampleRate,
 -         wantsStereo, wantsTrimming, wantsNormalisation, size);
 - }
 - 
 - void Convolution::copyAndLoadImpulseResponseFromBlock (AudioBlock<float> block, double bufferSampleRate,
 -                                                        bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size)
 - {
 -     jassert (bufferSampleRate > 0);
 - 
 -     if (block.getNumSamples() == 0)
 -         return;
 - 
 -     auto maximumSamples = (size_t) pimpl->maximumTimeInSamples;
 -     auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples));
 - 
 -     pimpl->copyBufferToTemporaryLocation (block);
 - 
 -     Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource,
 -                                      Pimpl::ChangeRequest::changeImpulseResponseSize,
 -                                      Pimpl::ChangeRequest::changeStereo,
 -                                      Pimpl::ChangeRequest::changeTrimming,
 -                                      Pimpl::ChangeRequest::changeNormalisation };
 - 
 -     Array<juce::var> sourceParameter;
 -     sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer));
 -     sourceParameter.add (juce::var (bufferSampleRate));
 - 
 -     juce::var parameters[] = { juce::var (sourceParameter),
 -                                juce::var (static_cast<int64> (wantedSize)),
 -                                juce::var (wantsStereo),
 -                                juce::var (wantsTrimming),
 -                                juce::var (wantsNormalisation) };
 - 
 -     pimpl->addToFifo (types, parameters, 5);
 - }
 - 
 - void Convolution::prepare (const ProcessSpec& spec)
 - {
 -     jassert (isPositiveAndBelow (spec.numChannels, static_cast<uint32> (3))); // only mono and stereo is supported
 - 
 -     Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSampleRate,
 -                                      Pimpl::ChangeRequest::changeMaximumBufferSize };
 - 
 -     juce::var parameters[] = { juce::var (spec.sampleRate),
 -                                juce::var (static_cast<int> (spec.maximumBlockSize)) };
 - 
 -     pimpl->addToFifo (types, parameters, 2);
 -     pimpl->initProcessing (static_cast<int> (spec.maximumBlockSize));
 - 
 -     for (size_t channel = 0; channel < spec.numChannels; ++channel)
 -     {
 -         volumeDry[channel].reset (spec.sampleRate, 0.05);
 -         volumeWet[channel].reset (spec.sampleRate, 0.05);
 -     }
 - 
 -     sampleRate = spec.sampleRate;
 -     dryBuffer = AudioBlock<float> (dryBufferStorage,
 -                                    jmin (spec.numChannels, 2u),
 -                                    spec.maximumBlockSize);
 - 
 -     isActive = true;
 - }
 - 
 - void Convolution::reset() noexcept
 - {
 -     dryBuffer.clear();
 -     pimpl->reset();
 - }
 - 
 - void Convolution::processSamples (const AudioBlock<const float>& input, AudioBlock<float>& output, bool isBypassed) noexcept
 - {
 -     if (! isActive)
 -         return;
 - 
 -     jassert (input.getNumChannels() == output.getNumChannels());
 -     jassert (isPositiveAndBelow (input.getNumChannels(), static_cast<size_t> (3))); // only mono and stereo is supported
 - 
 -     auto numChannels = jmin (input.getNumChannels(), (size_t) 2);
 -     auto numSamples  = jmin (input.getNumSamples(), output.getNumSamples());
 - 
 -     auto dry = dryBuffer.getSubsetChannelBlock (0, numChannels);
 - 
 -     if (volumeDry[0].isSmoothing())
 -     {
 -         dry.copyFrom (input);
 - 
 -         for (size_t channel = 0; channel < numChannels; ++channel)
 -             volumeDry[channel].applyGain (dry.getChannelPointer (channel), (int) numSamples);
 - 
 -         pimpl->processSamples (input, output);
 - 
 -         for (size_t channel = 0; channel < numChannels; ++channel)
 -             volumeWet[channel].applyGain (output.getChannelPointer (channel), (int) numSamples);
 - 
 -         output += dry;
 -     }
 -     else
 -     {
 -         if (! currentIsBypassed)
 -             pimpl->processSamples (input, output);
 - 
 -         if (isBypassed != currentIsBypassed)
 -         {
 -             currentIsBypassed = isBypassed;
 - 
 -             for (size_t channel = 0; channel < numChannels; ++channel)
 -             {
 -                 volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
 -                 volumeDry[channel].reset (sampleRate, 0.05);
 -                 volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
 - 
 -                 volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
 -                 volumeWet[channel].reset (sampleRate, 0.05);
 -                 volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
 -             }
 -         }
 -     }
 - }
 - 
 - } // namespace dsp
 - } // namespace juce
 
 
  |