diff --git a/src/dusk/audio/audiostream.c b/src/dusk/audio/audiostream.c index d3b281a0..d1bbc858 100644 --- a/src/dusk/audio/audiostream.c +++ b/src/dusk/audio/audiostream.c @@ -68,6 +68,16 @@ void audioStreamSetDirectionality( // TODO: Need to update internal decoder? } +void audioStreamSetLooping(audiostream_t *stream, const bool_t looping) { + assertNotNull(stream, "Stream cannot be NULL."); + + if(looping) { + stream->state |= AUDIO_STREAM_STATE_LOOPING; + } else { + stream->state &= ~AUDIO_STREAM_STATE_LOOPING; + } +} + errorret_t audioStreamUpdate(audiostream_t *stream) { assertNotNull(stream, "Stream cannot be NULL."); @@ -77,7 +87,10 @@ errorret_t audioStreamUpdate(audiostream_t *stream) { // TODO: Once streamed (rather than fully in-memory) sources exist, this is // where new data would be decoded/read in and re-buffered as playback - // consumes it, and looping (onLoop) would be handled. + // consumes it. Looping currently only supports restarting from the very + // start of the buffer (loopTo is not yet honored) since replaying the + // exact same stream->data/dataSize needs no platform-specific changes; + // an arbitrary loopTo offset would need slicing that buffer instead. if( (stream->state & AUDIO_STREAM_STATE_PLAYING) && !(stream->state & AUDIO_STREAM_STATE_BUFFERED) @@ -89,10 +102,18 @@ errorret_t audioStreamUpdate(audiostream_t *stream) { (stream->state & AUDIO_STREAM_STATE_BUFFERED) && audioStreamPlatformIsFinished(stream) ) { - stream->state &= ~(AUDIO_STREAM_STATE_PLAYING | AUDIO_STREAM_STATE_BUFFERED); + if(stream->state & AUDIO_STREAM_STATE_LOOPING) { + stream->state &= ~AUDIO_STREAM_STATE_BUFFERED; - if(stream->onEnd != NULL) { - stream->onEnd(stream); + if(stream->onLoop != NULL) { + stream->onLoop(stream); + } + } else { + stream->state &= ~(AUDIO_STREAM_STATE_PLAYING | AUDIO_STREAM_STATE_BUFFERED); + + if(stream->onEnd != NULL) { + stream->onEnd(stream); + } } } diff --git a/src/dusk/audio/audiostream.h b/src/dusk/audio/audiostream.h index 29cd865c..67173516 100644 --- a/src/dusk/audio/audiostream.h +++ b/src/dusk/audio/audiostream.h @@ -141,6 +141,17 @@ void audioStreamSetDirectionality( const int8_t directionality ); +/** + * Sets whether the given audio stream loops back to the start when it + * reaches the end, rather than stopping and firing onEnd. Only looping + * back to the very start of the buffer is currently supported - loopTo is + * not yet honored. + * + * @param stream The audio stream to update. + * @param looping Whether the stream should loop. + */ +void audioStreamSetLooping(audiostream_t *stream, const bool_t looping); + /** * Updates the given audio stream, decoding new data and advancing playback * as needed. Should be called every frame for every active stream. diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index ac43d89c..3b7f9b69 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -30,7 +30,10 @@ engine_t ENGINE; // 44100Hz: PSP's sceAudioChReserve hardware channels are fixed at this rate. #define AUDIO_TEST_TONE_SAMPLE_RATE 44100 -#define AUDIO_TEST_TONE_FREQUENCY 440 +// 441Hz (not 440): divides 44100Hz evenly into exactly 100 samples/cycle, +// so a whole-second buffer's last sample exactly matches its first - +// a seamless loop point with no source-data discontinuity to click on. +#define AUDIO_TEST_TONE_FREQUENCY 441 #define AUDIO_TEST_TONE_SAMPLES AUDIO_TEST_TONE_SAMPLE_RATE #define AUDIO_TEST_TONE_SIZE (AUDIO_TEST_TONE_SAMPLES * sizeof(int16_t)) @@ -41,6 +44,10 @@ void engineTestToneOnEnd(audiostream_t *stream) { consolePrint("Test tone finished playing"); } +void engineTestToneOnLoop(audiostream_t *stream) { + consolePrint("Test tone looped"); +} + errorret_t engineInit(const int32_t argc, const char_t **argv) { assertInit(); memoryZero(&ENGINE, sizeof(engine_t)); @@ -81,6 +88,8 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { AUDIO_TEST_TONE_SAMPLE_RATE, 1 )); stream->onEnd = engineTestToneOnEnd; + stream->onLoop = engineTestToneOnLoop; + audioStreamSetLooping(stream, true); audioStreamPlay(stream); consolePrint("Engine initialized");