From 8f3be5ca3c55da22896516fa2a01a7ada004b01e Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 5 Oct 2020 13:48:21 +0200 Subject: [PATCH] Compute loop information at the start of the voice The check for actual looping is still done in the render method, to see if we do have enough samples --- src/sfizz/Voice.cpp | 63 +++++++++++++++++++++++++++++---------------- src/sfizz/Voice.h | 19 ++++++++++++++ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 654dba5b..c1b05e8a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -99,6 +99,7 @@ void sfz::Voice::startVoice(Region* region, int delay, const TriggerEvent& event switchState(State::cleanMeUp); return; } + updateLoopInformation(); speedRatio = static_cast(currentPromise->sampleRate / this->sampleRate); } @@ -558,26 +559,11 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept } // calculate loop characteristics - bool isLooping = false; - int loopStart = 0; - int loopEnd = 0; - int loopSize = 0; - int loopXfadeSize = 0; - int loopXfOutStart = 0; - int loopXfInStart = 0; // Note: beware in case of negative index + const bool isLooping = region->shouldLoop() + && (static_cast(loop.end) < source.getNumFrames()); SpanHolder> xfadeTemp[2]; SpanHolder> xfadeIndexTemp[1]; - if (region->shouldLoop()) { - loopEnd = region->loopEnd(currentPromise->oversamplingFactor); - isLooping = static_cast(loopEnd) < source.getNumFrames(); - } if (isLooping) { - loopStart = static_cast(region->loopStart(currentPromise->oversamplingFactor)); - loopSize = loopEnd + 1 - loopStart; - loopXfadeSize = static_cast( - lroundPositive(region->loopCrossfade * static_cast(currentPromise->oversamplingFactor) * currentPromise->sampleRate)); - loopXfOutStart = loopEnd + 1 - loopXfadeSize; - loopXfInStart = loopStart - loopXfadeSize; for (auto& buf : xfadeTemp) { buf = resources.bufferPool.getBuffer(numSamples); if (!buf) @@ -642,12 +628,12 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept int index = (*indices)[i]; // wrap indices post loop-entry around the loop segment - int wrappedIndex = (index <= loopEnd) ? index : - (loopStart + (index - loopStart) % loopSize); + int wrappedIndex = (index <= loop.end) ? index : + (loop.start + (index - loop.start) % loop.size); (*indices)[i] = wrappedIndex; // identify the partition this index is in - bool xfading = wrappedIndex >= loopStart && wrappedIndex >= loopXfOutStart; + bool xfading = wrappedIndex >= loop.start && wrappedIndex >= loop.xfOutStart; int partitionType = xfading ? kPartitionLoopXfade : kPartitionNormal; // if looping or entering a different type, start a new partition bool start = i == 0 || wrappedIndex < oldIndex || partitionType != oldPartitionType; @@ -712,7 +698,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept // compute crossfade positions for (unsigned i = 0; i < ptSize; ++i) { float pos = ptIndices[i] + ptCoeffs[i]; - xfCurvePos[i] = (pos - loopXfOutStart) / loopXfadeSize; + xfCurvePos[i] = (pos - loop.xfOutStart) / loop.xfSize; } //----------------------------------------------------------------// @@ -752,7 +738,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept // compute indices of the crossfade input segment absl::Span xfInIndices = xfadeIndexTemp[0]->first(ptSize); absl::c_copy(ptIndices, xfInIndices.begin()); - subtract1(loopXfOutStart - loopXfInStart, xfInIndices); + subtract1(loop.xfOutStart - loop.xfInStart, xfInIndices); // disregard the segment whose indices have been pushed // into the negatives, take these virtually as zeroes. @@ -1067,6 +1053,8 @@ void sfz::Voice::reset() noexcept floatPositionOffset = 0.0f; noteIsOff = false; + resetLoopInformation(); + powerFollower.clear(); for (auto& filter : filters) @@ -1078,6 +1066,37 @@ void sfz::Voice::reset() noexcept removeVoiceFromRing(); } +void sfz::Voice::resetLoopInformation() noexcept +{ + loop.start = 0; + loop.end = 0; + loop.size = 0; + loop.xfSize = 0; + loop.xfOutStart = 0; + loop.xfInStart = 0; +} + +void sfz::Voice::updateLoopInformation() noexcept +{ + if (!region || !currentPromise) + return; + + if (!region->shouldLoop()) + return; + + const auto factor = currentPromise->oversamplingFactor; + const auto rate = currentPromise->sampleRate; + + loop.end = static_cast(region->loopEnd(factor)); + loop.start = static_cast(region->loopStart(factor)); + loop.size = loop.end + 1 - loop.start; + loop.xfSize = static_cast( + lroundPositive(region->loopCrossfade * static_cast(factor) * rate) + ); + loop.xfOutStart = loop.end + 1 - loop.xfSize; + loop.xfInStart = loop.start - loop.xfSize; +} + void sfz::Voice::setNextSisterVoice(Voice* voice) noexcept { // Should never be null diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 95dbd9ec..1c1d2d86 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -496,6 +496,25 @@ private: int sourcePosition { 0 }; int initialDelay { 0 }; int age { 0 }; + struct { + int start { 0 }; + int end { 0 }; + int size { 0 }; + int xfSize { 0 }; + int xfOutStart { 0 }; + int xfInStart { 0 }; + } loop; + /** + * @brief Reset the loop information + * + */ + void resetLoopInformation() noexcept; + /** + * @brief Read the loop information data from the region. + * This requires that the region and promise is properly set. + * + */ + void updateLoopInformation() noexcept; FilePromisePtr currentPromise { nullptr };