mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-31 04:25:40 +00:00
Complete overhaul of VolumeControl with fixes for some related bugs.
This commit is contained in:
parent
3e9e592c3b
commit
87e6837980
|
@ -24,7 +24,6 @@
|
|||
#include "Platform.h"
|
||||
#include "Scripting.h"
|
||||
#include "SystemData.h"
|
||||
#include "VolumeControl.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
|
||||
// The ALSA Audio Card and Audio Device selection code is disabled at the moment.
|
||||
// As PulseAudio controls the sound devices for the desktop environment, it doesn't
|
||||
// make much sense to be able to select ALSA devices directly. Normally (always?)
|
||||
// the selection doesn't make any difference at all. But maybe some PulseAudio
|
||||
// settings could be added later on, if needed.
|
||||
// make much sense to be able to select ALSA devices directly.
|
||||
// The code is still active for Raspberry Pi though as I'm not sure if this is
|
||||
// useful for that device.
|
||||
// Keeping mixerName and mixerCard at their default values should make sure that
|
||||
|
@ -37,82 +35,51 @@ std::string VolumeControl::mixerName = "Master";
|
|||
std::string VolumeControl::mixerCard = "default";
|
||||
#endif
|
||||
|
||||
std::weak_ptr<VolumeControl> VolumeControl::sInstance;
|
||||
VolumeControl* VolumeControl::sInstance = nullptr;
|
||||
|
||||
VolumeControl::VolumeControl()
|
||||
: originalVolume(0),
|
||||
internalVolume(0)
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
, mixerIndex(0),
|
||||
#if defined(__linux__)
|
||||
: mixerIndex(0),
|
||||
mixerHandle(nullptr),
|
||||
mixerElem(nullptr),
|
||||
mixerSelemId(nullptr)
|
||||
#elif defined(_WIN64)
|
||||
, mixerHandle(nullptr),
|
||||
: mixerHandle(nullptr),
|
||||
endpointVolume(nullptr)
|
||||
#endif
|
||||
{
|
||||
init();
|
||||
|
||||
// Get original volume levels for system.
|
||||
originalVolume = getVolume();
|
||||
}
|
||||
|
||||
VolumeControl::VolumeControl(
|
||||
const VolumeControl& right):
|
||||
originalVolume(0),
|
||||
internalVolume(0)
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
, mixerIndex(0),
|
||||
mixerHandle(nullptr),
|
||||
mixerElem(nullptr),
|
||||
mixerSelemId(nullptr)
|
||||
#elif defined(_WIN64)
|
||||
, mixerHandle(nullptr),
|
||||
endpointVolume(nullptr)
|
||||
#endif
|
||||
{
|
||||
static_cast<void>(right);
|
||||
sInstance = right.sInstance;
|
||||
}
|
||||
|
||||
VolumeControl& VolumeControl::operator=(const VolumeControl& right)
|
||||
{
|
||||
if (this != &right)
|
||||
sInstance = right.sInstance;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VolumeControl::~VolumeControl()
|
||||
{
|
||||
// Set original volume levels for system.
|
||||
//setVolume(originalVolume);
|
||||
|
||||
deinit();
|
||||
#if defined(__linux__)
|
||||
snd_config_update_free_global();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<VolumeControl>& VolumeControl::getInstance()
|
||||
VolumeControl* VolumeControl::getInstance()
|
||||
{
|
||||
// Check if an VolumeControl instance is already created, if not then create it.
|
||||
static std::shared_ptr<VolumeControl> sharedInstance = sInstance.lock();
|
||||
if (sharedInstance == nullptr) {
|
||||
sharedInstance.reset(new VolumeControl);
|
||||
sInstance = sharedInstance;
|
||||
// Check if a VolumeControl instance is already created, and if not then create it.
|
||||
if (!sInstance)
|
||||
sInstance = new VolumeControl();
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void VolumeControl::deleteInstance()
|
||||
{
|
||||
if (sInstance) {
|
||||
delete sInstance;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
void VolumeControl::init()
|
||||
{
|
||||
// Initialize audio mixer interface.
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
// Try to open mixer device.
|
||||
if (mixerHandle == nullptr) {
|
||||
// Allow user to override the AudioCard and AudioDevice in es_settings.cfg.
|
||||
|
@ -125,22 +92,17 @@ void VolumeControl::init()
|
|||
// Sets simple-mixer index and name.
|
||||
snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex);
|
||||
snd_mixer_selem_id_set_name(mixerSelemId, mixerName.c_str());
|
||||
// Open mixer.
|
||||
if (snd_mixer_open(&mixerHandle, 0) >= 0) {
|
||||
LOG(LogDebug) << "VolumeControl::init(): Opened ALSA mixer";
|
||||
// Ok, attach to defualt card.
|
||||
if (snd_mixer_attach(mixerHandle, mixerCard.c_str()) >= 0) {
|
||||
LOG(LogDebug) << "VolumeControl::init(): Attached to default card";
|
||||
// Ok, register simple element class.
|
||||
if (snd_mixer_selem_register(mixerHandle, nullptr, nullptr) >= 0) {
|
||||
LOG(LogDebug) << "VolumeControl::init(): Registered simple element class";
|
||||
// Ok, load registered elements.
|
||||
if (snd_mixer_load(mixerHandle) >= 0) {
|
||||
LOG(LogDebug) << "VolumeControl::init(): Loaded mixer elements";
|
||||
// Ok, find elements now.
|
||||
// Find elements.
|
||||
mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId);
|
||||
if (mixerElem != nullptr) {
|
||||
// Wohoo. good to go...
|
||||
LOG(LogDebug) << "VolumeControl::init(): Mixer initialized";
|
||||
}
|
||||
else {
|
||||
|
@ -213,14 +175,11 @@ void VolumeControl::init()
|
|||
void VolumeControl::deinit()
|
||||
{
|
||||
// Deinitialize audio mixer interface.
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (mixerHandle != nullptr) {
|
||||
snd_mixer_detach(mixerHandle, mixerCard.c_str());
|
||||
snd_mixer_free(mixerHandle);
|
||||
snd_mixer_close(mixerHandle);
|
||||
snd_config_update_free_global();
|
||||
mixerHandle = nullptr;
|
||||
mixerElem = nullptr;
|
||||
}
|
||||
|
@ -237,36 +196,31 @@ int VolumeControl::getVolume() const
|
|||
{
|
||||
int volume = 0;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (mixerElem != nullptr) {
|
||||
// Get volume range.
|
||||
long minVolume;
|
||||
long maxVolume;
|
||||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||
// Ok, now get volume.
|
||||
long rawVolume;
|
||||
if (snd_mixer_selem_get_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_MONO, &rawVolume) == 0) {
|
||||
// Worked. bring into range 0-100.
|
||||
// Bring into range 0-100.
|
||||
rawVolume -= minVolume;
|
||||
if (rawVolume > 0)
|
||||
volume = (rawVolume * 100.0) / (maxVolume - minVolume) + 0.5;
|
||||
//else
|
||||
// volume = 0;
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get mixer volume!";
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get mixer volume";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range!";
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range";
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
if (endpointVolume != nullptr) {
|
||||
// Windows Vista or above. uses EndpointVolume API.
|
||||
// Windows Vista or above, uses EndpointVolume API.
|
||||
float floatVolume = 0.0f; // 0-1
|
||||
if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) {
|
||||
volume = static_cast<int>(std::round(floatVolume * 100.0f));
|
||||
|
@ -278,38 +232,31 @@ int VolumeControl::getVolume() const
|
|||
}
|
||||
#endif
|
||||
|
||||
// Clamp to 0-100 range.
|
||||
volume = Math::clamp(volume, 0, 100);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
void VolumeControl::setVolume(int volume)
|
||||
{
|
||||
// Clamp to 0-100 range.
|
||||
volume = Math::clamp(volume, 0, 100);
|
||||
|
||||
// Store values in internal variables.
|
||||
internalVolume = volume;
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (mixerElem != nullptr) {
|
||||
// Get volume range.
|
||||
long minVolume;
|
||||
long maxVolume;
|
||||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||
// Ok, bring into minVolume-maxVolume range and set.
|
||||
// Bring into minVolume-maxVolume range and set.
|
||||
long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume;
|
||||
if (snd_mixer_selem_set_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_FRONT_LEFT, rawVolume) < 0 ||
|
||||
snd_mixer_selem_set_playback_volume(mixerElem,
|
||||
SND_MIXER_SCHN_FRONT_RIGHT, rawVolume) < 0) {
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to set mixer volume!";
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to set mixer volume";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range!";
|
||||
LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range";
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN64)
|
||||
|
@ -319,7 +266,7 @@ void VolumeControl::setVolume(int volume)
|
|||
if (volume > 0)
|
||||
floatVolume = static_cast<float>(volume) / 100.0f;
|
||||
if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK)
|
||||
LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume!";
|
||||
LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
//#error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
@ -21,15 +19,26 @@
|
|||
#include <Windows.h>
|
||||
#include <endpointvolume.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
// Singleton pattern. Call getInstance() to get an object.
|
||||
class VolumeControl
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
// #error TODO: Not implemented for MacOS yet!!!
|
||||
#elif defined(__linux__)
|
||||
public:
|
||||
VolumeControl();
|
||||
~VolumeControl();
|
||||
|
||||
static VolumeControl* getInstance();
|
||||
void deleteInstance();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
int getVolume() const;
|
||||
void setVolume(int volume);
|
||||
|
||||
static VolumeControl* sInstance;
|
||||
|
||||
#if defined(__linux__)
|
||||
static std::string mixerName;
|
||||
static std::string mixerCard;
|
||||
int mixerIndex;
|
||||
|
@ -41,26 +50,6 @@ class VolumeControl
|
|||
MIXERCONTROL mixerControl;
|
||||
IAudioEndpointVolume * endpointVolume;
|
||||
#endif
|
||||
|
||||
int originalVolume;
|
||||
int internalVolume;
|
||||
|
||||
static std::weak_ptr<VolumeControl> sInstance;
|
||||
|
||||
VolumeControl();
|
||||
VolumeControl(const VolumeControl & right);
|
||||
VolumeControl & operator=(const VolumeControl & right);
|
||||
|
||||
public:
|
||||
static std::shared_ptr<VolumeControl> & getInstance();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
int getVolume() const;
|
||||
void setVolume(int volume);
|
||||
|
||||
~VolumeControl();
|
||||
};
|
||||
|
||||
#endif // ES_APP_VOLUME_CONTROL_H
|
||||
|
|
|
@ -513,6 +513,10 @@ void GuiMenu::openSoundSettings()
|
|||
s->addSaveFunc([system_volume] {
|
||||
VolumeControl::getInstance()->
|
||||
setVolume(static_cast<int>(std::round(system_volume->getValue())));
|
||||
// Explicitly delete the VolumeControl instance so that it will reinitialize the
|
||||
// next time the menu is entered. This is the easiest way to detect new default
|
||||
// audio devices or changes in audio volume done by the operating system.
|
||||
VolumeControl::getInstance()->deleteInstance();
|
||||
});
|
||||
#endif
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ AudioManager::~AudioManager()
|
|||
|
||||
std::shared_ptr<AudioManager>& AudioManager::getInstance()
|
||||
{
|
||||
// Check if an AudioManager instance is already created, if not, create it.
|
||||
// Check if an AudioManager instance is already created, and if not then create it.
|
||||
if (sInstance == nullptr)
|
||||
sInstance = std::shared_ptr<AudioManager>(new AudioManager);
|
||||
|
||||
|
|
Loading…
Reference in a new issue