Skip to content
Permalink
Browse files

Merge pull request #12327 from unknownbrackets/audio-device

Use SDL2 audio device API, switch on new device
  • Loading branch information...
hrydgard committed Oct 8, 2019
2 parents c0aa759 + a1ee226 commit 56e2e0845ee3037ab8025547888c323aa7e55b45
Showing with 215 additions and 68 deletions.
  1. +1 −1 CMakeLists.txt
  2. +2 −0 Core/Config.cpp
  3. +2 −0 Core/Config.h
  4. +76 −26 Qt/QtMain.cpp
  5. +105 −31 SDL/SDLMain.cpp
  6. +5 −5 SDL/macbundle.sh
  7. +20 −0 UI/GameSettingsScreen.cpp
  8. +1 −0 UI/GameSettingsScreen.h
  9. +0 −5 b.sh
  10. +3 −0 ext/native/base/NativeApp.h
@@ -1996,7 +1996,7 @@ if(TargetBin)
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource} "ios/Launch Screen.storyboard")
else()
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource})
if(TARGET SDL2::SDL2)
if(TARGET SDL2::SDL2 AND NOT USING_QT_UI)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPSDL.app")
endif()
endif()
@@ -778,6 +778,8 @@ static ConfigSetting soundSettings[] = {
ConfigSetting("AudioResampler", &g_Config.bAudioResampler, true, true, true),
ConfigSetting("GlobalVolume", &g_Config.iGlobalVolume, VOLUME_MAX, true, true),
ConfigSetting("AltSpeedVolume", &g_Config.iAltSpeedVolume, -1, true, true),
ConfigSetting("AudioDevice", &g_Config.sAudioDevice, "", true, false),
ConfigSetting("AutoAudioDevice", &g_Config.bAutoAudioDevice, true, true, false),

ConfigSetting(false),
};
@@ -197,6 +197,8 @@ struct Config {
int iGlobalVolume;
int iAltSpeedVolume;
bool bExtraAudioBuffering; // For bluetooth
std::string sAudioDevice;
bool bAutoAudioDevice;

// UI
bool bShowDebuggerOnLoad;
@@ -47,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) {
@@ -70,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 "";
}
@@ -127,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
}
}

@@ -185,30 +256,7 @@ static int mainInternal(QApplication &a) {
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;

if (SDL_OpenAudio(&fmt, &ret_fmt) < 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_CloseAudio();
}
}
SDL_PauseAudio(0);
InitSDLAudioDevice();
#else
QScopedPointer<MainAudio> audio(new MainAudio());
audio->run();
@@ -612,8 +660,10 @@ int main(int argc, char *argv[])
ILOG("Left mainInternal here.");

#ifdef SDL
SDL_PauseAudio(1);
SDL_CloseAudio();
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
}
#endif
NativeShutdown();
glslang::FinalizeProcess();
@@ -81,6 +81,62 @@ 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(const std::string &name = "") {
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;

std::string startDevice = name;
if (startDevice.empty()) {
startDevice = g_Config.sAudioDevice;
}

audioDev = 0;
if (!startDevice.empty()) {
audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (audioDev <= 0) {
WLOG("Failed to open audio device: %s", startDevice.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 +176,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 +280,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 +335,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,32 +646,8 @@ 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;

if (SDL_OpenAudio(&fmt, &ret_fmt) < 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_CloseAudio();
}
}
InitSDLAudioDevice();

// Audio must be unpaused _after_ NativeInit()
SDL_PauseAudio(0);
if (joystick_enabled) {
joystick = new SDLJoystick();
} else {
@@ -852,6 +901,29 @@ int main(int argc, char *argv[]) {
break;
}
break;

#if SDL_VERSION_ATLEAST(2, 0, 4)
case SDL_AUDIODEVICEADDED:
// Automatically switch to the new device.
if (event.adevice.iscapture == 0) {
const char *name = SDL_GetAudioDeviceName(event.adevice.which, 0);
if (!name) {
break;
}
if (g_Config.bAutoAudioDevice || g_Config.sAudioDevice == name) {
StopSDLAudioDevice();
InitSDLAudioDevice(name ? name : "");
}
}
break;
case SDL_AUDIODEVICEREMOVED:
if (event.adevice.iscapture == 0 && event.adevice.which == audioDev) {
StopSDLAudioDevice();
InitSDLAudioDevice();
}
break;
#endif

default:
if (joystick) {
joystick->ProcessInput(event);
@@ -927,8 +999,10 @@ int main(int argc, char *argv[]) {
graphicsContext->ShutdownFromRenderThread();
delete graphicsContext;

SDL_PauseAudio(1);
SDL_CloseAudio();
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
}
SDL_Quit();
#if PPSSPP_PLATFORM(RPI)
bcm_host_deinit();
@@ -5,7 +5,7 @@ PPSSPPSDL="${PPSSPP}/Contents/MacOS/PPSSPPSDL"

if [ ! -f "${PPSSPPSDL}" ]; then
echo "No such file: ${PPSSPPSDL}!"
exit 1
exit 0
fi

SDL=$(otool -L "${PPSSPPSDL}" | grep -v @executable_path | grep -Eo /.+libSDL.+dylib)
@@ -16,12 +16,12 @@ fi

if [ ! -f "${SDL}" ]; then
echo "Cannot locate SDL: ${SDL}!"
exit 1
exit 0
fi

echo "Installing SDL from ${SDL}..."

SDLNAME=$(basename "${SDL}")
mkdir -p "${PPSSPP}/Contents/Frameworks" || exit 1
cp -r "$SDL" "${PPSSPP}/Contents/Frameworks" || exit 1
install_name_tool -change "${SDL}" "@executable_path/../Frameworks/${SDLNAME}" "${PPSSPPSDL}" || exit 1
mkdir -p "${PPSSPP}/Contents/Frameworks" || exit 0
cp -r "$SDL" "${PPSSPP}/Contents/Frameworks" || exit 0
install_name_tool -change "${SDL}" "@executable_path/../Frameworks/${SDLNAME}" "${PPSSPPSDL}" || exit 0

0 comments on commit 56e2e08

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