Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: When the first sound ends, so does the second.  (Read 4975 times)

0 Members and 1 Guest are viewing this topic.

Zephilinox

  • Newbie
  • *
  • Posts: 43
    • View Profile
When the first sound ends, so does the second.
« on: December 13, 2013, 02:58:36 pm »
Are there any obvious mistakes I'm making here?

Noteworthy: m_Sounds and m_SoundBuffer are static private member variables. m_Sounds is a vector and m_SoundBuffer is a map.

void ResourceManager::Update()
{
    for (unsigned int i = 0; i < m_Sounds.size(); ++i)
    {
        std::cout << "Sound " << i << " = " << m_Sounds[i].getPlayingOffset().asSeconds() << "\n";

        if (m_Sounds[i].getStatus() == sf::Sound::Stopped)
        {
            std::cout << i+1 << " / " << m_Sounds.size() << " was Killed\n";
            m_Sounds.erase(m_Sounds.begin() + i);
            continue;
        }
    }
}

sf::SoundBuffer& ResourceManager::soundBuffer(std::string fileName)
{
        std::map<std::string, sf::SoundBuffer>::iterator it = m_SoundBuffers.find(fileName);

        if (it == m_SoundBuffers.end()) //not found
        {
                sf::SoundBuffer sb;
                assert(sb.loadFromFile("audio/" + fileName + ".wav"));
                std::cout << "SoundBuffer Loaded: " << "audio/" << fileName << ".wav\n";
                m_SoundBuffers.insert(std::make_pair(fileName, sb));
                return m_SoundBuffers.find(fileName)->second;
        }

        return it->second;
}

void ResourceManager::sound(std::string fileName)
{
    sf::Sound s(this->soundBuffer(fileName));
    m_Sounds.push_back(s);
    m_Sounds.back().play();
}
 

case sf::Event::MouseButtonPressed:
{
    ResMan.sound("fireworks");
    break;
}
 



only when I close the RenderWindow do I get the segmentation fault

« Last Edit: December 13, 2013, 03:09:24 pm by Zephilinox »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11033
    • View Profile
    • development blog
    • Email
Re: When the first sound ends, so does the second.
« Reply #1 on: December 13, 2013, 04:45:25 pm »
Are there any obvious mistakes I'm making here?
The irony of your post is, that I can directly quote you as answer:

Noteworthy: m_Sounds and m_SoundBuffer are static private member variables. m_Sounds is a vector and m_SoundBuffer is a map.
Static members are essentially global variables, global variables have an undefined order of destruction and since SFML has global variables as well, it can happen that SFML's audio stuff gets deleted before the sound objects that still reference to the SFML parts, thus leading to a crash.

tl;dr: Don't use global variables! ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Zephilinox

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: When the first sound ends, so does the second.
« Reply #2 on: December 13, 2013, 05:09:02 pm »
Are there any obvious mistakes I'm making here?
The irony of your post is, that I can directly quote you as answer:

Noteworthy: m_Sounds and m_SoundBuffer are static private member variables. m_Sounds is a vector and m_SoundBuffer is a map.
Static members are essentially global variables, global variables have an undefined order of destruction and since SFML has global variables as well, it can happen that SFML's audio stuff gets deleted before the sound objects that still reference to the SFML parts, thus leading to a crash.

tl;dr: Don't use global variables! ;)

Thanks, I'll have to think of something else for that just in case another bug creeps up however making it non-static(for both sound and buffers) has not fixed the issue.

Edit: I quickly thought of something and changed the sound() function to this:
void ResourceManager::sound(std::string fileName)
{
    //sf::Sound s(this->soundBuffer(fileName));
    sf::Sound* s = new sf::Sound();
    s->setBuffer(this->soundBuffer(fileName));
    m_Sounds.push_back(s);
    m_Sounds.back()->play();
}
 

and it now works properly, so I then changed it to this to avoid memory management (if it comes to it I'll use smart pointers, just used manual to test quickly)

void ResourceManager::sound(std::string fileName)
{
    sf::Sound s;
    m_Sounds.push_back(s);
    m_Sounds.back().setBuffer(this->soundBuffer(fileName));
    m_Sounds.back().play();
}
 

but that has the same issues as the original version. Something is going on somewhere and I'm not sure what.
« Last Edit: December 13, 2013, 05:17:33 pm by Zephilinox »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: When the first sound ends, so does the second.
« Reply #3 on: December 13, 2013, 08:02:49 pm »
No need to use smart pointers here.

Be careful if m_Sounds is a std::vector: When inserting new objects, the container may relocate the existing elements, leading to the destruction (and stopping) of the sounds.

Furthermore, don't erase elements in the middle of the iteration in a std::vector. The std::vector::erase() call leads to copies of all the elements behind the one being removed, which has the issues mentioned above, in addition to being terribly inefficient.

You might consider std::list to store sounds, here you can also use erase() without problems. Or directly remove() or remove_if(). However, you have to use iterators instead of indices -- but you should iterate with iterators anyway, for any container (unless you actually need the random access or the numeric value of the index).
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Zephilinox

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: When the first sound ends, so does the second.
« Reply #4 on: December 13, 2013, 09:12:20 pm »
No need to use smart pointers here.

Be careful if m_Sounds is a std::vector: When inserting new objects, the container may relocate the existing elements, leading to the destruction (and stopping) of the sounds.

Furthermore, don't erase elements in the middle of the iteration in a std::vector. The std::vector::erase() call leads to copies of all the elements behind the one being removed, which has the issues mentioned above, in addition to being terribly inefficient.

You might consider std::list to store sounds, here you can also use erase() without problems. Or directly remove() or remove_if(). However, you have to use iterators instead of indices -- but you should iterate with iterators anyway, for any container (unless you actually need the random access or the numeric value of the index).

Switching from std::vector to std::list fixed it! thanks Nexus. It's obvious I need to learn a lot more about how these basic data structures work. Your description about stopping the sounds was another issue I was having where as soon as a sound is added, even if none of them are at the end it would indeed destroy all of them but that is now fixed too :)

Indices are just a habit for me, I'll try to use iterators from now on.