Skip to content

Commit 2b7f9fe

Browse files
tucktuckg00seclaude
andcommitted
fix: eliminate Bungee loop buzzing with unbounded phase model
Remove post-grain position canonicalization that jumped bungeeSrcPos back into loop range, disrupting Bungee's overlap-add continuity. Position now grows unbounded; readExactLoopSample maps grain reads through the loop at read time. Ping-pong flips speed only on actual boundary crossings. UI cursor wraps position for display only. Also make reseekStretcher loop-aware for startup consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent deef693 commit 2b7f9fe

1 file changed

Lines changed: 21 additions & 29 deletions

File tree

src/audio/VoicePool.cpp

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -676,17 +676,15 @@ static void reseekStretcher (Voice& v, const SampleData& sample)
676676
seekLen = std::min (seekLen, v.endSample - v.startSample);
677677
seekLen = juce::jlimit (0, (int) v.stretchInBufL.size(), seekLen);
678678

679-
int maxFrame = sample.getNumFrames() - 1;
680-
if (seekLen > 0 && maxFrame >= 0)
679+
if (seekLen > 0 && sample.getNumFrames() > 0)
681680
{
682681
for (int i = 0; i < seekLen; ++i)
683682
{
684-
int srcIdx = (v.direction > 0)
685-
? (int) v.stretchSrcPos + i
686-
: (int) v.stretchSrcPos - i;
687-
srcIdx = juce::jlimit (0, maxFrame, srcIdx);
688-
v.stretchInBufL[(size_t) i] = sample.getInterpolatedSample (srcIdx, 0);
689-
v.stretchInBufR[(size_t) i] = sample.getInterpolatedSample (srcIdx, 1);
683+
double srcPos = (v.direction > 0)
684+
? v.stretchSrcPos + i
685+
: v.stretchSrcPos - i;
686+
v.stretchInBufL[(size_t) i] = readExactLoopSample (v, sample, srcPos, 0);
687+
v.stretchInBufR[(size_t) i] = readExactLoopSample (v, sample, srcPos, 1);
690688
}
691689
float* ptrs[2] = { v.stretchInBufL.data(), v.stretchInBufR.data() };
692690
v.stretcher->outputSeek (ptrs, seekLen);
@@ -871,28 +869,16 @@ static void fillBungeeBlock (Voice& v, const SampleData& sample)
871869
// Advance source position from the engine
872870
v.bungeeSrcPos = request.position;
873871

874-
// Canonicalize position back into loop range for next grain
872+
// Unbounded phase: do NOT canonicalize bungeeSrcPos back into loop range.
873+
// readExactLoopSample() maps grain read positions through the loop at read time.
874+
// This keeps Bungee's overlap-add state coherent across loop boundaries.
875875
if (v.pingPong)
876876
{
877-
const double lo = (double) v.startSample;
878-
const double hi = (double) (v.endSample - 1);
879-
while (v.bungeeSrcPos > hi || v.bungeeSrcPos < lo)
880-
{
881-
if (v.bungeeSrcPos > hi)
882-
{
883-
v.bungeeSrcPos = 2.0 * hi - v.bungeeSrcPos;
884-
v.bungeeSpeed = -v.bungeeSpeed;
885-
}
886-
if (v.bungeeSrcPos < lo)
887-
{
888-
v.bungeeSrcPos = 2.0 * lo - v.bungeeSrcPos;
889-
v.bungeeSpeed = -v.bungeeSpeed;
890-
}
891-
}
892-
}
893-
else if (v.looping)
894-
{
895-
v.bungeeSrcPos = wrapLoopPosition (v.bungeeSrcPos, v.startSample, v.endSample);
877+
// Flip speed only when crossing a boundary in the current travel direction
878+
if (v.bungeeSpeed > 0.0 && v.bungeeSrcPos >= (double) v.endSample)
879+
v.bungeeSpeed = -std::abs (v.bungeeSpeed);
880+
else if (v.bungeeSpeed < 0.0 && v.bungeeSrcPos < (double) v.startSample)
881+
v.bungeeSpeed = std::abs (v.bungeeSpeed);
896882
}
897883
}
898884

@@ -1019,7 +1005,13 @@ void VoicePool::processVoiceSample (int i, const SampleData& sample, double /*sr
10191005
v.bungeeOutReadPos++;
10201006
}
10211007

1022-
voicePositions[i].store ((float) v.bungeeSrcPos, std::memory_order_relaxed);
1008+
// Wrap unbounded Bungee phase for UI cursor display only
1009+
if (v.looping)
1010+
voicePositions[i].store ((float) wrapLoopPosition (v.bungeeSrcPos, v.startSample, v.endSample), std::memory_order_relaxed);
1011+
else if (v.pingPong)
1012+
voicePositions[i].store ((float) reflectPingPongPosition (v.bungeeSrcPos, v.startSample, v.endSample), std::memory_order_relaxed);
1013+
else
1014+
voicePositions[i].store ((float) v.bungeeSrcPos, std::memory_order_relaxed);
10231015
}
10241016
else
10251017
{

0 commit comments

Comments
 (0)