1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

ARAPluginDemo: Use crossfade in the EditorRenderer to avoid clicks

This commit is contained in:
attila 2022-12-14 18:27:10 +01:00
parent 180bbce579
commit a97c1d7ee0

View file

@ -117,6 +117,18 @@ private:
SpinLock processingFlag;
};
static void crossfade (const float* sourceA,
const float* sourceB,
float aProportionAtStart,
float aProportionAtFinish,
float* destinationBuffer,
int numSamples)
{
AudioBuffer<float> destination { &destinationBuffer, 1, numSamples };
destination.copyFromWithRamp (0, 0, sourceA, numSamples, aProportionAtStart, aProportionAtFinish);
destination.addFromWithRamp (0, 0, sourceB, numSamples, 1.0f - aProportionAtStart, 1.0f - aProportionAtFinish);
}
class Looper
{
public:
@ -138,28 +150,60 @@ public:
}
const auto numChannelsToCopy = std::min (inputBuffer->getNumChannels(), buffer.getNumChannels());
const auto actualCrossfadeLengthSamples = std::min (loopRange.getLength() / 2, (int64) desiredCrossfadeLengthSamples);
for (auto samplesCopied = 0; samplesCopied < buffer.getNumSamples();)
{
const auto numSamplesToCopy =
std::min (buffer.getNumSamples() - samplesCopied, (int) (loopRange.getEnd() - pos));
const auto [needsCrossfade, samplePosOfNextCrossfadeTransition] = [&]() -> std::pair<bool, int64>
{
if (const auto endOfFadeIn = loopRange.getStart() + actualCrossfadeLengthSamples; pos < endOfFadeIn)
return { true, endOfFadeIn };
return { false, loopRange.getEnd() - actualCrossfadeLengthSamples };
}();
const auto samplesToNextCrossfadeTransition = samplePosOfNextCrossfadeTransition - pos;
const auto numSamplesToCopy = std::min (buffer.getNumSamples() - samplesCopied,
(int) samplesToNextCrossfadeTransition);
const auto getFadeInGainAtPos = [this, actualCrossfadeLengthSamples] (auto p)
{
return jmap ((float) p, (float) loopRange.getStart(), (float) loopRange.getStart() + (float) actualCrossfadeLengthSamples - 1.0f, 0.0f, 1.0f);
};
for (int i = 0; i < numChannelsToCopy; ++i)
{
buffer.copyFrom (i, samplesCopied, *inputBuffer, i, (int) pos, numSamplesToCopy);
if (needsCrossfade)
{
const auto overlapStart = loopRange.getEnd() - actualCrossfadeLengthSamples
+ (pos - loopRange.getStart());
crossfade (inputBuffer->getReadPointer (i, (int) pos),
inputBuffer->getReadPointer (i, (int) overlapStart),
getFadeInGainAtPos (pos),
getFadeInGainAtPos (pos + numSamplesToCopy),
buffer.getWritePointer (i, samplesCopied),
numSamplesToCopy);
}
else
{
buffer.copyFrom (i, samplesCopied, *inputBuffer, i, (int) pos, numSamplesToCopy);
}
}
samplesCopied += numSamplesToCopy;
pos += numSamplesToCopy;
jassert (pos <= loopRange.getEnd());
jassert (pos <= loopRange.getEnd() - actualCrossfadeLengthSamples);
if (pos == loopRange.getEnd())
if (pos == loopRange.getEnd() - actualCrossfadeLengthSamples)
pos = loopRange.getStart();
}
}
private:
static constexpr int desiredCrossfadeLengthSamples = 50;
const AudioBuffer<float>* inputBuffer;
Range<int64> loopRange;
int64 pos;
@ -510,8 +554,21 @@ public:
if (! locked)
return true;
const auto fadeOutIfNecessary = [this, &buffer]
{
if (std::exchange (wasPreviewing, false))
{
previewLooper.writeInto (buffer);
const auto fadeOutStart = std::max (0, buffer.getNumSamples() - 50);
buffer.applyGainRamp (fadeOutStart, buffer.getNumSamples() - fadeOutStart, 1.0f, 0.0f);
}
};
if (positionInfo.getIsPlaying())
{
fadeOutIfNecessary();
return true;
}
if (const auto previewedRegion = previewState->previewedRegion.load())
{
@ -565,9 +622,19 @@ public:
else
{
previewLooper.writeInto (buffer);
if (! std::exchange (wasPreviewing, true))
{
const auto fadeInLength = std::min (50, buffer.getNumSamples());
buffer.applyGainRamp (0, fadeInLength, 0.0f, 1.0f);
}
}
}
}
else
{
fadeOutIfNecessary();
}
return true;
});
@ -599,6 +666,7 @@ private:
double lastPreviewTime = 0.0;
ARAPlaybackRegion* lastPlaybackRegion = nullptr;
bool lastPreviewDimmed = false;
bool wasPreviewing = false;
std::unique_ptr<AudioBuffer<float>> previewBuffer;
Looper previewLooper;