Skip to content

Commit deef693

Browse files
tucktuckg00seclaude
andcommitted
fix: correct ping-pong direction tracking for stretch engines
Replace ambiguous position-based direction inference in reflectPingPongPosition with explicit boundary checks. Signalsmith advanceStretchSrcPos now flips direction only on actual boundary crossings. Bungee post-grain canonicalization uses iterative reflection preserving speed sign. Simplify reflectPingPongPosition to position-only mapping since direction output is no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a25a0f0 commit deef693

1 file changed

Lines changed: 28 additions & 24 deletions

File tree

src/audio/VoicePool.cpp

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,43 +91,29 @@ static double wrapLoopPosition (double pos, int start, int end)
9191
}
9292

9393
// Reflects pos through a ping-pong cycle over [start, end-1].
94-
// Returns the reflected position and the effective direction at that phase.
94+
// Returns the reflected position only (stateless position mapping).
9595
// Does not duplicate the turnaround sample.
96-
static double reflectPingPongPosition (double pos, int start, int end, int& directionOut)
96+
static double reflectPingPongPosition (double pos, int start, int end)
9797
{
9898
const double lo = (double) start;
9999
const double hi = (double) (end - 1);
100100
const double span = hi - lo;
101101
if (span <= 0.0)
102-
{
103-
directionOut = 1;
104102
return lo;
105-
}
106103

107104
double t = pos - lo;
108-
// Track whether we've inverted direction an odd number of times
109-
bool flipped = false;
110105
if (t < 0.0)
111-
{
112106
t = -t;
113-
flipped = !flipped;
114-
}
115107

116108
const double fullCycle = 2.0 * span;
117109
t = std::fmod (t, fullCycle);
118110
if (t < 0.0)
119111
t += fullCycle;
120112

121113
if (t <= span)
122-
{
123-
directionOut = flipped ? -1 : 1;
124114
return lo + t;
125-
}
126115
else
127-
{
128-
directionOut = flipped ? 1 : -1;
129116
return lo + (fullCycle - t);
130-
}
131117
}
132118

133119
// Reads a sample from the virtual looped source at an arbitrary position.
@@ -142,8 +128,7 @@ static float readExactLoopSample (const Voice& v, const SampleData& sample,
142128

143129
if (v.pingPong)
144130
{
145-
int unusedDir = 1;
146-
double mapped = reflectPingPongPosition (pos, v.startSample, v.endSample, unusedDir);
131+
double mapped = reflectPingPongPosition (pos, v.startSample, v.endSample);
147132
return sample.getInterpolatedSample (juce::jlimit (0.0, (double) maxFrame, mapped), channel);
148133
}
149134

@@ -164,9 +149,16 @@ static VoiceBoundaryAction advanceStretchSrcPos (Voice& v)
164149

165150
if (v.pingPong)
166151
{
167-
int newDir = v.direction;
168-
v.stretchSrcPos = reflectPingPongPosition (v.stretchSrcPos, v.startSample, v.endSample, newDir);
169-
v.direction = newDir;
152+
if (v.stretchSrcPos >= v.endSample)
153+
{
154+
v.stretchSrcPos = 2.0 * (v.endSample - 1) - v.stretchSrcPos;
155+
v.direction = -1;
156+
}
157+
else if (v.stretchSrcPos < v.startSample)
158+
{
159+
v.stretchSrcPos = 2.0 * v.startSample - v.stretchSrcPos;
160+
v.direction = 1;
161+
}
170162
return VoiceBoundaryAction::continuePlayback;
171163
}
172164

@@ -882,9 +874,21 @@ static void fillBungeeBlock (Voice& v, const SampleData& sample)
882874
// Canonicalize position back into loop range for next grain
883875
if (v.pingPong)
884876
{
885-
int newDir = 1;
886-
v.bungeeSrcPos = reflectPingPongPosition (v.bungeeSrcPos, v.startSample, v.endSample, newDir);
887-
v.bungeeSpeed = std::abs (v.bungeeSpeed) * (double) newDir;
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+
}
888892
}
889893
else if (v.looping)
890894
{

0 commit comments

Comments
 (0)