SFML community forums

Help => Audio => Topic started by: gamepopper on May 08, 2020, 06:15:27 pm

Title: Setting LoopPoints more than once will cause the music to stop playing.
Post by: gamepopper on May 08, 2020, 06:15:27 pm
I'm currently working on a small game and what I want is for the music to loop in small segments, with each segment building up the music.

The solution was to set loop points when playing a track and then after a specific event change the loop points to further along in the song.

The solution worked, anytime new loop points were set the music would keep on playing up until the end of the new loop point where it would jump back to start of the new loop point. However, the music would stop playing eventually, even though no functions were called to stop the sound.

I assumed it was just something in my engine, but I've modified the sound example to only play my music file and update the loop points every 25 seconds, and the music eventually stops (even the music status no longer returning the Playing state). I've attached the sample code and file in the post.

 // Load an ogg music file
sf::Music music;
if (!music.openFromFile("resources/" + filename))
    return;

music.setLoop(true);
music.setLoopPoints(sf::Music::TimeSpan(sf::seconds(0.0f), sf::seconds(8.0f)));

// Play it
music.play();

int level = 1;

// Loop while the music is playing
while (music.getStatus() == sf::Music::Playing)
{
     // Leave some CPU time for other processes
     sf::sleep(sf::milliseconds(25000));

     level++;
     float loopEnd = (level + 2) * 8.0f;

     if (loopEnd <= music.getDuration().asSeconds())
     {
        music.setLoop(true);
        music.setLoopPoints(sf::Music::TimeSpan(sf::seconds(loopEnd - 8.0f), sf::seconds(8.0f)));
    }
}

I'm using the latest SFML source code (last update 15th April 2020) and running on Windows 10, using Visual Studio 2017 for dev.

I've found that setting the loop points only once does not cause any issues, but setting loop points to the same music stream more than once causes it to eventually stop.
Title: Re: Setting LoopPoints more than once will cause the music to stop playing.
Post by: Hapax on May 08, 2020, 08:48:21 pm
You're setting the loop points to loop around the exact last 8 seconds?
It could be that it is not exact and may go over the end (even by just a sample), which might then stop the music if the loop points are not actually at the end.
This may not even be just a sample, it could be a block.

Could you try the exact same code but a few seconds earlier, avoiding the last part of the sound buffer for certain?
If that is the case, you could just increase the length of your sound buffer (even if just with silence) just to cover it.



EDIT:
After testing your code with a full song, I can see that it doesn't actually get near the end of the music so my hypothesis above doesn't seem like a likely cause.
I wonder if it's possible that setting loop points past the end (especially if the start point is after the end) could cause the music to stop playing.

EDIT 2:
It won't allow you to set your loop points to start at the duration's end or afterwards but it will allow you to set it so that the timespan is after the end. e.g. offset: duration - 4, length: 8
On a side note, changing the loop points introduces a significant (audible) delay in the sound itself. But, this is probably completely separate and I'm unsure if this is known.
Title: Re: Setting LoopPoints more than once will cause the music to stop playing.
Post by: Hapax on May 08, 2020, 10:28:48 pm
Okay. With some further testing, I've written a small code that is a little similar to yours that moves the loop points automatically very quickly. Not even that far but they must change.

#include <SFML/Audio.hpp>

int main()
{
    sf::Music music;
    if (!music.openFromFile("music.ogg"))
        return EXIT_FAILURE;
    music.setLoop(true);

    const sf::Time timespanOffsetStep{ sf::seconds(16.f) };
    const sf::Time timespanLength{ sf::seconds(1.f) };
    const sf::Time resetLoopPointsTime{ sf::seconds(0.03f) };

    unsigned int loopPointStart{ 0u };
    sf::Clock clock;
    music.play();

    while (!sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
    {
        if (clock.getElapsedTime() > resetLoopPointsTime)
        {
            ++loopPointStart;
            if (loopPointStart > 1u)
                loopPointStart = 0u;
            music.setLoopPoints(sf::Music::TimeSpan(static_cast<float>(loopPointStart) * timespanOffsetStep, timespanLength));
            clock.restart();
        }
    }
}

Please can some people test this code.
The music file is a 3 and a half minute song.

The first thing you may notice is that even though the only thing that is happening is the loop points are moving, the music playback itself is severly affected.
Occasionally, it may stop a couple of seconds in. Sometimes, it may not.
These are the most 'reliable' settings for me to get it to stop. Feel free to change the 3 const times to experiment with its effects.


I would say that the setting the music's loop points should be implemented in such a way that it does not affect the music performance itself. Just changing it once, I noticed the slight delay 'click'. The code above makes this happen often so you can see it more obviously.

On top of that, and also getting back to the original poster's problem, this can also cause the music to just stop dead. There must be something somewhere that causes setting a loop point to stop the music, although I suspect that it's linked to the fact that the performance is affected since it must be changing the stream every time the loop points are altered.
Title: Re: Setting LoopPoints more than once will cause the music to stop playing.
Post by: eXpl0it3r on May 11, 2020, 11:12:44 am
Since it's all based on data streams, it might just be an issue with picking the right stream position or mismatch between reading the file stream and decoding the file stream.
Another thing that came to my mind is, that we need to prepare a number of samples ahead of time, so maybe the "pre-fetching" is not accounting for the loop point, which then can lead to mismatches once the play position reaches the loop point.
And finally, variable bit rates (VBR) can cause issues with exact position jumps, then again Gamepopper's audio has a constant bit rate.

It's a feature that as far as I know, hasn't been used as much, thus I'd not be surprised if there are certain bugs, that we haven't found so far.

Don't forget that the behavior can be different, depending on the audio format picked, as different decoders are used.
Title: Re: Setting LoopPoints more than once will cause the music to stop playing.
Post by: Hapax on May 11, 2020, 05:45:06 pm
The one thing I want to mention is that changing the loop points shouldn't affect the performance of the music, especially if the music is not reaching any loop point.

Adding
music.setPlayingOffset(timespanOffsetStep + timespanLength);
in my code above means that it starts after any of the loop points so never touch one.
Even though the music is playing from after the loop points, changing those points to something different (still before the current playing position) affects the performance.

I just wanted to make sure this effect was know as not sure whether it was or not.