@@ -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