@@ -27,6 +27,10 @@ public class AudioSample extends SoundObject {
27
27
// position within the sample
28
28
protected long startFrameCountOffset = 0 ;
29
29
30
+ // helper variable for making isPlaying() return true for the fraction of a
31
+ // second after play() was called but audio data hasn't been queued yet
32
+ private long isPlayingAtLeastUntil = 0 ;
33
+
30
34
public AudioSample (PApplet parent , int frames ) {
31
35
this (parent , frames , false );
32
36
}
@@ -133,13 +137,20 @@ protected void initiatePlayer() {
133
137
* @Override
134
138
**/
135
139
public boolean isPlaying () {
136
- // this used to just return dataQueue.hasMore(), but this raises false
137
- // positives as .hasMore() still returns true for a split second after
138
- // dataQueue.clear() has been called. the AudioSample class therefore
139
- // manually sets the isPlaying boolean to true when playback commences,
140
- // but every time the user queries the playing status we check if the
141
- // queue has actually run out in the meantime.
142
- this .isPlaying = this .isPlaying & this .player .dataQueue .hasMore ();
140
+ // relying on dataQueue.hasMore() alone is problematic because it can
141
+ // raise both false positives (return true for a split second after
142
+ // dataQueue.clear() has been called) as well as false negatives
143
+ // (return false for a split second after audio data has been queued
144
+ // but hasn't been transferred into the queue yet). The AudioSample class
145
+ // therefore manually sets the isPlaying boolean to true when playback
146
+ // commences and false when it is stopped, but also every time the
147
+ // playing status is queried we update the boolean based on additional
148
+ // checks:
149
+ if (this .isPlaying ) {
150
+ // set isPlaying to false if the queue is empty, UNLESS too little
151
+ // time has passed since the last playback command
152
+ this .isPlaying = this .player .dataQueue .hasMore () || System .currentTimeMillis () < this .isPlayingAtLeastUntil ;
153
+ }
143
154
return this .isPlaying ;
144
155
}
145
156
@@ -315,6 +326,7 @@ private void loopInternal(int startFrame, int numFrames, int numLoops) {
315
326
} else {
316
327
this .player .dataQueue .queueLoop (this .sample , startFrame , numFrames );
317
328
}
329
+ this .isPlayingAtLeastUntil = System .currentTimeMillis () + 50 ;
318
330
this .isPlaying = true ;
319
331
}
320
332
@@ -401,6 +413,7 @@ private void playInternal(int startFrame, int numFrames) {
401
413
this .setStartFrameCountOffset ();
402
414
// only queueImmediate() guarantees that a directly subsequent call to .hasMore() returns true
403
415
this .player .dataQueue .queue (this .sample , startFrame , numFrames );
416
+ this .isPlayingAtLeastUntil = System .currentTimeMillis () + 50 ;
404
417
this .isPlaying = true ;
405
418
}
406
419
0 commit comments