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:
parent
180bbce579
commit
a97c1d7ee0
1 changed files with 73 additions and 5 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue