Looping regressions with streams shorter than 2 seconds
First of all, thank you a lot for this great library! Even the high-level API has everything I was looking for. I only found two bugs when streaming files that are smaller than the two seconds taken up by the two streaming pages. A minimal example:
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include <stdio.h>
int main()
{
ma_engine engine;
ma_sound sine_intro;
ma_sound sine_loop;
ma_engine_init(NULL, &engine);
ma_sound_init_from_file(&engine, "sine.intro.flac", MA_SOUND_FLAG_STREAM, NULL, NULL, &sine_intro);
ma_sound_init_from_file(&engine, "sine.loop.flac", MA_SOUND_FLAG_STREAM, NULL, NULL, &sine_loop);
ma_data_source_set_next(ma_sound_get_data_source(&sine_intro), ma_sound_get_data_source(&sine_loop));
ma_sound_set_looping(&sine_loop, 1);
// Bug 1: `sine_intro` is followed by `sine_loop` without a gap, but
// `sine_loop` just stops after the first iteration.
// Introduced in 0929f8c8f52747edcb04fe6c0e0d7a621272990a.
// {
// ma_sound_start(&sine_intro);
// }
// Bug 2: `sine_loop` is looping, but miniaudio adds 960 samples of silence
// between the first and second iteration. Further loops do work fine and
// without any gap.
// Introduced in 5df38de8adfb38a35ba54d2660f9fcbed66350a8.
// {
// ma_sound_start(&sine_loop);
// }
printf("Press Enter to quit...");
getchar();
ma_engine_uninit(&engine);
return 0;
}
Both bugs disappear if I either remove the MA_SOUND_FLAG_STREAM flag or extend sine.loop.flac to at least two seconds.
Here are the test files in case you need them. The MA_DEBUG_OUTPUT on my system:
DEBUG: Loading library: user32.dll
DEBUG: Loading symbol: GetForegroundWindow
DEBUG: Loading symbol: GetDesktopWindow
DEBUG: Loading library: advapi32.dll
DEBUG: Loading symbol: RegOpenKeyExA
DEBUG: Loading symbol: RegCloseKey
DEBUG: Loading symbol: RegQueryValueExA
DEBUG: Loading library: ole32.dll
DEBUG: Loading symbol: CoInitialize
DEBUG: Loading symbol: CoInitializeEx
DEBUG: Loading symbol: CoUninitialize
DEBUG: Loading symbol: CoCreateInstance
DEBUG: Loading symbol: CoTaskMemFree
DEBUG: Loading symbol: PropVariantClear
DEBUG: Loading symbol: StringFromGUID2
DEBUG: Attempting to initialize WASAPI backend...
DEBUG: Loading library: kernel32.dll
DEBUG: Loading symbol: VerifyVersionInfoW
DEBUG: Loading symbol: VerSetConditionMask
DEBUG: Loading library: avrt.dll
DEBUG: Loading symbol: AvSetMmThreadCharacteristicsA
DEBUG: Loading symbol: AvRevertMmThreadCharacteristics
DEBUG: System Architecture:
DEBUG: Endian: LE
DEBUG: SSE2: YES
DEBUG: AVX2: YES
DEBUG: NEON: NO
DEBUG: [WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=480)
DEBUG: defaultPeriodInFrames=480
DEBUG: fundamentalPeriodInFrames=480
DEBUG: minPeriodInFrames=480
DEBUG: maxPeriodInFrames=480
DEBUG: [WASAPI] Using IAudioClient3
DEBUG: periodSizeInFramesOut=480
INFO: [WASAPI]
INFO: Speakers (USB Multi-Channel Audio Device) (Playback)
INFO: Format: 32-bit IEEE Floating Point -> 32-bit IEEE Floating Point
INFO: Channels: 2 -> 2
INFO: Sample Rate: 48000 -> 48000
INFO: Buffer Size: 480*3 (1440)
INFO: Conversion:
INFO: Pre Format Conversion: NO
INFO: Post Format Conversion: NO
INFO: Channel Routing: NO
INFO: Resampling: NO
INFO: Passthrough: YES
INFO: Channel Map In: {CHANNEL_FRONT_LEFT CHANNEL_FRONT_RIGHT}
INFO: Channel Map Out: {CHANNEL_FRONT_LEFT CHANNEL_FRONT_RIGHT}
Thanks for the report and test cases. I'll have a look at this as soon as I get a chance.
I've finally got around to having a look at this. The commits you mentioned in your original comments are so old they're not really relevant. The issue is that when you initialize a stream, it pre-fills the pages with data, but since it doesn't yet realise that you want to loop, it just stops filling the buffer once it reaches the end.
The way to enable looping on streams is to specify it at initialization time. There is a ma_sound_init_ex() function which initializes from a ma_sound_config (the same config/init pattern you see everywhere in miniaudio). In that config there is a isLooping variable - that was the way it should be done. However, in the dev branch just now I've deprecated that and replaced it with a flag so it can be used easily with ma_sound_init_from_file(). So in your sample code, both of these issues should be fixed if you were to change the loading of your second sound to this:
ma_sound_init_from_file(&engine, "sine.loop.flac", MA_SOUND_FLAG_STREAM | MA_SOUND_FLAG_LOOPING, NULL, NULL, &sine_loop);
With that change, you don't need to explicitly call ma_sound_set_looping(&sine_loop, 1)
So this has essentially become a will not fix which I realise isn't the most satisfying outcome, but considering the use of streams with such a small sound is so uncommon, I don't think it's worth complicating the implementation by doing whatever would be needed to get ma_sound_set_looping(&sine_loop, 1) working cleanly for such a specific scenario.