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
|
|
|
// AudioManager.cpp
|
|
|
|
//
|
2020-06-26 16:11:24 +00:00
|
|
|
// Low-level audio functions (using SDL2).
|
2020-06-21 12:25:28 +00:00
|
|
|
//
|
|
|
|
|
2012-10-13 18:29:53 +00:00
|
|
|
#include "AudioManager.h"
|
|
|
|
|
2013-01-04 23:31:51 +00:00
|
|
|
#include "Log.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "Settings.h"
|
|
|
|
#include "Sound.h"
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-06-26 16:11:24 +00:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
2020-12-20 23:23:22 +00:00
|
|
|
std::shared_ptr<AudioManager> AudioManager::sInstance;
|
2013-05-14 19:31:39 +00:00
|
|
|
std::vector<std::shared_ptr<Sound>> AudioManager::sSoundVector;
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_AudioDeviceID AudioManager::sAudioDevice = 0;
|
2013-05-14 19:31:39 +00:00
|
|
|
SDL_AudioSpec AudioManager::sAudioFormat;
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
bool stillPlaying = false;
|
|
|
|
|
|
|
|
// Initialize the buffer to "silence".
|
|
|
|
SDL_memset(stream, 0, len);
|
|
|
|
|
|
|
|
// Iterate through all our samples.
|
|
|
|
std::vector<std::shared_ptr<Sound>>::const_iterator soundIt = sSoundVector.cbegin();
|
|
|
|
while (soundIt != sSoundVector.cend()) {
|
|
|
|
std::shared_ptr<Sound> sound = *soundIt;
|
2020-07-13 18:58:25 +00:00
|
|
|
if (sound->isPlaying()) {
|
2020-06-21 12:25:28 +00:00
|
|
|
// Calculate rest length of current sample.
|
|
|
|
Uint32 restLength = (sound->getLength() - sound->getPosition());
|
2020-10-18 17:45:26 +00:00
|
|
|
if (restLength > static_cast<Uint32>(len)) {
|
2020-06-21 12:25:28 +00:00
|
|
|
// If stream length is smaller than sample length, clip it.
|
|
|
|
restLength = len;
|
|
|
|
}
|
|
|
|
// Mix sample into stream.
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_MixAudioFormat(stream, &(sound->getData()[sound->getPosition()]), AUDIO_S16,
|
2020-12-20 15:41:58 +00:00
|
|
|
restLength, Settings::getInstance()->getInt("SoundVolumeNavigation") * 1.28);
|
2020-06-21 12:25:28 +00:00
|
|
|
if (sound->getPosition() + restLength < sound->getLength()) {
|
|
|
|
//sample hasn't ended yet
|
|
|
|
stillPlaying = true;
|
|
|
|
}
|
|
|
|
// Set new sound position. if this is at or beyond the end of the sample,
|
|
|
|
// it will stop automatically.
|
|
|
|
sound->setPosition(sound->getPosition() + restLength);
|
|
|
|
}
|
|
|
|
// Advance to next sound.
|
|
|
|
++soundIt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have processed all samples. check if some will still be playing.
|
|
|
|
if (!stillPlaying) {
|
2020-12-20 23:23:22 +00:00
|
|
|
// Nothing is playing, pause the audio until Sound::play() wakes us up.
|
|
|
|
SDL_PauseAudioDevice(sAudioDevice, 1);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2013-05-14 19:31:39 +00:00
|
|
|
AudioManager::AudioManager()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
init();
|
2013-05-21 08:40:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioManager::~AudioManager()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
deinit();
|
2013-05-21 08:40:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
std::shared_ptr<AudioManager>& AudioManager::getInstance()
|
2013-05-21 08:40:01 +00:00
|
|
|
{
|
2020-12-20 23:23:22 +00:00
|
|
|
// Check if an AudioManager instance is already created, if not create it.
|
|
|
|
if (sInstance == nullptr)
|
2020-06-21 12:25:28 +00:00
|
|
|
sInstance = std::shared_ptr<AudioManager>(new AudioManager);
|
2020-12-20 23:23:22 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return sInstance;
|
2013-05-21 08:40:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioManager::init()
|
|
|
|
{
|
2020-12-20 23:23:22 +00:00
|
|
|
LOG(LogInfo) << "Setting up AudioManager...";
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
|
|
|
|
LOG(LogError) << "Error initializing SDL audio!\n" << SDL_GetError();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop playing all Sounds.
|
2020-07-13 18:58:25 +00:00
|
|
|
for (unsigned int i = 0; i < sSoundVector.size(); i++) {
|
|
|
|
if (sSoundVector.at(i)->isPlaying())
|
2020-06-21 12:25:28 +00:00
|
|
|
sSoundVector[i]->stop();
|
|
|
|
}
|
|
|
|
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_AudioSpec sRequestedAudioFormat;
|
|
|
|
|
|
|
|
SDL_memset(&sRequestedAudioFormat, 0, sizeof(sRequestedAudioFormat));
|
|
|
|
SDL_memset(&sAudioFormat, 0, sizeof(sAudioFormat));
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Set up format and callback. Play 16-bit stereo audio at 44.1Khz.
|
2020-12-20 23:23:22 +00:00
|
|
|
sRequestedAudioFormat.freq = 44100;
|
|
|
|
sRequestedAudioFormat.format = AUDIO_S16;
|
|
|
|
sRequestedAudioFormat.channels = 2;
|
|
|
|
sRequestedAudioFormat.samples = 4096;
|
|
|
|
sRequestedAudioFormat.callback = mixAudio;
|
|
|
|
sRequestedAudioFormat.userdata = nullptr;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < SDL_GetNumAudioDevices(0); i++) {
|
|
|
|
LOG(LogDebug) << "Detected playback device '" << SDL_GetAudioDeviceName(i, 0) << "'.";
|
|
|
|
}
|
|
|
|
|
|
|
|
sAudioDevice = SDL_OpenAudioDevice(0, 0, &sRequestedAudioFormat, &sAudioFormat,
|
|
|
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
|
|
|
|
|
|
|
if (sAudioDevice == 0) {
|
|
|
|
LOG(LogError) << "Unable to open audio device: " << SDL_GetError() << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sAudioFormat.freq != sRequestedAudioFormat.freq) {
|
|
|
|
LOG(LogDebug) << "AudioManager::init(): Requested frequency 44100 could not be "
|
|
|
|
"set, obtained " << std::to_string(sAudioFormat.freq) << ".";
|
|
|
|
}
|
|
|
|
if (sAudioFormat.format != sRequestedAudioFormat.format) {
|
|
|
|
LOG(LogDebug) << "AudioManager::init(): Requested format " << AUDIO_S16 << " could not be "
|
|
|
|
"set, obtained " << std::to_string(sAudioFormat.format) << ".";
|
|
|
|
}
|
|
|
|
if (sAudioFormat.channels != sRequestedAudioFormat.channels) {
|
|
|
|
LOG(LogDebug) << "AudioManager::init(): Requested channel count 2 could not be "
|
|
|
|
"set, obtained " << std::to_string(sAudioFormat.channels) << ".";
|
|
|
|
}
|
|
|
|
if (sAudioFormat.samples != sRequestedAudioFormat.samples) {
|
|
|
|
LOG(LogDebug) << "AudioManager::init(): Requested sample buffer size 4096 could not be "
|
|
|
|
"set, obtained " << std::to_string(sAudioFormat.samples) << ".";
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2020-12-20 15:41:58 +00:00
|
|
|
|
|
|
|
// Just in case someone changed the es_settings.cfg file manually to invalid values.
|
|
|
|
if (Settings::getInstance()->getInt("SoundVolumeNavigation") > 100)
|
|
|
|
Settings::getInstance()->setInt("SoundVolumeNavigation", 100);
|
|
|
|
if (Settings::getInstance()->getInt("SoundVolumeNavigation") < 0)
|
|
|
|
Settings::getInstance()->setInt("SoundVolumeNavigation", 0);
|
|
|
|
if (Settings::getInstance()->getInt("SoundVolumeVideos") > 100)
|
|
|
|
Settings::getInstance()->setInt("SoundVolumeVideos", 100);
|
|
|
|
if (Settings::getInstance()->getInt("SoundVolumeVideos") < 0)
|
|
|
|
Settings::getInstance()->setInt("SoundVolumeVideos", 0);
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
|
|
|
|
2013-05-21 08:40:01 +00:00
|
|
|
void AudioManager::deinit()
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Stop all playback.
|
|
|
|
stop();
|
|
|
|
// Completely tear down SDL audio. else SDL hogs audio resources and
|
|
|
|
// emulators might fail to start...
|
|
|
|
SDL_CloseAudio();
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
|
|
sInstance = nullptr;
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
void AudioManager::registerSound(std::shared_ptr<Sound>& sound)
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
sSoundVector.push_back(sound);
|
2013-05-14 19:31:39 +00:00
|
|
|
}
|
2012-10-13 18:29:53 +00:00
|
|
|
|
2020-10-18 17:45:26 +00:00
|
|
|
void AudioManager::unregisterSound(std::shared_ptr<Sound>& sound)
|
2013-05-14 19:31:39 +00:00
|
|
|
{
|
2020-07-13 18:58:25 +00:00
|
|
|
for (unsigned int i = 0; i < sSoundVector.size(); i++) {
|
|
|
|
if (sSoundVector.at(i) == sound) {
|
2020-06-21 12:25:28 +00:00
|
|
|
sSoundVector[i]->stop();
|
|
|
|
sSoundVector.erase(sSoundVector.cbegin() + i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-07-26 21:30:45 +00:00
|
|
|
LOG(LogError) << "AudioManager - tried to unregister a sound that wasn't registered!";
|
2012-10-13 18:29:53 +00:00
|
|
|
}
|
2013-05-14 19:31:39 +00:00
|
|
|
|
|
|
|
void AudioManager::play()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Unpause audio, the mixer will figure out if samples need to be played...
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_PauseAudioDevice(sAudioDevice, 0);
|
2013-05-22 17:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioManager::stop()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Stop playing all Sounds.
|
2020-07-13 18:58:25 +00:00
|
|
|
for (unsigned int i = 0; i < sSoundVector.size(); i++) {
|
|
|
|
if (sSoundVector.at(i)->isPlaying())
|
2020-06-21 12:25:28 +00:00
|
|
|
sSoundVector[i]->stop();
|
|
|
|
}
|
2020-07-13 18:58:25 +00:00
|
|
|
// Pause audio.
|
2020-12-20 23:23:22 +00:00
|
|
|
SDL_PauseAudioDevice(sAudioDevice, 1);
|
2013-05-22 17:11:10 +00:00
|
|
|
}
|