Loop control
Basically, for video game audio, there are countless times when you need to have loop points set other than the beginning and ending of the track. For example a song that has an intro that should not loop, but the main body of the song does loop.
I was able to implement this behavior with minimal bloat to the library. Not sure if you want to include it... We had discussed this in https://github.com/goldfire/howler.js/issues/1086
How is this different than just defining it as a sprite?
As far as I understand, there was no way to define loop points within a sprite? There is only a boolean flag for looping the entire thing.
My example use case is music that has an intro but loop the body of the song (never replaying the intro).
Found that in the doc:
sprite
Object{}Define a sound sprite for the sound. The offset and duration are defined in milliseconds. A third (optional) parameter is available to set a sprite as looping. An easy way to generate compatible sound sprites is with audiosprite.
{ key: [offset, duration, (loop)] }
You could do something like that, no ?
sprite: {
intro: [0, 10],
main: [10, 100, true],
outro: [110, 10]
}
I see, that is possible--- but how is the best way to play "main" after "intro" is complete?
The only way I could figure out is to something incredibly hacky by storing off the id in a variable and looking at it in the onend function. The sprite's name really should be provided to these callback functions. Also, the fact that playing a "song" ultimately ends up generating two different IDs means that it is substantially more work to stop a song because you need to know the intro ID AND the main ID in order to stop the track during either the intro or the looping section...
var introId;
var song = new Howl({
src: ['audio/output.ogg', 'audio/output.wav'],
sprite: {
intro: [0, 1000],
main: [1000, 1000, true],
},
onend: function(id) {
if (id === introId) {
song.play('main');
}
},
})
introId = song.play('intro');
although I suppose I could forgo the loop parameter and then I can avoid the if condition in the onend... But it seems like there should be a much better way to do this... Any suggestions?
Actually I was able to get the behavior I want with this, though it is still incredibly hacky to have to dig in and mutate _onend... But, I am at least able to do what I need in a somewhat generic way. If there is a better way to do this, I'd love to know what it is.
playSong: function(song) {
var currentSong = this.library.music[song];
if (currentSong._sprite.intro) {
currentSong.play('intro');
currentSong._onend = [
{ fn:
function(id) {
currentSong.play('main');
currentSong._onend = [];
}
}
];
} else {
currentSong.play('main');
}
this.currentSong = currentSong;
},
stopCurrentSong: function() {
this.currentSong.stop();
},
library: {
music: {
level1: new Howl({
src: ['audio/output.ogg', 'audio/output.wav'],
sprite: {
intro: [0, 2000],
main: [2000, 1000, true],
},
})
},
}
After testing this technique out with several audio tracks, it appears this method using soundsprites is unsatisfactory as there is always an audible "blip" when the onend callback happens to start playing the main body.
The version that I did with allowing Howler to use loop control points does not have this problem, and the looping is always clean and flawless.
@patrick99e99 web audio has an api to schedule playback when exact timing is crucial. I do not think you will end up with satisfactory results if you just try to hit play when you are told that previous playback has ended. Depending on your actual use case, you might probably be better off to just implement your playback yourself (however I am not affiliated with howler in any way). You should be able to find quite some information on how to do that on the web. For instance this one: A Tale of Two Clocks - Scheduling Web Audio with Precision It's a few years old, but it does not look like much has changed since then. Pretty much during your last loop iteration you would need to schedule playback of the ending ahead of time. If you just do it when your being told that playback has ended, you will very often just be too late.
@st-h well that's the point of this pull request. I modified the howler code so that loop points can be precisely specified by exact sample numbers and works perfectly. It's up to whether Howler devs wants to support this functionality.
@patrick99e99 my apologies. I must have overlooked that part when reading through your PR.
I'd like this function, in order to shorten samples, to make their loops tighter on the fly! Could anyone help with that? I need an end time, where the loop goes back to the start at that point.
You can use my loop control branch since I don't think the maintainers of howler are interested in supporting this.
Given that this is still open, I'd like to +1 this feature. I think it's very useful and gives more control on looping. I don't think it introduces too much complexity to the library, and, most importantly, I don't think it's out of the scope of the library.
@patrick99e99 How did you solve your needs in the long run? Did you stay with Howler + your custom code?
@Racso, I did stick w/ the changes on my branch for my project. In the future however, I would just write my own player with the webaudio api and not rely on 3rd party libraries for various reasons.