Skip to content
Permalink
Browse files

SDL: Add UI to reset audio device at runtime.

  • Loading branch information...
unknownbrackets committed Sep 15, 2019
1 parent 8751316 commit c30bc4ee7d36789a59702c6f790cdd211b65c15b
Showing with 164 additions and 79 deletions.
  1. +74 −39 Qt/QtMain.cpp
  2. +73 −40 SDL/SDLMain.cpp
  3. +13 −0 UI/GameSettingsScreen.cpp
  4. +1 −0 UI/GameSettingsScreen.h
  5. +3 −0 ext/native/base/NativeApp.h
@@ -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<MainAudio> 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
@@ -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();
@@ -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<std::string> 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;
@@ -97,6 +97,7 @@ class GameSettingsScreen : public UIDialogScreenWithGameBackground {
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);
@@ -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,
};

0 comments on commit c30bc4e

Please sign in to comment.
You can’t perform that action at this time.