Skip to main content
GameDev.net gamedev.net
🔒 Locked

Problem with Sounds in SFML 3

Started by snnooze Jun 7 at 12:35 AM 11 replies 1.3k views
Original Post
snnooze
snnooze

Hello,

In my actual project where I use SFML 3 I try to add some sound effects, actually music works but when I try to play a sound nothing happen.

I've tested several ways and places, I have checked the path of the file is ok, I have no compile errors, and the file open well in a media player (VLC) and I've tried with .wav, .ogg and .mp3 files too.

The game run and don't crash, just no sound effect when it may be have one.

The code I've used is in on case :

sf::SoundBuffer buffer("Assets/Sounds/rebond_SFX.ogg");
sf::Sound rebond = sf::Sound(buffer);
rebond.play();

In others :


//buffer is declared in the header file of the class
if (this->buffer.loadFromFile("Assets/Sounds/rebond_SFX.ogg")) {
    sf::Sound rebond = sf::Sound(this->buffer);
    rebond.play();
}

I have tried to add some libraries just to test :

SFML::Graphics SFML::Audio FLAC::FLAC Vorbis::vorbis Vorbis::vorbisenc Vorbis::vorbis Ogg::ogg

I don't know what to do more now.

If someone have an idea I'm listening 🙂

Thanks for your help,

Have a nice day.

RmbRT
RmbRT

Looks to me like unlike what the documentation suggests, the sf::Sound object has to live for as long as the sound should be played. Sound::play() plays the sound in a background thread, so it is not blocking, but Sound::~Sound() calls Sound::stop() and then erases the sound handle from the buffer: Here's the destructor (SFML/Audio/Sound.cpp:209):

Sound::~Sound()
{
    stop();
    if (m_impl->buffer)
        m_impl->buffer->detachSound(this);
}

Here's the SoundBuffer::detachSound() method (SFML/Audio/SoundBuffer.cpp:305):

void SoundBuffer::detachSound(Sound* sound) const
{
    m_sounds.erase(sound);
}

So, the sf::Sound has to live for as long as the sfx plays, and the sf::SoundBuffer should live ideally for the entire lifetime of the program.

One way you can easily test this is just insert a 1 second sleep after your call to Sound::play(). If that fixes it, you know that's the cause. Alternatively, you can just do a memory leak via new sf::Sound() and just never delete it. For anything more sophisticated, you would have to maintain a list of sound objects, and regularly check whether they finished playing (Sound::getStatus()), and then delete or recycle them.

Actually, the docs do state this, although not super unambiguously, considering the context of the passage:

Sounds (and music) are played in a separate thread. This means that you are free to do whatever you want after calling play() (except destroying the sound or its data, of course), the sound will continue to play until it's finished or explicitly stopped.

https://www.sfml-dev.org/tutorials/3.0/audio/sounds/

I also looked around some more and it seems likee SFML doesn't offer a “fire and forget” type API for playing sound effects.

Walk with God.
snnooze
snnooze

RmbRT wrote:

Looks to me like unlike what the documentation suggests, the sf::Sound object has to live for as long as the sound should be played. Sound::play() plays the sound in a background thread, so it is not blocking, but Sound::~Sound() calls Sound::stop() and then erases the sound handle from the buffer: Here's the destructor (SFML/Audio/Sound.cpp:209):

Hello,

Thanks for your answer,

That was exactly the problem, now I can play sounds 🙂

There is the code I have used :

if (this->m_buffer.loadFromFile("Assets/Sounds/rebond_SFX.ogg")) {
    
     if(this->m_rebond.getStatus() == sf::SoundSource::Status::Stopped) {

        this->m_rebond.setBuffer(this->m_buffer);
        this->m_rebond.play();

    }
    
}

Now I think I can optimize that by not loading the file every time, I will try that.

Thanks a lot for your help,

Have a nice day 🙂

RmbRT
RmbRT

@khawk The forum claims there was a second reply (by OP) to this thread, 3 hours ago, and I also got a notification, but the reply just doesn't show up, neither with Beta enabled nor disabled, and also not in a private tab.

Walk with God.
RmbRT
RmbRT

snnooze said:
Now I think I can optimize that by not loading the file every time, I will try that.

You generally want to have a program structure like this:

  • on_start() or something: set up the global program state, initialise libraries and your own program modules, create the window, allocate buffers, and acquire/load resources. This is where you should be loading the sounds.
  • on_frame(dt): updates the game state, and displays it.
  • on_event(): handles keyboard and mouse events between frames, also updating game state.
  • on_teardown(): called on program termination, can release resources etc..

There are a few variations on this: many programs are designed to have an infinite loop that does this:

time last_frame = now();
while(!program_should_quit())
{
	// handle all events that occurred since the last time we checked.
	for(Event e; poll_event(&e); )
		on_event(e);

	time this_frame = now();
	on_frame(this_frame - last_frame);
	last_frame = this_frame;
}
on_teardown();

Here, event handling is manual, and game state updates are inherently tied to the rendering code. In something like glfw, you register callbacks that get called by glfw, instead of you actively polling them. And then there are approaches where there is a separate function update_game_state(dt) which gets called in regular intervals, so that on_frame() really only handles displaying. In many cases and for small projects, it is usually less effort to have rendering and updating in one per-frame function.

