When seeking an sf::Music object to certain playback offsets, it will not loop at the end despite setLoop having been called on it.
The following example reproduces this issue on Linux Mint 18.3 (the example requires testsound.ogg, found in the attachments, to be present in the working directory, though any stereo Ogg sound should work):
#include <SFML/Audio/Music.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
std::vector<char> readFile(std::string fileName)
{
std::ifstream f(fileName);
std::vector<char> data;
f.seekg(0, std::ios::end);
data.resize(f.tellg());
f.seekg(0, std::ios::beg);
f.read(data.data(), data.size());
return data;
}
std::unique_ptr<sf::Music> play(const std::vector<char> & buffer, sf::Time offset)
{
std::unique_ptr<sf::Music> music = std::make_unique<sf::Music>();
music->openFromMemory(buffer.data(), buffer.size());
music->setLoop(true);
music->setVolume(20);
music->setPitch(1);
music->play();
if (offset != sf::Time::Zero)
{
music->setPlayingOffset(offset);
std::cout << "Playing at " << offset.asMicroseconds() << " us" << std::endl;
}
else
{
std::cout << "Playing from start" << std::endl;
}
return std::move(music);
}
int main()
{
auto buffer = readFile("testsound.ogg");
std::unique_ptr<sf::Music> music;
music = play(buffer, sf::Time::Zero); // Playing from the start: sound loops correctly
sf::sleep(sf::seconds(5));
music = play(buffer, sf::microseconds(20)); // Issue occurs here: sound does not loop
sf::sleep(sf::seconds(5));
music = play(buffer, sf::microseconds(25)); // No issue here, sound loops properly
sf::sleep(sf::seconds(5));
return 0;
}
Playing the example sound from the start or seeking to 25 microseconds loops the sound just fine, but seeking to 20 microseconds causes the sound to stop at the end instead.
Through debugging, I found that in the erroneous case, when reaching the end of the sound,
SoundStream::fillAndPushBuffer exceeds the retry limit and requests a stop.
This is due to neither of the two conditions in
Music::onLoop being met: the current sample position is just one sample short of the music's ending, but no more full multi-channel samples can be read, therefore never allowing the loop condition to trigger.
This playback offset misalignment was caused by
InputSoundFile::seek(Uint64), which accepts offsets that end up between full multi-channel samples. I fixed this issue locally by dividing/multiplying the offset by the channel count, changing InputSoundFile::seek(Uint64)'s function body to the following:
void InputSoundFile::seek(Uint64 sampleOffset)
{
if (m_reader && m_channelCount != 0)
{
// The reader handles an overrun gracefully, but we
// pre-check to keep our known position consistent
m_sampleOffset = std::min(sampleOffset / m_channelCount * m_channelCount, m_sampleCount);
m_reader->seek(m_sampleOffset);
}
}
I haven't familiarized myself too deeply with SFML's sound streaming classes and audio streaming in general, so I might be completely mistaken about most or all of what I've written above. It could also be a completely unrelated issue instead, as the phenomenon has proven rather difficult to reproduce reliably.