Skip to content

Commit 56e2e08

Browse files
authored
Merge pull request #12327 from unknownbrackets/audio-device
Use SDL2 audio device API, switch on new device
2 parents c0aa759 + a1ee226 commit 56e2e08

10 files changed

+215
-68
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ if(TargetBin)
19961996
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource} "ios/Launch Screen.storyboard")
19971997
else()
19981998
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource})
1999-
if(TARGET SDL2::SDL2)
1999+
if(TARGET SDL2::SDL2 AND NOT USING_QT_UI)
20002000
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPSDL.app")
20012001
endif()
20022002
endif()

Core/Config.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,8 @@ static ConfigSetting soundSettings[] = {
778778
ConfigSetting("AudioResampler", &g_Config.bAudioResampler, true, true, true),
779779
ConfigSetting("GlobalVolume", &g_Config.iGlobalVolume, VOLUME_MAX, true, true),
780780
ConfigSetting("AltSpeedVolume", &g_Config.iAltSpeedVolume, -1, true, true),
781+
ConfigSetting("AudioDevice", &g_Config.sAudioDevice, "", true, false),
782+
ConfigSetting("AutoAudioDevice", &g_Config.bAutoAudioDevice, true, true, false),
781783

782784
ConfigSetting(false),
783785
};

Core/Config.h

+2
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ struct Config {
197197
int iGlobalVolume;
198198
int iAltSpeedVolume;
199199
bool bExtraAudioBuffering; // For bluetooth
200+
std::string sAudioDevice;
201+
bool bAutoAudioDevice;
200202

201203
// UI
202204
bool bShowDebuggerOnLoad;

Qt/QtMain.cpp

+76-26
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,55 @@ static int browseFileEvent = -1;
4747
static int browseFolderEvent = -1;
4848

4949
#ifdef SDL
50+
static SDL_AudioDeviceID audioDev = 0;
51+
5052
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
5153
NativeMix((short *)stream, len / 4);
5254
}
55+
56+
static void InitSDLAudioDevice() {
57+
SDL_AudioSpec fmt, ret_fmt;
58+
memset(&fmt, 0, sizeof(fmt));
59+
fmt.freq = 44100;
60+
fmt.format = AUDIO_S16;
61+
fmt.channels = 2;
62+
fmt.samples = 2048;
63+
fmt.callback = &mixaudio;
64+
fmt.userdata = nullptr;
65+
66+
audioDev = 0;
67+
if (!g_Config.sAudioDevice.empty()) {
68+
audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
69+
if (audioDev <= 0) {
70+
WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str());
71+
}
72+
}
73+
if (audioDev <= 0) {
74+
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
75+
}
76+
if (audioDev <= 0) {
77+
ELOG("Failed to open audio: %s", SDL_GetError());
78+
} else {
79+
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
80+
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
81+
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
82+
ELOG("Sound buffer format does not match requested format.");
83+
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
84+
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
85+
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
86+
ELOG("Provided output format does not match requirement, turning audio off");
87+
SDL_CloseAudioDevice(audioDev);
88+
}
89+
SDL_PauseAudioDevice(audioDev, 0);
90+
}
91+
}
92+
93+
static void StopSDLAudioDevice() {
94+
if (audioDev > 0) {
95+
SDL_PauseAudioDevice(audioDev, 1);
96+
SDL_CloseAudioDevice(audioDev);
97+
}
98+
}
5399
#endif
54100

55101
std::string System_GetProperty(SystemProperty prop) {
@@ -70,6 +116,26 @@ std::string System_GetProperty(SystemProperty prop) {
70116
return QLocale::system().name().toStdString();
71117
case SYSPROP_CLIPBOARD_TEXT:
72118
return QApplication::clipboard()->text().toStdString();
119+
#if defined(SDL)
120+
case SYSPROP_AUDIO_DEVICE_LIST:
121+
{
122+
std::string result;
123+
for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) {
124+
const char *name = SDL_GetAudioDeviceName(i, 0);
125+
if (!name) {
126+
continue;
127+
}
128+
129+
if (i == 0) {
130+
result = name;
131+
} else {
132+
result.append(1, '\0');
133+
result.append(name);
134+
}
135+
}
136+
return result;
137+
}
138+
#endif
73139
default:
74140
return "";
75141
}
@@ -127,6 +193,11 @@ void System_SendMessage(const char *command, const char *parameter) {
127193
qApp->exit(0);
128194
} else if (!strcmp(command, "setclipboardtext")) {
129195
QApplication::clipboard()->setText(parameter);
196+
#if defined(SDL)
197+
} else if (!strcmp(command, "audio_resetDevice")) {
198+
StopSDLAudioDevice();
199+
InitSDLAudioDevice();
200+
#endif
130201
}
131202
}
132203