Anyway, if you go by this general structure, you can load all the resources up front, or maybe have a special function for the first frame that shows a loading/splash screen while you load. There are many more sophisticated techniques for program flow for games, like nested state machines and all that stuff, but this is the bare minimum I'd recommend that is also quite easy to use, and immediately beneficial. You can simply create global variables for each sound file you want to load, and load them during the on_start() call.

Walk with God.
snnooze
snnooze

Hello,

Yes and I think it's approximativly tried to do, I'm sure my code is far to be perfectly organized but I've tried to keep an init/inputs/update/draw system.

And, in the init I have a loading step.

It's not easy to explain how I've coded unil now that project but if you want to take a look you can find the project code here. (sorry some comments are in french)

Thanks for your help,

Have a nice day 🙂

RmbRT
RmbRT

I noticed you're manually listing your source files in your CMakeLists.txt. Check out what I did for an old project of mine to make source file handling easier: https://github.com/RmbRT/re/blob/master/CMakeLists.txt. This also differentiates between headers and implementations, but that's not needed if you don't want to export a separate include folder containing only your headers.

Walk with God.
snnooze
snnooze

Hello,

Yes, my first idea was to do something as you do.

I don't remember why and when I've started to list the files and as my IDE (CLion) automaticly manage the list in the Cmake file I have let it as it was.

I will correct that.

Thanks for your help,

Have a nice day 🙂

snnooze
snnooze

Hello,

me again 🙂

I have a new problem that seems to be in relation with the sounds and sound buffer of SFML.

The game starts and runs well with sounds when I launch it from the IDE but if I try to launch it by clicking on the .exe icon that crash/don't want to start. (no erros at the compilation)

After a few test it seems that the difference that cause that problem is I declare the sound buffer in my header file:

sf::SoundBuffer m_buffer;

If I comment that line the .exe launch.

I don't know what could be the problem as all works with no errors in debug mode and the debug mode seems to launch exactlly the same .exe file. I have tried to lauch it from the terminal too and nothing happened (no game and no error message).

Maybe I've made a mistake but I don't know wich.

Maybe someone know how to solve that ?

Thanks for your help,

Have a nice day 🙂

RmbRT
RmbRT

You need to launch the .exe file from within the right directory. Let's say you have your Game/ directory, and then Game/build/Debug/game.exe. If you launch it from the IDE, it would launch it from within Game/, meaning, if you tried to open "x.txt", it would refer to Game/x.txt. But if you just click on the game.exe inside Game/build/Debug/, it would refer to Game/build/Debug/x.txt. All relative paths that the game tries to open are looked up relative to the directory you are currently in, not necessarily relative to the .exe file.

It may also be that there's something going on with it trying to load a .dll or something? In general, it's not good to have classes that have constructors as global variables (i.e., code that implicitly runs before main() starts). This is also why I don't like OOP and C++. In C, you don't get such issues. Though looking at the constructor of SoundBuffer, it doesn't really do anything.

Are you sure it's the fault of that line? Also, is that header file included by multiple .cpp files? You can't declare a global variable in a header file if multiple .cpp files include it. If you do, you need to declare it as extern sf::SoundBuffer m_buffer; and then in one .cpp file, declare it normally without extern. Also, does any code even use that variable? It would be weird if commenting out that line made a difference if nothing uses the variable to begin with. Also, global variables should not be prefixed m_, that is usually used for members of a class. It would be better to use something like g_ (global) or s_ (static / singleton).

Walk with God.
snnooze
snnooze

Hello,

Sorry for the delay,

RmbRT wrote:

You need to launch the .exe file from within the right directory. Let's say you have your Game/ directory, and then Game/build/Debug/game.exe. If you launch it from the IDE, it would launch it from within Game/, meaning, if you tried to open "x.txt", it would refer to Game/x.txt. But if you just click on the game.exe inside Game/build/Debug/, it would refer to Game/build/Debug/x.txt. All relative paths that the game tries to open are looked up relative to the directory you are currently in, not necessarily relative to the .exe file.

I think that point is OK as I have the assets directory in the debug directory near the .exe file and launching the .exe works usually and succeed to load the assets.

RmbRT wrote:

It may also be that there's something going on with it trying to load a .dll or something?

Possible but usually I have a dialog box that say a .dll is missing and wich one but in that case nothing.

RmbRT wrote:

Are you sure it's the fault of that line?

I have commented all the sound effect lines and the .exe launch but as soon as I uncomment that line the .exe don't want to launch so I think that is that line the problem.

RmbRT wrote:

is that header file included by multiple .cpp files?

That is the "ball.hpp" file and it is included in the ball.cpp where I have wrote the classe/functions and included in Game.hpp the be used. So no only in one .cpp file.

RmbRT wrote:

Also, does any code even use that variable? It would be weird if commenting out that line made a difference if nothing uses the variable to begin with

Yes but for findinf the problem I have commented all lines that use it.

RmbRT wrote:

Also, global variables should not be prefixed m_, that is usually used for members of a class. It would be better to use something like g_ (global) or s_ (static / singleton).

I don't use it as a global variable, maybe I'm wrong but in my head the sound is "linked" to the ball so in the class of the ball I manage several things one of that things is reversing the direction when the ball hit one of the 2 "paddles" and make a sound at that moment so I'have declared that as a member.

I will continue my tests to find a solution or if that need a .dll that I don't know.

Thanks for your help,

Have a nice day.

snnooze
snnooze

RmbRT wrote:

you need to declare it as extern sf::SoundBuffer m_buffer; and then in one .cpp file, declare it normally without extern.

Finally that solution works

Thanks for your help 🙂

Have a nice day.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.