/** Convolution processing handling interpolation between previous and new states
        of the convolution engines.
    */
    void processSamples (const AudioBlock<float>& input, AudioBlock<float>& output)
    {
        processFifo();

        size_t numChannels = input.getNumChannels();
        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 = AudioBlock<float> (interpolationBuffer).getSubBlock (0, numSamples);

            for (size_t channel = 0; channel < numChannels; ++channel)
            {
                auto&& buffer = output.getSingleChannelBlock (channel);

                interpolationBuffer.copyFrom ((int) channel, 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 ((int) channel);
                engines[(int) channel + 2]->processSamples (interPtr, interPtr, numSamples);
                changeVolumes[channel + 2].applyGain (interPtr, (int) numSamples);

                buffer += interpolated.getSingleChannelBlock (channel);
            }

            if (changeVolumes[0].isSmoothing() == false)
            {
                mustInterpolate = false;

                for (auto channel = 0; channel < 2; ++channel)
                    engines[channel]->copyStateFromOtherEngine (*engines[channel + 2]);
            }
        }
    }