@@ -185,30 +256,7 @@ static int mainInternal(QApplication &a) {
185256
SDLJoystick joy(true);
186257
joy.registerEventHandler();
187258
SDL_Init(SDL_INIT_AUDIO);
188-
SDL_AudioSpec fmt, ret_fmt;
189-
memset(&fmt, 0, sizeof(fmt));
190-
fmt.freq = 44100;
191-
fmt.format = AUDIO_S16;
192-
fmt.channels = 2;
193-
fmt.samples = 2048;
194-
fmt.callback = &mixaudio;
195-
fmt.userdata = (void *)0;
196-
197-
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
198-
ELOG("Failed to open audio: %s", SDL_GetError());
199-
} else {
200-
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
201-
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
202-
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
203-
ELOG("Sound buffer format does not match requested format.");
204-
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
205-
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
206-
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
207-
ELOG("Provided output format does not match requirement, turning audio off");
208-
SDL_CloseAudio();
209-
}
210-
}
211-
SDL_PauseAudio(0);
259+
InitSDLAudioDevice();
212260
#else
213261
QScopedPointer<MainAudio> audio(new MainAudio());
214262
audio->run();
@@ -612,8 +660,10 @@ int main(int argc, char *argv[])
612660
ILOG("Left mainInternal here.");
613661

614662
#ifdef SDL
615-
SDL_PauseAudio(1);
616-
SDL_CloseAudio();
663+
if (audioDev > 0) {
664+
SDL_PauseAudioDevice(audioDev, 1);
665+
SDL_CloseAudioDevice(audioDev);
666+
}
617667
#endif
618668
NativeShutdown();
619669
glslang::FinalizeProcess();

SDL/SDLMain.cpp

+105-31
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,62 @@ int getDisplayNumber(void) {
8181
return displayNumber;
8282
}
8383

84+
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
85+
NativeMix((short *)stream, len / 4);
86+
}
87+
88+
static SDL_AudioDeviceID audioDev = 0;
89+
90+
// Must be called after NativeInit().
91+
static void InitSDLAudioDevice(const std::string &name = "") {
92+
SDL_AudioSpec fmt, ret_fmt;
93+
memset(&fmt, 0, sizeof(fmt));
94+
fmt.freq = 44100;
95+
fmt.format = AUDIO_S16;
96+
fmt.channels = 2;
97+
fmt.samples = 2048;
98+
fmt.callback = &mixaudio;
99+
fmt.userdata = nullptr;
100+
101+
std::string startDevice = name;
102+
if (startDevice.empty()) {
103+
startDevice = g_Config.sAudioDevice;
104+
}
105+
106+
audioDev = 0;
107+
if (!startDevice.empty()) {
108+
audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
109+
if (audioDev <= 0) {
110+
WLOG("Failed to open audio device: %s", startDevice.c_str());
111+
}
112+
}
113+
if (audioDev <= 0) {
114+
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
115+
}
116+
if (audioDev <= 0) {
117+
ELOG("Failed to open audio: %s", SDL_GetError());
118+
} else {
119+
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
120+
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
121+
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
122+
ELOG("Sound buffer format does not match requested format.");
123+
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
124+
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
125+
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
126+
ELOG("Provided output format does not match requirement, turning audio off");
127+
SDL_CloseAudioDevice(audioDev);
128+
}
129+
SDL_PauseAudioDevice(audioDev, 0);
130+
}
131+
}
132+
133+
static void StopSDLAudioDevice() {
134+
if (audioDev > 0) {
135+
SDL_PauseAudioDevice(audioDev, 1);
136+
SDL_CloseAudioDevice(audioDev);
137+
}
138+
}
139+
84140
// Simple implementations of System functions
85141

86142

@@ -120,6 +176,9 @@ void System_SendMessage(const char *command, const char *parameter) {
120176
g_QuitRequested = true;
121177
} else if (!strcmp(command, "setclipboardtext")) {
122178
SDL_SetClipboardText(parameter);
179+
} else if (!strcmp(command, "audio_resetDevice")) {
180+
StopSDLAudioDevice();
181+
InitSDLAudioDevice();
123182
}
124183
}
125184

