2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-21 12:25:28 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-21 12:25:28 +00:00
|
|
|
// Sound.cpp
|
|
|
|
//
|
|
|
|
// Higher-level audio functions.
|
|
|
|
// Reading theme sound setings, playing audio samples etc.
|
|
|
|
//
|
|
|
|
|
2012-10-13 18:29:53 +00:00
|
|
|
#include "Sound.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2020-07-12 09:40:30 +00:00
|
|
|
#include "resources/ResourceManager.h"
|
2012-10-13 18:29:53 +00:00
|
|
|
#include "AudioManager.h"
|
2013-01-04 23:31:51 +00:00
|
|
|
#include "Log.h"
|
2013-08-07 05:41:55 +00:00
|
|
|
#include "Settings.h"
|
2014-01-03 16:40:36 +00:00
|
|
|
#include "ThemeData.h"
|
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
NavigationSounds* NavigationSounds::sInstance = nullptr;
|
|
|
|
|
2020-12-16 20:19:48 +00:00
|
|
|
std::map<std::string, std::shared_ptr<Sound>> Sound::sMap;
|
2014-01-03 16:40:36 +00:00
|
|
|
|
|
|
|
std::shared_ptr<Sound> Sound::get(const std::string& path)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
auto it = sMap.find(path);
|
2020-07-13 18:58:25 +00:00
|
|
|
if (it != sMap.cend())
|
2020-06-21 12:25:28 +00:00
|
|
|
return it->second;
|
2014-01-03 16:40:36 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<Sound> sound = std::shared_ptr<Sound>(new Sound(path));
|
|
|
|
AudioManager::getInstance()->registerSound(sound);
|
|
|
|
sMap[path] = sound;
|
|
|
|
return sound;
|
2014-01-03 16:40:36 +00:00
|
|
|
}
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
|
|
|
const std::string& view, const std::string& element)
|
2014-01-03 16:40:36 +00:00
|
|
|
{
|
2020-07-12 09:40:30 +00:00
|
|
|
LOG(LogDebug) << "Sound::getFromTheme(): Looking for navigation sound tag <sound name=\"" <<
|
2020-10-19 15:28:20 +00:00
|
|
|
element << "\">";
|
2014-01-03 16:40:36 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound");
|
2020-07-13 18:58:25 +00:00
|
|
|
if (!elem || !elem->has("path")) {
|
2020-10-19 15:28:20 +00:00
|
|
|
LOG(LogDebug) << "Sound::getFromTheme(): " << "Tag not found, using fallback sound file";
|
2020-07-12 09:40:30 +00:00
|
|
|
return get(ResourceManager::getInstance()->
|
|
|
|
getResourcePath(":/sounds/" + element + ".wav"));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2020-06-06 11:10:33 +00:00
|
|
|
|
2020-10-19 15:28:20 +00:00
|
|
|
LOG(LogDebug) << "Sound::getFromTheme(): Tag found, ready to load theme sound file";
|
2020-06-21 12:25:28 +00:00
|
|
|
return get(elem->get<std::string>("path"));
|
2014-01-03 16:40:36 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
Sound::Sound(
|
2020-10-18 17:45:26 +00:00
|
|
|
const std::string& path)
|
2020-06-28 16:39:18 +00:00
|
|
|
: mSampleData(nullptr),
|
2020-06-21 12:25:28 +00:00
|
|
|
mSamplePos(0),
|
|
|
|
mSampleLength(0),
|
|
|
|
playing(false)
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
loadFile(path);
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Sound::~Sound()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
deinit();
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
void Sound::loadFile(const std::string& path)
|
2012-10-13 18:29:53 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mPath = path;
|
|
|
|
init();
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::init()
|
|
|
|
{
|
2020-07-13 18:58:25 +00:00
|
|
|
if (mSampleData != nullptr)
|
2020-06-21 12:25:28 +00:00
|
|
|
deinit();
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-07-13 18:58:25 +00:00
|
|
|
if (mPath.empty())
|
2020-06-21 12:25:28 +00:00
|
|
|
return;
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Load WAV file via SDL.
|
|
|
|
SDL_AudioSpec wave;
|
2020-10-18 17:45:26 +00:00
|
|
|
Uint8* data = nullptr;
|
2013-05-14 19:31:39 +00:00
|
|
|
Uint32 dlen = 0;
|
2020-06-21 12:25:28 +00:00
|
|
|
if (SDL_LoadWAV(mPath.c_str(), &wave, &data, &dlen) == nullptr) {
|
2020-07-26 21:30:45 +00:00
|
|
|
LOG(LogError) << "Failed to load theme navigation sound file:";
|
2020-07-12 09:40:30 +00:00
|
|
|
LOG(LogError) << SDL_GetError();
|
2020-06-21 12:25:28 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-12-22 22:27:23 +00:00
|
|
|
|
|
|
|
// Convert sound file to the format required by ES-DE.
|
|
|
|
SDL_AudioStream *conversionStream = SDL_NewAudioStream(wave.format, wave.channels, wave.freq,
|
|
|
|
AudioManager::sAudioFormat.format, AudioManager::sAudioFormat.channels,
|
|
|
|
AudioManager::sAudioFormat.freq);
|
|
|
|
|
|
|
|
if (conversionStream == nullptr) {
|
|
|
|
LOG(LogError) << "Failed to create sample conversion stream:";
|
|
|
|
LOG(LogError) << SDL_GetError();
|
|
|
|
return;
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2020-12-22 22:27:23 +00:00
|
|
|
|
|
|
|
if (SDL_AudioStreamPut(conversionStream, data, dlen) == -1) {
|
|
|
|
LOG(LogError) << "Failed to put samples in the conversion stream:";
|
|
|
|
LOG(LogError) << SDL_GetError();
|
|
|
|
SDL_FreeAudioStream(conversionStream);
|
|
|
|
return;
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2020-12-22 22:27:23 +00:00
|
|
|
|
|
|
|
int sampleLength = SDL_AudioStreamAvailable(conversionStream);
|
|
|
|
|
|
|
|
Uint8* converted = new Uint8[sampleLength];
|
|
|
|
if (SDL_AudioStreamGet(conversionStream, converted, sampleLength) == -1) {
|
|
|
|
LOG(LogError) << "Failed to convert sound file '" << mPath << "':";
|
|
|
|
LOG(LogError) << SDL_GetError();
|
|
|
|
SDL_FreeAudioStream(conversionStream);
|
|
|
|
delete[] converted;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSampleData = converted;
|
|
|
|
mSampleLength = sampleLength;
|
|
|
|
mSamplePos = 0;
|
|
|
|
mSampleFormat.freq = AudioManager::sAudioFormat.freq;
|
|
|
|
mSampleFormat.channels = AudioManager::sAudioFormat.channels;
|
|
|
|
mSampleFormat.format = AudioManager::sAudioFormat.format;
|
|
|
|
SDL_FreeAudioStream(conversionStream);
|
2013-05-14 19:31:39 +00:00
|
|
|
SDL_FreeWAV(data);
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::deinit()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
playing = false;
|
2013-05-14 19:31:39 +00:00
|
|
|
|
2020-07-13 18:58:25 +00:00
|
|
|
if (mSampleData != nullptr) {
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
2020-06-21 12:25:28 +00:00
|
|
|
delete[] mSampleData;
|
|
|
|
mSampleData = nullptr;
|
|
|
|
mSampleLength = 0;
|
|
|
|
mSamplePos = 0;
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::play()
|
|
|
|
{
|
2020-07-13 18:58:25 +00:00
|
|
|
if (mSampleData == nullptr)
|
2020-06-21 12:25:28 +00:00
|
|
|
return;
|
|
|
|
|
2020-07-13 18:58:25 +00:00
|
|
|
if (!Settings::getInstance()->getBool("NavigationSounds"))
|
2020-06-21 12:25:28 +00:00
|
|
|
return;
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-12-31 13:27:31 +00:00
|
|
|
if (!AudioManager::getInstance()->getHasAudioDevice())
|
|
|
|
return;
|
|
|
|
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
2017-09-14 01:26:33 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (playing)
|
|
|
|
// Replay from start. rewind the sample to the beginning.
|
|
|
|
mSamplePos = 0;
|
|
|
|
else
|
|
|
|
// Flag our sample as playing.
|
|
|
|
playing = true;
|
2019-08-25 15:23:02 +00:00
|
|
|
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
2020-06-21 12:25:28 +00:00
|
|
|
// Tell the AudioManager to start playing samples.
|
|
|
|
AudioManager::getInstance()->play();
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2013-05-14 19:31:39 +00:00
|
|
|
bool Sound::isPlaying() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return playing;
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 19:31:39 +00:00
|
|
|
void Sound::stop()
|
2012-10-13 18:29:53 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Flag our sample as not playing and rewind its position.
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_LockAudioDevice(AudioManager::sAudioDevice);
|
2020-06-21 12:25:28 +00:00
|
|
|
playing = false;
|
|
|
|
mSamplePos = 0;
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
const Uint8* Sound::getData() const
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSampleData;
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Uint32 Sound::getPosition() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSamplePos;
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::setPosition(Uint32 newPosition)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mSamplePos = newPosition;
|
|
|
|
if (mSamplePos >= mSampleLength) {
|
|
|
|
// Got to or beyond the end of the sample. stop playing.
|
|
|
|
playing = false;
|
|
|
|
mSamplePos = 0;
|
|
|
|
}
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Uint32 Sound::getLength() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSampleLength;
|
2013-08-06 13:15:20 +00:00
|
|
|
}
|
|
|
|
|
2020-12-22 22:27:23 +00:00
|
|
|
NavigationSounds* NavigationSounds::getInstance()
|
|
|
|
{
|
|
|
|
if (sInstance == nullptr)
|
|
|
|
sInstance = new NavigationSounds();
|
|
|
|
|
|
|
|
return sInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NavigationSounds::deinit()
|
|
|
|
{
|
|
|
|
if (sInstance)
|
|
|
|
delete sInstance;
|
|
|
|
|
|
|
|
sInstance = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NavigationSounds::loadThemeNavigationSounds(const std::shared_ptr<ThemeData>& theme)
|
|
|
|
{
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "systembrowse"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "quicksysselect"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "select"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "back"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "scroll"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "favorite"));
|
|
|
|
navigationSounds.push_back(Sound::getFromTheme(theme, "all", "launch"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void NavigationSounds::playThemeNavigationSound(NavigationSoundsID soundID)
|
2013-08-06 13:15:20 +00:00
|
|
|
{
|
2020-12-22 22:27:23 +00:00
|
|
|
NavigationSounds::getInstance()->navigationSounds[soundID]->play();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NavigationSounds::isPlayingThemeNavigationSound(NavigationSoundsID soundID)
|
|
|
|
{
|
|
|
|
return NavigationSounds::getInstance()->navigationSounds[soundID]->isPlaying();
|
2013-08-06 13:15:20 +00:00
|
|
|
}
|