In my game, I have lots of short sounds that will be event triggered. However, I don't want to load them all into memory, instead, I just want to load, play, and destroy them after they are done playing.
Is there a way to do this? Mainly, is there an event I can receive when a sample has finished playing?
I can do it via polling but that feels like a big hack.
Thanks
Not currently, no.
Which part of Allegro would fire off the audio events? The mixer, or the voice? I suppose it would have to be the mixer. It should have a list of SAMPLE_IDS that it is playing, and it should be able to detect when that sample is done playing. But when is the event fired? During the buffer update? That could lead to destroying a sample before it is finished mixing. Remember the sample that quit and post it to a queue of events to fire off before the next mixer update?
I'm sure allegro could be hacked to do this, but I haven't looked at the actual code. There's probably a mixer callback controlled by a timer in a separate thread.
I personally would like to see this happen too. No promises, but I'll try to take a look at the code to at least see how feasible this is, and what would need to be done.
Yeah, I guess it's a bit trickier the way Allegro is designed. When I coded on ios a few months ago, every AVAudioPlayer object can attach a listener (AVAudioDelegate) and you get a callback when the player finishes playing.
But that doesn't involve a mixer and causes things like global gain to be trickier to implement.
it would be nice to have something like:
al_register_sample_instance_callback((void* callback(ALLEGRO_SAMPLE_INSTANCE* spl, ALLEGRO_MIXER* mixer, bool finished));
So if the sample gets destroyed, it could dispatch the callback with finished = false. But this probably would create all kinds of problems I'm not considering.
I think the magic happens in MAKE_MIXER, specifically in fix_looped_position.
In the case of ALLEGRO_PLAYMODE_ONCE fix_looped_position returns false. This could be used as a signal by MAKE_MIXER to fire a sample over event. You would also have to account for ALLEGRO_PLAYMODE_LOOP and BIDIR too, but fix_looped_position returns true for those, since they haven't stopped playing yet.
I've always thought that the audio add-on needed to fire off events. But that never happened.
Well, I think it makes sense to emit events when a sample / stream is over / loops.
My problem with using events for this is that it satisfies this use case only, and no other use cases. E.g. what if you wanted to play a second sample as soon as the first ended? You can't use an event for that, as there will a gap between when the sample ends and when the event is processed.
So from my point of view, this is where you pull out the good old ALLEGRO_AUDIO_STREAM and stream the samples yourself.
EDIT: The backends do have a finite buffer that provides you some latency during which this can be done, but there's no API to configure its size... that's a failing of the current system, I think. In principle, if you didn't mind the latency and had a dedicated thread for audio processing, events might work ok.
You can't use an event for that, as there will a gap between when the sample ends and when the event is processed.
Allow firing the event some X time before it ends, allowing the handler time to be ready to cue up the next sample.
Disclaimer: I've not used Allegro 5 much.
Allow firing the event some X time before it ends, allowing the handler time to be ready to cue up the next sample.
The delay is still non-deterministic (the audio runs on a separate thread, there's no synchronization between it and the main thread). There is also no mechanism to do 'conditional playback' of a sample.
One final alternative to all this is to add a yet another audio source, that is based on callbacks. It'd be very similar to ALLEGRO_AUDIO_STREAM but would have no internal buffering or anything else of the sort.
So from my point of view, this is where you pull out the good old ALLEGRO_AUDIO_STREAM and stream the samples yourself.
+1
For my use case, having a delay from the time the sample ends to the time the event is sent is perfectly okay. My use case was strictly for memory management purposes; the case when you have 100's of samples and one of them can be played at any given time, and you want to avoid loading all 100 samples into memory. I could easily stop a prior sample before starting another, but that would not sound right.
So I'll probably instead poll and check if my samples are done and destroy them as they finish playing.
What kind of latency are we talking about here? How big is the voice buffer, and how often is it refilled?
What kind of latency are we talking about here? How big is the voice buffer, and how often is it refilled?
This is backend dependent, which is the crux of the issue here: it's unpredictable. I haven't looked if this is just an API oversight, or some backends don't have a meaningful number you can change/query.
ALLEGRO_AUDIO_STREAM
When two devs and Boobuigi recommend the same thing, you should take note.