@@ -221,6 +280,24 @@ std::string System_GetProperty(SystemProperty prop) {
221280
}
222281
case SYSPROP_CLIPBOARD_TEXT:
223282
return SDL_HasClipboardText() ? SDL_GetClipboardText() : "";
283+
case SYSPROP_AUDIO_DEVICE_LIST:
284+
{
285+
std::string result;
286+
for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) {
287+
const char *name = SDL_GetAudioDeviceName(i, 0);
288+
if (!name) {
289+
continue;
290+
}
291+
292+
if (i == 0) {
293+
result = name;
294+
} else {
295+
result.append(1, '\0');
296+
result.append(name);
297+
}
298+
}
299+
return result;
300+
}
224301
default:
225302
return "";
226303
}
@@ -258,10 +335,6 @@ bool System_GetPropertyBool(SystemProperty prop) {
258335
}
259336
}
260337

261-
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
262-
NativeMix((short *)stream, len / 4);
263-
}
264-
265338
// returns -1 on failure
266339
static int parseInt(const char *str) {
267340
int val;
@@ -573,32 +646,8 @@ int main(int argc, char *argv[]) {
573646
// Ensure that the swap interval is set after context creation (needed for kmsdrm)
574647
SDL_GL_SetSwapInterval(1);
575648

576-
SDL_AudioSpec fmt, ret_fmt;
577-
memset(&fmt, 0, sizeof(fmt));
578-
fmt.freq = 44100;
579-
fmt.format = AUDIO_S16;
580-
fmt.channels = 2;
581-
fmt.samples = 2048;
582-
fmt.callback = &mixaudio;
583-
fmt.userdata = (void *)0;
584-
585-
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
586-
ELOG("Failed to open audio: %s", SDL_GetError());
587-
} else {
588-
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
589-
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
590-
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
591-
ELOG("Sound buffer format does not match requested format.");
592-
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
593-
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
594-
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
595-
ELOG("Provided output format does not match requirement, turning audio off");
596-
SDL_CloseAudio();
597-
}
598-
}
649+
InitSDLAudioDevice();
599650

600-
// Audio must be unpaused _after_ NativeInit()
601-
SDL_PauseAudio(0);
602651
if (joystick_enabled) {
603652
joystick = new SDLJoystick();
604653
} else {
@@ -852,6 +901,29 @@ int main(int argc, char *argv[]) {
852901
break;
853902
}
854903
break;
904+
905+
#if SDL_VERSION_ATLEAST(2, 0, 4)
906+
case SDL_AUDIODEVICEADDED:
907+
// Automatically switch to the new device.
908+
if (event.adevice.iscapture == 0) {
909+
const char *name = SDL_GetAudioDeviceName(event.adevice.which, 0);
910+
if (!name) {
911+
break;
912+
}
913+
if (g_Config.bAutoAudioDevice || g_Config.sAudioDevice == name) {
914+
StopSDLAudioDevice();
915+
InitSDLAudioDevice(name ? name : "");
916+
}
917+
}
918+
break;
919+
case SDL_AUDIODEVICEREMOVED:
920+
if (event.adevice.iscapture == 0 && event.adevice.which == audioDev) {
921+
StopSDLAudioDevice();
922+
InitSDLAudioDevice();
923+
}
924+
break;
925+
#endif
926+
855927
default:
856928
if (joystick) {
857929
joystick->ProcessInput(event);
@@ -927,8 +999,10 @@ int main(int argc, char *argv[]) {
927999
graphicsContext->ShutdownFromRenderThread();
9281000
delete graphicsContext;
9291001

930-
SDL_PauseAudio(1);
931-
SDL_CloseAudio();
1002+
if (audioDev > 0) {
1003+
SDL_PauseAudioDevice(audioDev, 1);
1004+
SDL_CloseAudioDevice(audioDev);
1005+
}
9321006
SDL_Quit();
9331007
#if PPSSPP_PLATFORM(RPI)
9341008
bcm_host_deinit();

SDL/macbundle.sh

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ PPSSPPSDL="${PPSSPP}/Contents/MacOS/PPSSPPSDL"
55

66
if [ ! -f "${PPSSPPSDL}" ]; then
77
echo "No such file: ${PPSSPPSDL}!"
8-
exit 1
8+
exit 0
99
fi
1010

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

1717
if [ ! -f "${SDL}" ]; then
1818
echo "Cannot locate SDL: ${SDL}!"
19-
exit 1
19+
exit 0
2020
fi
2121

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

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

0 commit comments

Comments
 (0)