Home > Software design >  How to play a sf::Sound in a loop?
How to play a sf::Sound in a loop?

Time:12-03

We (school) are developping a game in C with SFML. The game is a fight game, where we need to play little sounds when the player gets hit for exemple.

I'm attempting to play a sf::Sound in a loop. I know we should not call the play() method of sf::Sound in a loop, but as the SFML apps all run in while loops, I have no other choices. Or at least, I don't know any other way to do it.

I tried to use a sound manager, I read multiple posts about it but I found nothing working.

Here is a sample code :

#include <SFML/Audio.hpp>

#include <vector>
#include <iostream>

int main() {
    int FPS = 60;

    //    sf::Music music;
    //    if(!music.openFromFile("resources/audio/fight_theme.ogg")) {
    //        std::cout << "Music was not found" << std::endl;
    //    }
    //    music.setVolume(10.f);
    //    music.play();

    // Create the main window
    sf::VideoMode desktopMode = sf::VideoMode::getDesktopMode();
    sf::RenderWindow app(
        sf::VideoMode(
            desktopMode.width,
            desktopMode.height,
            desktopMode.bitsPerPixel),
        "SkyddaForStackOverflow",
        sf::Style::Fullscreen
    );
    app.setFramerateLimit(FPS);

    //Main loop
    while (app.isOpen()) {
        /*In the actual code, we have two player instances.
        When the user press S or keyDown button, we call the attack function.
        In the function, we deal the damages to the ennemy, then we call playSound
        to play the hitting sound

        Please note this is a sample code to help understanding the case.
        In the actual code, everything is delegated to the Player class, to a SoundLoader (which is basically playSound() here)
        The problem is, even with delegation, even if the playSound method is not situated in the main loop,
        it is called in the main loop so the location of the code does not make any difference : it won't play since it won't
        be called outside of a loop as we do for the background music (see commented code after main() {..., the background music
        works fine.*/
        if(player.attack(ennemy)) {
            playSound();
        }
    }

    return EXIT_SUCCESS;
}

void playSound() {
    sf::SoundBuffer buffer;
    if(!buffer.loadFromFile(path)) {
        std::cout << "Sound was not found at " << path << std::endl;
    }
    sf::Sound sound(buffer);
    sound.play();
}

Don't hesitate if you have additionnal questions. Thanks !

CodePudding user response:

The issue is not with "being called outside the loop"; the issue is that your sf::Sound object is destroyed at the end of the playSound function!

First, define two global (or class-) variables:

std::map<std::string, sf::SoundBuffer> buffers;
std::map<std::string, sf::Sound> sounds;

You can now define playSound as follows:

void playSound(std::string path) {
  if (auto it = sounds.find(path); it == sounds.end()) {
    bool ok = buffers[path].loadFromFile(path);
    // do something if ok is false
    sounds[path] = sf::Sound{buffers[path]};
  }
  sounds[path].play();
}

This will keep your sf::Sound objects (and associated sf::SoundBuffers) alive. The first call will load the file from disk, the subsequent calls will just restart the existing sound.

Note that this code is slightly suboptimal in favor of understandability: it looks up sounds[path] twice. As an exercise to you, get rid of the second lookup by reusing it in the play call.

  • Related