diff --git a/plugins/spu2-x/src/CMakeLists.txt b/plugins/spu2-x/src/CMakeLists.txt index b94715dd05..a5f693a7f1 100644 --- a/plugins/spu2-x/src/CMakeLists.txt +++ b/plugins/spu2-x/src/CMakeLists.txt @@ -97,8 +97,26 @@ set(spu2xLinuxHeaders Linux/Dialogs.h) # add additional include directories -include_directories(. - Linux) +include_directories(. Linux) + +### Don't use yet c11 feature. I will bump gcc requirement to 4.7 when 4.9 is out. +### Note: actually it might work on 4.6 too +# # If compiling on GCC Version 4.7 or higher we build SDLMod in C++11 mode +# if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") +# execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) +# if(GCC_VERSION VERSION_GREATER 4.7) +# set_source_files_properties(SndOut_SDL.cpp PROPERTIES COMPILE_FLAGS -std=c++11) +# message(STATUS "GCC has C++11 support for SDLAudioMod") +# endif(GCC_VERSION VERSION_GREATER 4.7) +# endif() + +if(SDL_FOUND) + add_definitions(-DSPU2X_SDL) + list(APPEND spu2xSources SndOut_SDL.cpp) + #elseif(SDL2_FOUND) + # add_definitions(-DSPU2X_SDL2) + # list(APPEND spu2xSources SndOut_SDL.cpp) +endif() # add library add_library(${Output} SHARED @@ -107,15 +125,13 @@ add_library(${Output} SHARED ${spu2xLinuxSources} ${spu2xLinuxHeaders}) - # link target with project internal libraries target_link_libraries(${Output} Utilities) -# link target with ALSA +# link target with various backend target_link_libraries(${Output} ${ALSA_LIBRARIES}) - -# link target with PortAudio target_link_libraries(${Output} ${PORTAUDIO_LIBRARIES}) +target_link_libraries(${Output} ${SDL_LIBRARY}) # link target with SoundTouch target_link_libraries(${Output} ${SOUNDTOUCH_LIBRARIES}) diff --git a/plugins/spu2-x/src/Linux/Config.cpp b/plugins/spu2-x/src/Linux/Config.cpp index 9e8f23853c..7a9be9872b 100644 --- a/plugins/spu2-x/src/Linux/Config.cpp +++ b/plugins/spu2-x/src/Linux/Config.cpp @@ -185,7 +185,8 @@ void DisplayDialog() mod_box = gtk_combo_box_new_text (); gtk_combo_box_append_text(GTK_COMBO_BOX(mod_box), "0 - No Sound (emulate SPU2 only)"); gtk_combo_box_append_text(GTK_COMBO_BOX(mod_box), "1 - PortAudio (cross-platform)"); - //gtk_combo_box_append_text(GTK_COMBO_BOX(mod_box), "2 - Alsa (probably doesn't work)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(mod_box), "2 - SDL Audio (recommanded for pulseaudio"); + //gtk_combo_box_append_text(GTK_COMBO_BOX(mod_box), "3 - Alsa (probably doesn't work)"); gtk_combo_box_set_active(GTK_COMBO_BOX(mod_box), OutputModule); api_label = gtk_label_new ("PortAudio API:"); diff --git a/plugins/spu2-x/src/SndOut.cpp b/plugins/spu2-x/src/SndOut.cpp index e348b500dc..ba865cf381 100644 --- a/plugins/spu2-x/src/SndOut.cpp +++ b/plugins/spu2-x/src/SndOut.cpp @@ -92,6 +92,9 @@ SndOutModule* mods[]= WaveOut, #endif PortaudioOut, +#if defined(SPU2X_SDL) || defined(SPU2X_SDL2) + SDLOut, +#endif #ifdef __LINUX__ AlsaOut, #endif diff --git a/plugins/spu2-x/src/SndOut.h b/plugins/spu2-x/src/SndOut.h index cc0cb6da8e..477b72b375 100644 --- a/plugins/spu2-x/src/SndOut.h +++ b/plugins/spu2-x/src/SndOut.h @@ -488,6 +488,9 @@ extern SndOutModule* DSoundOut; extern SndOutModule* XAudio2Out; #endif extern SndOutModule* PortaudioOut; +#if defined(SPU2X_SDL) || defined(SPU2X_SDL2) +extern SndOutModule * const SDLOut; +#endif #ifdef __LINUX__ extern SndOutModule* AlsaOut; #endif diff --git a/plugins/spu2-x/src/SndOut_SDL.cpp b/plugins/spu2-x/src/SndOut_SDL.cpp new file mode 100644 index 0000000000..ba9ea7d21b --- /dev/null +++ b/plugins/spu2-x/src/SndOut_SDL.cpp @@ -0,0 +1,151 @@ +/* SDL Audio sink for SPU2-X. + * Copyright (c) 2013, Matt Scheirer + * + * SPU2-X is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ + +#include +#include + +#include "Global.h" +#include "SndOut.h" + +#if __cplusplus >= 201103L +#include +#else +#include +#endif + +/* Using SDL2 requires other SDL dependencies in pcsx2 get upgraded as well, or the + * symbol tables would conflict. Other dependencies in Linux are wxwidgets (you can + * build wx without sdl support, though) and onepad at the time of writing this. */ +#ifdef SPU2X_SDL2 +#include +#include +typedef StereoOut32 StereoOut_SDL; +#else +#include +#include +typedef StereoOut16 StereoOut_SDL; +#endif + +namespace { + /* Since spu2 only ever outputs stereo, we don't worry about emitting surround sound + * even though SDL2 supports it */ + const Uint8 channels = 2; + /* SDL2 supports s32 audio */ + /* Samples should vary from [512,8192] according to SDL spec. Take note this is the desired + * sample count and SDL may provide otherwise. Pulseaudio will cut this value in half if + * PA_STREAM_ADJUST_LATENCY is set in the backened, for example. */ + const Uint16 desiredSamples = 1024; + const Uint16 format = +#if SDL_MAJOR_VERSION >= 2 + AUDIO_S32SYS; +#else + AUDIO_S16SYS; +#endif + + Uint16 samples = desiredSamples; + +#if __cplusplus >= 201103L + std::unique_ptr buffer; +#else + StereoOut_SDL *buffer = NULL; +#endif + + void callback_fillBuffer(void *userdata, Uint8 *stream, int len) { + // Length should always be samples in bytes. + assert(len / sizeof(StereoOut_SDL) == samples); + + for(Uint16 i = 0; i < samples; i += SndOutPacketSize) + SndBuffer::ReadSamples(&buffer[i]); +#if __cplusplus >= 201103L + SDL_MixAudio(stream, (Uint8*) buffer.get() , len, SDL_MIX_MAXVOLUME); +#else + SDL_MixAudio(stream, (Uint8*) buffer , len, SDL_MIX_MAXVOLUME); +#endif + } +} + +struct SDLAudioMod : public SndOutModule { + static SDLAudioMod mod; + + s32 Init() { + /* SDL backends will mangle the AudioSpec and change the sample count. If we reopen + * the audio backend, we need to make sure we keep our desired samples in the spec */ + spec.samples = desiredSamples; + + if(SDL_Init(SDL_INIT_AUDIO) < 0 || SDL_OpenAudio(&spec, NULL) < 0) { + std::cerr << "SPU2-X: SDL audio error: " << SDL_GetError() << std::endl; + return -1; + } + /* This is so ugly. It is hilariously ugly. I didn't use a vector to save reallocs. */ + if(samples != spec.samples || buffer == NULL) +#if __cplusplus >= 201103L + buffer = std::unique_ptr(new StereoOut_SDL[spec.samples]); +#else + buffer = new StereoOut_SDL[spec.samples]; +#endif + if(samples != spec.samples) { + // Samples must always be a multiple of packet size. + assert(spec.samples % SndOutPacketSize == 0); + samples = spec.samples; + } + SDL_PauseAudio(0); + return 0; + } + + const wchar_t* GetIdent() const { return L"SDLAudio"; } + const wchar_t* GetLongName() const { return L"SDL Audio"; } + + void Close() { + SDL_CloseAudio(); +#if __cplusplus < 201103L + delete[] buffer; + buffer = NULL; +#endif + } + + s32 Test() const { return 0; } + void Configure(uptr parent) {} + void ReadSettings() {} + void SetApiSettings(wxString api) {} + void WriteSettings() const {}; + int GetEmptySampleCount() { return 0; } + + ~SDLAudioMod() { Close(); } + + private: + SDL_AudioSpec spec; + + /* Only C++11 supports the aggregate initializer list syntax used here. */ + SDLAudioMod() +#if __cplusplus >= 201103L + : spec({SampleRate, format, channels, 0, + desiredSamples, 0, 0, &callback_fillBuffer, nullptr}) { +#else + { + spec.freq = SampleRate; + spec.format = format; + spec.channels = channels; + spec.samples = desiredSamples; + spec.callback = callback_fillBuffer; + spec.userdata = NULL; +#endif + // Number of samples must be a multiple of packet size. + assert(samples % SndOutPacketSize == 0); + } + }; + + SDLAudioMod SDLAudioMod::mod; + + SndOutModule * const SDLOut = &SDLAudioMod::mod;