From 00b7f8ccb8325375ff0780b6f7bce5a22cddbf42 Mon Sep 17 00:00:00 2001 From: Pavel Sountsov Date: Tue, 20 Feb 2024 22:55:04 -0800 Subject: [PATCH] Prefill ALLEGRO_AUDIO_STREAMs with data in docstrings/examples/acodec. The audio system will attempt to grab data from a stream *before* it has sent any events that it needs fragments. This causes a bit of lag, and also log spam as the system complains about having no data. This change adjusts most places to prefill the streams with data to avoid this issue. It should be safe, and in fact beneficial as we should get a few less sample-worths of lag when using `al_play/load_audio_stream`. --- addons/audio/kcm_stream.c | 7 +++-- docs/src/refman/audio.txt | 14 +++++++++ examples/ex_saw.c | 7 +++++ examples/ex_synth.cpp | 62 ++++++++++++++++----------------------- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/addons/audio/kcm_stream.c b/addons/audio/kcm_stream.c index b17a6670ad..8d784fa9ad 100644 --- a/addons/audio/kcm_stream.c +++ b/addons/audio/kcm_stream.c @@ -684,6 +684,7 @@ void *_al_kcm_feed_stream(ALLEGRO_THREAD *self, void *vstream) ALLEGRO_AUDIO_STREAM *stream = vstream; ALLEGRO_EVENT_QUEUE *queue; bool finished_event_sent = false; + bool prefill = true; (void)self; ALLEGRO_DEBUG("Stream feeder thread started.\n"); @@ -702,10 +703,12 @@ void *_al_kcm_feed_stream(ALLEGRO_THREAD *self, void *vstream) char *fragment; ALLEGRO_EVENT event; - al_wait_for_event(queue, &event); + if (!prefill) + al_wait_for_event(queue, &event); - if (event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT + if ((prefill || event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) && !stream->is_draining) { + prefill = false; unsigned long bytes; unsigned long bytes_written; ALLEGRO_MUTEX *stream_mutex; diff --git a/docs/src/refman/audio.txt b/docs/src/refman/audio.txt index 2667ffb025..abce66b52b 100644 --- a/docs/src/refman/audio.txt +++ b/docs/src/refman/audio.txt @@ -735,6 +735,20 @@ If you're late with supplying new data, the stream will be silent until new data is provided. You must call [al_drain_audio_stream] when you're finished with supplying data to the stream. +It is often a good idea to prefill the audio stream with data before +[ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT] events arrive. Here is a snippet that will +fill the stream buffers with silence: + +~~~~c +ALLEGRO_AUDIO_STREAM *stream = al_create_audio_stream(num_buffers, + samples_per_buffer, freq, depth, channel_conf); +void *buf; +while ((buf = al_get_audio_stream_fragment(stream))) { + al_fill_silence(buf, samples_per_buffer, depth, channel_conf); + al_set_audio_stream_fragment(stream, buf); +} +~~~~ + If the stream is created by [al_load_audio_stream] or [al_play_audio_stream] then it will also generate an [ALLEGRO_EVENT_AUDIO_STREAM_FINISHED] event if it reaches the end of the file and is not set to loop. diff --git a/examples/ex_saw.c b/examples/ex_saw.c index b35700e81b..1a2d21b496 100644 --- a/examples/ex_saw.c +++ b/examples/ex_saw.c @@ -83,6 +83,7 @@ static void saw(ALLEGRO_AUDIO_STREAM *stream) int main(int argc, char **argv) { ALLEGRO_AUDIO_STREAM *stream; + void *buf; (void)argc; (void)argv; @@ -98,6 +99,12 @@ int main(int argc, char **argv) stream = al_create_audio_stream(8, SAMPLES_PER_BUFFER, 22050, ALLEGRO_AUDIO_DEPTH_UINT8, ALLEGRO_CHANNEL_CONF_1); + while ((buf = al_get_audio_stream_fragment(stream))) { + al_fill_silence(buf, SAMPLES_PER_BUFFER, ALLEGRO_AUDIO_DEPTH_UINT8, + ALLEGRO_CHANNEL_CONF_1); + al_set_audio_stream_fragment(stream, buf); + } + if (!stream) { abort_example("Could not create stream.\n"); } diff --git a/examples/ex_synth.cpp b/examples/ex_synth.cpp index 3ada24d4ad..c058442c3b 100644 --- a/examples/ex_synth.cpp +++ b/examples/ex_synth.cpp @@ -48,11 +48,7 @@ static void sawtooth(float *buf, size_t samples, double t, /* globals */ ALLEGRO_FONT *font_gui; -ALLEGRO_AUDIO_STREAM *stream1; -ALLEGRO_AUDIO_STREAM *stream2; -ALLEGRO_AUDIO_STREAM *stream3; -ALLEGRO_AUDIO_STREAM *stream4; -ALLEGRO_AUDIO_STREAM *stream5; +ALLEGRO_AUDIO_STREAM *streams[5]; bool saving = false; ALLEGRO_FILE *save_fp = NULL; @@ -358,11 +354,8 @@ void Prog::run() { d.prepare(); - d.register_event_source(al_get_audio_stream_event_source(stream1)); - d.register_event_source(al_get_audio_stream_event_source(stream2)); - d.register_event_source(al_get_audio_stream_event_source(stream3)); - d.register_event_source(al_get_audio_stream_event_source(stream4)); - d.register_event_source(al_get_audio_stream_event_source(stream5)); + for (int i = 0; i < 5; i++) + d.register_event_source(al_get_audio_stream_event_source(streams[i])); d.set_event_handler(this); while (!d.is_quit_requested()) { @@ -399,15 +392,15 @@ void Prog::handle_event(const ALLEGRO_EVENT & event) return; } - if (stream == stream1) + if (stream == streams[0]) group = &group1; - else if (stream == stream2) + else if (stream == streams[1]) group = &group2; - else if (stream == stream3) + else if (stream == streams[2]) group = &group3; - else if (stream == stream4) + else if (stream == streams[3]) group = &group4; - else if (stream == stream5) + else if (stream == streams[4]) group = &group5; else group = NULL; @@ -474,27 +467,24 @@ int main(int argc, char *argv[]) size_t buffers = 8; unsigned samples = SAMPLES_PER_BUFFER; unsigned freq = STREAM_FREQUENCY; + void *buf; ALLEGRO_AUDIO_DEPTH depth = ALLEGRO_AUDIO_DEPTH_FLOAT32; ALLEGRO_CHANNEL_CONF ch = ALLEGRO_CHANNEL_CONF_1; + ALLEGRO_MIXER *mixer = al_get_default_mixer(); - stream1 = al_create_audio_stream(buffers, samples, freq, depth, ch); - stream2 = al_create_audio_stream(buffers, samples, freq, depth, ch); - stream3 = al_create_audio_stream(buffers, samples, freq, depth, ch); - stream4 = al_create_audio_stream(buffers, samples, freq, depth, ch); - stream5 = al_create_audio_stream(buffers, samples, freq, depth, ch); - if (!stream1 || !stream2 || !stream3 || !stream4 || !stream5) { - abort_example("Could not create stream.\n"); - } + for (int i = 0; i < 5; i++) { + streams[i] = al_create_audio_stream(buffers, samples, freq, depth, ch); + if (!streams[i]) { + abort_example("Could not create stream.\n"); + } + while ((buf = al_get_audio_stream_fragment(streams[i]))) { + al_fill_silence(buf, samples, depth, ch); + al_set_audio_stream_fragment(streams[i], buf); + } - ALLEGRO_MIXER *mixer = al_get_default_mixer(); - if ( - !al_attach_audio_stream_to_mixer(stream1, mixer) || - !al_attach_audio_stream_to_mixer(stream2, mixer) || - !al_attach_audio_stream_to_mixer(stream3, mixer) || - !al_attach_audio_stream_to_mixer(stream4, mixer) || - !al_attach_audio_stream_to_mixer(stream5, mixer) - ) { - abort_example("Could not attach stream to mixer.\n"); + if (!al_attach_audio_stream_to_mixer(streams[i], mixer)) { + abort_example("Could not attach stream to mixer.\n"); + } } al_set_mixer_postprocess_callback(mixer, mixer_pp_callback, mixer); @@ -506,11 +496,9 @@ int main(int argc, char *argv[]) prog.run(); } - al_destroy_audio_stream(stream1); - al_destroy_audio_stream(stream2); - al_destroy_audio_stream(stream3); - al_destroy_audio_stream(stream4); - al_destroy_audio_stream(stream5); + for (int i = 0; i < 5; i++) { + al_destroy_audio_stream(streams[i]); + } al_uninstall_audio(); al_destroy_font(font_gui);