We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I'm putting together an application which plays a series of sound files in sequence; I want to be able to tell when a file has finished.
The obvious ways of doing this (checking for the currentTime() >= duration() and checking for !.isPlaying() && !.isPaused()) don't work - when a sound file finishes playing, currentTime() returns 0, and the second statement is also true when there's nothing playing at all.
I can probably sort this by tracking the currentTime and watching for a jump down from a non-zero value, but this seems like a hacky way of doing things. Have I missed something obvious or is there a feature that isn't documented that would solve my problem?
Relatedly, it seems like it would be useful for the soundFile to raise some sort of event when it finishes playing, or alternatively for its currentTime to remain equal to its duration rather than resetting to zero.
Answers
@dhmstark===
You should request some kinda "finishedPlayback" callback capability here:
https://GitHub.com/processing/p5.js-sound/issues
@dhmstark - having tested a solution locally I went ahead and added an issue.
I didn't figure it out in detail; but it looks to me like the sound library checks the length of the audio against how much has played internally to determine when it has finished; probably because media event browser support appears patchy at present...
As far as I could see adding an optional callback should be trivial; though not knowing the inner complexities of the library I'll leave it to the authors to confirm ;)
Thanks for the comments and in particular to @blindfish for adding the issue! I'm never sure how to go about these things so it's really appreciated. :)
I'll go with my hacky solution for now as at the very least it's working, but will keep an eye on that issue to see if it gets anywhere.
@dhmstark: The issue has been closed and an onended callback has been implemented :)
Get the latest p5.js-sound. Add an onended callback to your sound file like so:
I wish they'd do the same for Processing's sound & video libraries too. [-O<
It'd be cool if "p5.sound.js" had chainable methods too... :-\"
Like:
sound.play().onended(myCallback);
?That's a pattern heavily used in jQuery. I've read criticisms claiming chaining can make it harder to debug your code; but it definitely makes it easier to read.
The definite downside with adding sound.onended in setup, as I did in my example, is that you're going to have to look in two separate places (or three if you define a separate callback function instead of using an anonymous function) to figure out the result of playing a sound. Worse: you might forget that you attached the onended event and be wondering why something unexpected always happens after you play a sound...
On reflection I should have made that point when therewasaguy suggested the separate method. Adding the callback as an argument to sound.play() makes more sense to me; but might have meant more work in terms of implementation since it would need careful thought around argument overloading...
Looking at p5.js eco-system, in general only loading functions got callbacks as parameters.
Other types of callbacks are plugged in as optional later.
Since play() isn't a loader, onended() ended up as a plugin callback event for the p5.sound object. :-B
Works wonderfully, thank you!
For reference, if anyone cares, this is the result of what I was working on - just a prototype right now, but I'm using p5.sound to handle the background music which should now play a continuous loop of random loaded tracks. The callback is basically used to pick the next one, which is much simpler than my previous method. :)
Only one issue that I've run into - it seems like the callback is also called on pause, as well as on end. Is this intentional? Easy enough for me to work around but it seemed slightly unintuitive.
Provide feedback - either as a comment on the existing issue or raise a new one. The feature was implemented quickly (a good thing) so such issues may not have been considered (not so good).
A solution might be for the callback function to be passed the name of the event that fired it: you'd then be able to use this directly to determine how to respond. So in pseudocode:
Alternatively you should be able to specify which events fire the callback; though I suspect that would be more complex in terms of implementation.
I haven't tested the onended solution; but another possible issue I wondered about was whether sound files that have been explicitly set to loop would fire the callback at each repetition... That again may not be desirable/expected.
IMO, onended() should only fire when a file reaches its end naturally. Not when paused or stopped.
For loop() I still think it should fire so we know when it has restarted.
Should definitely ask the onended()'s implementator to make these adjustments.
And btW, fantastic rain cloud music animation! :-bd
Fair comment; but it would then make sense to add onPaused and onStopped events too. And instead of calling onended when audio loops, instead have an onLoop event... Looking at a couple of existing JS audio libraries (SoundJS and howler.js they both allow listening for multiple event types; but take subtly different approaches in their implementation; neither of which involve sending a flag to the callback function...
I'm trying to use onended() but I might be misunderstanding something here. I thought onended() executed the callback only after the sound has been played AND ended. Yet, if I simply load a sound in preload and then call onended() on it, without ever playing it, it does execute the callback.
Example: `function preload() { soundOne = loadSound('audio/later.mp3'); }
I'm trying to accomplish the same as the OP: play several files in sequence, but am probably missing something on how to implement onended() here.
undefined
to onended() here:soundOne.onended(ended());
:-\"return something;
always returnundefined
.undefined
when it's invoked. b-(instead of invoking the former w/
()
:soundOne.onended(ended);
*-:)Wow, that was a great explanation @GoToLoop! It works (and I finally got it how it works). Thanks!