diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index dac7223ba1..9aa200641a 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -29,9 +29,7 @@ #ifdef SDL #include "SDL/SDLJoystick.h" #include "SDL_audio.h" -#define MainAudioDeviceID SDL_AudioDeviceID #endif -#define MainAudioDeviceID int #include "QtMain.h" #include "gfx_es2/gpu_features.h" #include "i18n/i18n.h" @@ -49,9 +47,55 @@ static int browseFileEvent = -1; static int browseFolderEvent = -1; #ifdef SDL +static SDL_AudioDeviceID audioDev = 0; + extern void mixaudio(void *userdata, Uint8 *stream, int len) { NativeMix((short *)stream, len / 4); } + +static void InitSDLAudioDevice() { + SDL_AudioSpec fmt, ret_fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.freq = 44100; + fmt.format = AUDIO_S16; + fmt.channels = 2; + fmt.samples = 2048; + fmt.callback = &mixaudio; + fmt.userdata = nullptr; + + audioDev = 0; + if (!g_Config.sAudioDevice.empty()) { + audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (audioDev <= 0) { + WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str()); + } + } + if (audioDev <= 0) { + audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + } + if (audioDev <= 0) { + ELOG("Failed to open audio: %s", SDL_GetError()); + } else { + if (ret_fmt.samples != fmt.samples) // Notify, but still use it + ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); + if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { + ELOG("Sound buffer format does not match requested format."); + ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); + ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); + ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); + ELOG("Provided output format does not match requirement, turning audio off"); + SDL_CloseAudioDevice(audioDev); + } + SDL_PauseAudioDevice(audioDev, 0); + } +} + +static void StopSDLAudioDevice() { + if (audioDev > 0) { + SDL_PauseAudioDevice(audioDev, 1); + SDL_CloseAudioDevice(audioDev); + } +} #endif std::string System_GetProperty(SystemProperty prop) { @@ -72,6 +116,26 @@ std::string System_GetProperty(SystemProperty prop) { return QLocale::system().name().toStdString(); case SYSPROP_CLIPBOARD_TEXT: return QApplication::clipboard()->text().toStdString(); +#if defined(SDL) + case SYSPROP_AUDIO_DEVICE_LIST: + { + std::string result; + for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) { + const char *name = SDL_GetAudioDeviceName(i, 0); + if (!name) { + continue; + } + + if (i == 0) { + result = name; + } else { + result.append(1, '\0'); + result.append(name); + } + } + return result; + } +#endif default: return ""; } @@ -129,6 +193,11 @@ void System_SendMessage(const char *command, const char *parameter) { qApp->exit(0); } else if (!strcmp(command, "setclipboardtext")) { QApplication::clipboard()->setText(parameter); +#if defined(SDL) + } else if (!strcmp(command, "audio_resetDevice")) { + StopSDLAudioDevice(); + InitSDLAudioDevice(); +#endif } } @@ -170,7 +239,7 @@ float CalculateDPIScale() #endif } -static int mainInternal(QApplication &a, MainAudioDeviceID &audioDev) { +static int mainInternal(QApplication &a) { #ifdef MOBILE_DEVICE emugl = new MainUI(); emugl->resize(pixel_xres, pixel_yres); @@ -187,40 +256,7 @@ static int mainInternal(QApplication &a, MainAudioDeviceID &audioDev) { SDLJoystick joy(true); joy.registerEventHandler(); SDL_Init(SDL_INIT_AUDIO); - SDL_AudioSpec fmt, ret_fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.freq = 44100; - fmt.format = AUDIO_S16; - fmt.channels = 2; - fmt.samples = 2048; - fmt.callback = &mixaudio; - fmt.userdata = (void *)0; - - audioDev = 0; - if (!g_Config.sAudioDevice.empty()) { - audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - if (audioDev <= 0) { - WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str()); - } - } - if (audioDev <= 0) { - audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - } - if (audioDev <= 0) { - ELOG("Failed to open audio: %s", SDL_GetError()); - } else { - if (ret_fmt.samples != fmt.samples) // Notify, but still use it - ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); - if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { - ELOG("Sound buffer format does not match requested format."); - ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); - ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); - ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); - ELOG("Provided output format does not match requirement, turning audio off"); - SDL_CloseAudioDevice(audioDev); - } - SDL_PauseAudioDevice(audioDev, 0); - } + InitSDLAudioDevice(); #else QScopedPointer audio(new MainAudio()); audio->run(); @@ -620,8 +656,7 @@ int main(int argc, char *argv[]) // TODO: Support other backends than GL, like Vulkan, in the Qt backend. g_Config.iGPUBackend = (int)GPUBackend::OPENGL; - MainAudioDeviceID audioDev = 0; - int ret = mainInternal(a, audioDev); + int ret = mainInternal(a); ILOG("Left mainInternal here."); #ifdef SDL diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index ded43ae31c..7ff551d7a1 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -81,6 +81,57 @@ int getDisplayNumber(void) { return displayNumber; } +extern void mixaudio(void *userdata, Uint8 *stream, int len) { + NativeMix((short *)stream, len / 4); +} + +static SDL_AudioDeviceID audioDev = 0; + +// Must be called after NativeInit(). +static void InitSDLAudioDevice() { + SDL_AudioSpec fmt, ret_fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.freq = 44100; + fmt.format = AUDIO_S16; + fmt.channels = 2; + fmt.samples = 2048; + fmt.callback = &mixaudio; + fmt.userdata = nullptr; + + audioDev = 0; + if (!g_Config.sAudioDevice.empty()) { + audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (audioDev <= 0) { + WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str()); + } + } + if (audioDev <= 0) { + audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + } + if (audioDev <= 0) { + ELOG("Failed to open audio: %s", SDL_GetError()); + } else { + if (ret_fmt.samples != fmt.samples) // Notify, but still use it + ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); + if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { + ELOG("Sound buffer format does not match requested format."); + ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); + ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); + ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); + ELOG("Provided output format does not match requirement, turning audio off"); + SDL_CloseAudioDevice(audioDev); + } + SDL_PauseAudioDevice(audioDev, 0); + } +} + +static void StopSDLAudioDevice() { + if (audioDev > 0) { + SDL_PauseAudioDevice(audioDev, 1); + SDL_CloseAudioDevice(audioDev); + } +} + // Simple implementations of System functions @@ -120,6 +171,9 @@ void System_SendMessage(const char *command, const char *parameter) { g_QuitRequested = true; } else if (!strcmp(command, "setclipboardtext")) { SDL_SetClipboardText(parameter); + } else if (!strcmp(command, "audio_resetDevice")) { + StopSDLAudioDevice(); + InitSDLAudioDevice(); } } @@ -221,6 +275,24 @@ std::string System_GetProperty(SystemProperty prop) { } case SYSPROP_CLIPBOARD_TEXT: return SDL_HasClipboardText() ? SDL_GetClipboardText() : ""; + case SYSPROP_AUDIO_DEVICE_LIST: + { + std::string result; + for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) { + const char *name = SDL_GetAudioDeviceName(i, 0); + if (!name) { + continue; + } + + if (i == 0) { + result = name; + } else { + result.append(1, '\0'); + result.append(name); + } + } + return result; + } default: return ""; } @@ -258,10 +330,6 @@ bool System_GetPropertyBool(SystemProperty prop) { } } -extern void mixaudio(void *userdata, Uint8 *stream, int len) { - NativeMix((short *)stream, len / 4); -} - // returns -1 on failure static int parseInt(const char *str) { int val; @@ -573,42 +641,7 @@ int main(int argc, char *argv[]) { // Ensure that the swap interval is set after context creation (needed for kmsdrm) SDL_GL_SetSwapInterval(1); - SDL_AudioSpec fmt, ret_fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.freq = 44100; - fmt.format = AUDIO_S16; - fmt.channels = 2; - fmt.samples = 2048; - fmt.callback = &mixaudio; - fmt.userdata = (void *)0; - - SDL_AudioDeviceID audioDev = 0; - if (!g_Config.sAudioDevice.empty()) { - audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - if (audioDev <= 0) { - WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str()); - } - } - if (audioDev <= 0) { - audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - } - if (audioDev <= 0) { - ELOG("Failed to open audio: %s", SDL_GetError()); - } else { - if (ret_fmt.samples != fmt.samples) // Notify, but still use it - ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); - if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { - ELOG("Sound buffer format does not match requested format."); - ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); - ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); - ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); - ELOG("Provided output format does not match requirement, turning audio off"); - SDL_CloseAudioDevice(audioDev); - } - - // Audio must be unpaused _after_ NativeInit() - SDL_PauseAudioDevice(audioDev, 0); - } + InitSDLAudioDevice(); if (joystick_enabled) { joystick = new SDLJoystick(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7645fc9b7e..abf7284728 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -20,6 +20,7 @@ #include "base/display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres #include "base/colorutil.h" +#include "base/stringutil.h" #include "base/timeutil.h" #include "math/curves.h" #include "net/resolve.h" @@ -491,6 +492,13 @@ void GameSettingsScreen::CreateViews() { } #endif +#if defined(SDL) + std::vector audioDeviceList; + SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList); + PopupMultiChoiceDynamic *audioDevice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sAudioDevice, a->T("Device"), audioDeviceList, nullptr, screenManager())); + audioDevice->OnChoice.Handle(this, &GameSettingsScreen::OnAudioDevice); +#endif + static const char *latency[] = { "Low", "Medium", "High" }; PopupMultiChoice *lowAudio = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioLatency, a->T("Audio Latency"), latency, 0, ARRAY_SIZE(latency), gr->GetName(), screenManager())); lowAudio->SetEnabledPtr(&g_Config.bEnableSound); @@ -1180,6 +1188,11 @@ UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { + System_SendMessage("audio_resetDevice", ""); + return UI::EVENT_DONE; +} + UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) const size_t name_len = 256; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index cde4e47409..e4b6232e5e 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -97,6 +97,7 @@ private: UI::EventReturn OnRenderingMode(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRenderingDevice(UI::EventParams &e); + UI::EventReturn OnAudioDevice(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); #if PPSSPP_PLATFORM(ANDROID) UI::EventReturn OnChangeMemStickDir(UI::EventParams &e); diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index 87978dea4d..623332d37e 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -163,6 +163,9 @@ enum SystemProperty { SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE, SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER, + // Exposed on SDL. + SYSPROP_AUDIO_DEVICE_LIST, + SYSPROP_SUPPORTS_PERMISSIONS, SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE, };