Hello everybody,
Today I decided to re-write the design/logic in my Sound class (renamed to Audio) (it used to work before the re-write but the design was bad) by storing a std::string and a sf::SoundBuffer in a std::map in the class and then loading all .wav files into this map on start-up of the application (later on moving this to a separate thread, of course). A new issue had arisen so I started throwing around breakpoints and came to the conclusion that it for whatever reason seems to not ever load the sfml-audio-d-2.dll, even though it is in place with all other .dll's. Here a screenshot of the error:
(http://img694.imageshack.us/img694/4161/3ar.png)
(take a look at the Watch window)
Showing that it did in fact find the song we are trying to play (different sound being played):
(http://img507.imageshack.us/img507/7228/e5i.png)
As for the .dll's:
(http://img585.imageshack.us/img585/3730/veo3.png)
Now as for the code, it can all be found on this (https://github.com/Discover-/Platformer-SFML) GitHub repository, but I'll paste some code regarding the issue here anyhow.
audio.h
#pragma once
#include <vector>
#include <array>
#include <map>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
class Game;
class Audio
{
public:
Audio(Game* _game);
~Audio();
bool Load(std::string filename);
void Play(std::string filename, bool loop = false);
void Stop(std::string filename);
void SetVolume(std::string filename, float volume);
float GetVolume(std::string filename);
void SetLoop(std::string filename, bool val);
bool IsLooping(std::string filename);
sf::SoundSource::Status GetStatus(std::string filename);
sf::Sound* GetPlayingSound(std::string filename);
private:
Game* game;
std::map<std::string, sf::SoundBuffer> soundBuffers;
};
audio.cpp
#include "audio.h"
#include "game.h"
#include <SFML/Audio.hpp>
Audio::Audio(Game* _game)
{
game = _game;
}
Audio::~Audio()
{
}
bool Audio::Load(std::string filename)
{
sf::SoundBuffer buffer;
if (buffer.loadFromFile(filename))
{
soundBuffers[filename] = buffer;
return true;
}
else
std::cout << "Audio::Load: Could not find audio file '" << filename << "'" << std::endl;
return false;
}
void Audio::Play(std::string filename, bool loop /* = false */)
{
std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.find(filename);
if (itr != soundBuffers.end())
{
sf::Sound playingSound = sf::Sound((*itr).second);
playingSound.play();
if (loop)
playingSound.setLoop(true);
if (game->IsMusicMuted())
playingSound.setVolume(0.0f);
}
else
std::cout << "Audio::Play: Could not find audio file '" << filename << "'" << std::endl;
}
void Audio::Stop(std::string filename)
{
if (filename == "all")
{
for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
sf::Sound((*itr).second).stop();
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->stop();
else
std::cout << "Audio::Stop: Could not find audio file '" << filename << "'" << std::endl;
}
}
void Audio::SetVolume(std::string filename, float volume)
{
if (filename == "all")
{
for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
sf::Sound((*itr).second).setVolume(volume);
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->setVolume(volume);
else
std::cout << "Audio::SetVolume: Could not find audio file '" << filename << "'" << std::endl;
}
}
float Audio::GetVolume(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getVolume();
else
std::cout << "Audio::GetVolume: Could not find audio file '" << filename << "'" << std::endl;
return 0.0f;
}
void Audio::SetLoop(std::string filename, bool val)
{
if (filename == "all")
{
for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
sf::Sound((*itr).second).setLoop(val);
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->setLoop(val);
else
std::cout << "Audio::SetLoop: Could not find audio file '" << filename << "'" << std::endl;
}
}
bool Audio::IsLooping(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getLoop();
else
std::cout << "Audio::IsLooping: Could not find audio file '" << filename << "'" << std::endl;
return false;
}
sf::SoundSource::Status Audio::GetStatus(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getStatus();
else
std::cout << "Audio::GetStatus: Could not find audio file '" << filename << "'" << std::endl;
return sf::SoundSource::Stopped;
}
sf::Sound* Audio::GetPlayingSound(std::string filename)
{
std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.find(filename);
if (itr != soundBuffers.end())
return &sf::Sound((*itr).second);
return NULL;
}
Loading the audio (done using the Dirent library to load all files from a given directory)
void Game::LoadAllAudio()
{
DIR* dir;
struct dirent* ent;
std::stringstream ss;
audio = new Audio(this);
if ((dir = opendir("Audio")) != NULL)
{
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_name[0] != '.') //! These seem to be the only hidden invisible files in there and the dirent library doesn't offer detection for it, so this will work. :)
{
ss << "Audio/" << ent->d_name;
audio->Load(ss.str().c_str());
ss.str(std::string());
}
}
closedir(dir);
}
}
Playing the audio (example)
Menu::Menu(Game* _game)
{
game = _game;
selectedOption = 1;
currentMenu = MENU_MAIN;
newMenu = MENU_NONE;
movingCurrMenuOut = false;
movingNewMenuIn = false;
game->GetAudio()->Play("Audio/menu_music.wav", true);
}
Hope that is enough!
Thanks for reading,
Jasper
Sorry to say that, but your new design is totally wrong :P
You're always constructing/using/returning temporary sf::Sound objects, how do you expect anything to work permanently? As soon as a sf::Sound is destroyed, everything that you've done on it is lost. All your functions are useless.
You must store both the sound buffers and the sounds that you create.
No need to be sorry, hehe.
I implemented your idea with a small twist (sf::SoundBuffer becoming sf::Sound in the std::map because there is no reason to store both, right?). Sadly, it's still not working.. (I get the same debug error, too)
#include "audio.h"
#include "game.h"
#include <SFML/Audio.hpp>
Audio::Audio(Game* _game)
{
game = _game;
}
Audio::~Audio()
{
}
bool Audio::Load(std::string filename)
{
sf::SoundBuffer buffer;
if (buffer.loadFromFile(filename))
{
sounds[filename] = sf::Sound(buffer);
return true;
}
else
std::cout << "Audio::Load: Could not find audio file '" << filename << "'" << std::endl;
return false;
}
void Audio::Play(std::string filename, bool loop /* = false */)
{
std::map<std::string, sf::Sound>::iterator itr = sounds.find(filename);
if (itr != sounds.end())
{
(*itr).second.play();
if (loop)
(*itr).second.setLoop(true);
if (game->IsMusicMuted())
(*itr).second.setVolume(0.0f);
}
else
std::cout << "Audio::Play: Could not find audio file '" << filename << "'" << std::endl;
}
void Audio::Stop(std::string filename)
{
if (filename == "all")
{
for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
(*itr).second.stop();
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->stop();
else
std::cout << "Audio::Stop: Could not find audio file '" << filename << "'" << std::endl;
}
}
void Audio::SetVolume(std::string filename, float volume)
{
if (filename == "all")
{
for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
(*itr).second.setVolume(volume);
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->setVolume(volume);
else
std::cout << "Audio::SetVolume: Could not find audio file '" << filename << "'" << std::endl;
}
}
float Audio::GetVolume(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getVolume();
else
std::cout << "Audio::GetVolume: Could not find audio file '" << filename << "'" << std::endl;
return 0.0f;
}
void Audio::SetLoop(std::string filename, bool val)
{
if (filename == "all")
{
for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
(*itr).second.setLoop(val);
}
else
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
playingSnd->setLoop(val);
else
std::cout << "Audio::SetLoop: Could not find audio file '" << filename << "'" << std::endl;
}
}
bool Audio::IsLooping(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getLoop();
else
std::cout << "Audio::IsLooping: Could not find audio file '" << filename << "'" << std::endl;
return false;
}
sf::SoundSource::Status Audio::GetStatus(std::string filename)
{
if (sf::Sound* playingSnd = GetPlayingSound(filename))
return playingSnd->getStatus();
else
std::cout << "Audio::GetStatus: Could not find audio file '" << filename << "'" << std::endl;
return sf::SoundSource::Stopped;
}
sf::Sound* Audio::GetPlayingSound(std::string filename)
{
std::map<std::string, sf::Sound>::iterator itr = sounds.find(filename);
if (itr != sounds.end())
return &(*itr).second;
return NULL;
}
So you're suggesting to save the sf::Sound and sf::SoundBuffer along with the filename (std::string) and then do.. what exactly on Audio::Play?
You should store one sound buffer per audio file, and then as many sounds as you need to play. You may play several sounds that use the same sound buffer at the same time (gun shot, foot step, ...), so it's not a good idea to link the sf::Sound to the sf::SoundBuffer/filename. That's the reason why SFML uses this design with two separate classes.
So you should redesign your whole Audio class, your current design cannot handle playing multiple instances of the same sound at the same time.
I'm not getting the part I bolded in your text. How am I supposed to store several sf::Sound instances and link them all to a single sf::SoundBuffer and std::string? Are we talking something like this:
std::map<std::string, std::pair<sf::SoundBuffer, std::list<sf::Sound> > > sounds;
I'm sorry for the many questions, I'm just having a really hard time understanding the audio system (as you can tell by now).
Also, when that part (thus the way you're saying it should work) is implemented, wouldn't it be better - in terms of design - to make the Audio class singleton?
What do you think of
std::map<std::string, sf::SoundBuffer> buffers;
std::list<sf::Sound> activeSounds;
Do you use sfml-graphics? The sf::Sprite and sf::Texture classes?
wouldn't it be better - in terms of design - to make the Audio class singleton?
Singletons are never a "better" design choice. You can read many things about them on the internet.