diff options
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/audio_core/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | src/audio_core/sdl2_sink.cpp | 126 | ||||
| -rw-r--r-- | src/audio_core/sdl2_sink.h | 30 | ||||
| -rw-r--r-- | src/audio_core/sink.h | 2 | ||||
| -rw-r--r-- | src/audio_core/sink_details.cpp | 7 | ||||
| -rw-r--r-- | src/citra/default_ini.h | 2 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 1 | ||||
| -rw-r--r-- | src/common/logging/log.h | 3 |
9 files changed, 182 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d628ecc50..8f2898973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -152,12 +152,15 @@ if (ENABLE_SDL2) | |||
| 152 | download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX) | 152 | download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX) |
| 153 | endif() | 153 | endif() |
| 154 | 154 | ||
| 155 | set(SDL2_FOUND YES) | ||
| 155 | set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") | 156 | set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") |
| 156 | set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") | 157 | set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") |
| 157 | set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") | 158 | set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") |
| 158 | else() | 159 | else() |
| 159 | find_package(SDL2 REQUIRED) | 160 | find_package(SDL2 REQUIRED) |
| 160 | endif() | 161 | endif() |
| 162 | else() | ||
| 163 | set(SDL2_FOUND NO) | ||
| 161 | endif() | 164 | endif() |
| 162 | 165 | ||
| 163 | IF (APPLE) | 166 | IF (APPLE) |
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 4cd7aba67..13b5e400e 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -25,7 +25,18 @@ set(HEADERS | |||
| 25 | 25 | ||
| 26 | include_directories(../../externals/soundtouch/include) | 26 | include_directories(../../externals/soundtouch/include) |
| 27 | 27 | ||
| 28 | if(SDL2_FOUND) | ||
| 29 | set(SRCS ${SRCS} sdl2_sink.cpp) | ||
| 30 | set(HEADERS ${HEADERS} sdl2_sink.h) | ||
| 31 | include_directories(${SDL2_INCLUDE_DIR}) | ||
| 32 | endif() | ||
| 33 | |||
| 28 | create_directory_groups(${SRCS} ${HEADERS}) | 34 | create_directory_groups(${SRCS} ${HEADERS}) |
| 29 | 35 | ||
| 30 | add_library(audio_core STATIC ${SRCS} ${HEADERS}) | 36 | add_library(audio_core STATIC ${SRCS} ${HEADERS}) |
| 31 | target_link_libraries(audio_core SoundTouch) | 37 | target_link_libraries(audio_core SoundTouch) |
| 38 | |||
| 39 | if(SDL2_FOUND) | ||
| 40 | target_link_libraries(audio_core ${SDL2_LIBRARY}) | ||
| 41 | set_property(TARGET audio_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) | ||
| 42 | endif() | ||
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp new file mode 100644 index 000000000..dc75c04ee --- /dev/null +++ b/src/audio_core/sdl2_sink.cpp | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <list> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include <SDL.h> | ||
| 9 | |||
| 10 | #include "audio_core/audio_core.h" | ||
| 11 | #include "audio_core/sdl2_sink.h" | ||
| 12 | |||
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/logging/log.h" | ||
| 15 | #include <numeric> | ||
| 16 | |||
| 17 | namespace AudioCore { | ||
| 18 | |||
| 19 | struct SDL2Sink::Impl { | ||
| 20 | unsigned int sample_rate = 0; | ||
| 21 | |||
| 22 | SDL_AudioDeviceID audio_device_id = 0; | ||
| 23 | |||
| 24 | std::list<std::vector<s16>> queue; | ||
| 25 | |||
| 26 | static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes); | ||
| 27 | }; | ||
| 28 | |||
| 29 | SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | ||
| 30 | if (SDL_Init(SDL_INIT_AUDIO) < 0) { | ||
| 31 | LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed"); | ||
| 32 | impl->audio_device_id = 0; | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | |||
| 36 | SDL_AudioSpec desired_audiospec; | ||
| 37 | SDL_zero(desired_audiospec); | ||
| 38 | desired_audiospec.format = AUDIO_S16; | ||
| 39 | desired_audiospec.channels = 2; | ||
| 40 | desired_audiospec.freq = native_sample_rate; | ||
| 41 | desired_audiospec.samples = 1024; | ||
| 42 | desired_audiospec.userdata = impl.get(); | ||
| 43 | desired_audiospec.callback = &Impl::Callback; | ||
| 44 | |||
| 45 | SDL_AudioSpec obtained_audiospec; | ||
| 46 | SDL_zero(obtained_audiospec); | ||
| 47 | |||
| 48 | impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); | ||
| 49 | if (impl->audio_device_id <= 0) { | ||
| 50 | LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | impl->sample_rate = obtained_audiospec.freq; | ||
| 55 | |||
| 56 | // SDL2 audio devices start out paused, unpause it: | ||
| 57 | SDL_PauseAudioDevice(impl->audio_device_id, 0); | ||
| 58 | } | ||
| 59 | |||
| 60 | SDL2Sink::~SDL2Sink() { | ||
| 61 | if (impl->audio_device_id <= 0) | ||
| 62 | return; | ||
| 63 | |||
| 64 | SDL_CloseAudioDevice(impl->audio_device_id); | ||
| 65 | } | ||
| 66 | |||
| 67 | unsigned int SDL2Sink::GetNativeSampleRate() const { | ||
| 68 | if (impl->audio_device_id <= 0) | ||
| 69 | return native_sample_rate; | ||
| 70 | |||
| 71 | return impl->sample_rate; | ||
| 72 | } | ||
| 73 | |||
| 74 | void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) { | ||
| 75 | if (impl->audio_device_id <= 0) | ||
| 76 | return; | ||
| 77 | |||
| 78 | ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)"); | ||
| 79 | |||
| 80 | SDL_LockAudioDevice(impl->audio_device_id); | ||
| 81 | impl->queue.emplace_back(samples); | ||
| 82 | SDL_UnlockAudioDevice(impl->audio_device_id); | ||
| 83 | } | ||
| 84 | |||
| 85 | size_t SDL2Sink::SamplesInQueue() const { | ||
| 86 | if (impl->audio_device_id <= 0) | ||
| 87 | return 0; | ||
| 88 | |||
| 89 | SDL_LockAudioDevice(impl->audio_device_id); | ||
| 90 | |||
| 91 | size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), | ||
| 92 | [](size_t sum, const auto& buffer) { | ||
| 93 | // Division by two because each stereo sample is made of two s16. | ||
| 94 | return sum + buffer.size() / 2; | ||
| 95 | }); | ||
| 96 | |||
| 97 | SDL_UnlockAudioDevice(impl->audio_device_id); | ||
| 98 | |||
| 99 | return total_size; | ||
| 100 | } | ||
| 101 | |||
| 102 | void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { | ||
| 103 | Impl* impl = reinterpret_cast<Impl*>(impl_); | ||
| 104 | |||
| 105 | size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. | ||
| 106 | |||
| 107 | while (remaining_size > 0 && !impl->queue.empty()) { | ||
| 108 | if (impl->queue.front().size() <= remaining_size) { | ||
| 109 | memcpy(buffer, impl->queue.front().data(), impl->queue.front().size() * sizeof(s16)); | ||
| 110 | buffer += impl->queue.front().size() * sizeof(s16); | ||
| 111 | remaining_size -= impl->queue.front().size(); | ||
| 112 | impl->queue.pop_front(); | ||
| 113 | } else { | ||
| 114 | memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); | ||
| 115 | buffer += remaining_size * sizeof(s16); | ||
| 116 | impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); | ||
| 117 | remaining_size = 0; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | if (remaining_size > 0) { | ||
| 122 | memset(buffer, 0, remaining_size * sizeof(s16)); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | } // namespace AudioCore | ||
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h new file mode 100644 index 000000000..0f296b673 --- /dev/null +++ b/src/audio_core/sdl2_sink.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "audio_core/sink.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | |||
| 14 | class SDL2Sink final : public Sink { | ||
| 15 | public: | ||
| 16 | SDL2Sink(); | ||
| 17 | ~SDL2Sink() override; | ||
| 18 | |||
| 19 | unsigned int GetNativeSampleRate() const override; | ||
| 20 | |||
| 21 | void EnqueueSamples(const std::vector<s16>& samples) override; | ||
| 22 | |||
| 23 | size_t SamplesInQueue() const override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | struct Impl; | ||
| 27 | std::unique_ptr<Impl> impl; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } // namespace AudioCore | ||
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h index cad21a85e..1c881c3d2 100644 --- a/src/audio_core/sink.h +++ b/src/audio_core/sink.h | |||
| @@ -19,7 +19,7 @@ public: | |||
| 19 | virtual ~Sink() = default; | 19 | virtual ~Sink() = default; |
| 20 | 20 | ||
| 21 | /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) | 21 | /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) |
| 22 | virtual unsigned GetNativeSampleRate() const = 0; | 22 | virtual unsigned int GetNativeSampleRate() const = 0; |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Feed stereo samples to sink. | 25 | * Feed stereo samples to sink. |
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index d2cc74103..ba5e83d17 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp | |||
| @@ -8,10 +8,17 @@ | |||
| 8 | #include "audio_core/null_sink.h" | 8 | #include "audio_core/null_sink.h" |
| 9 | #include "audio_core/sink_details.h" | 9 | #include "audio_core/sink_details.h" |
| 10 | 10 | ||
| 11 | #ifdef HAVE_SDL2 | ||
| 12 | #include "audio_core/sdl2_sink.h" | ||
| 13 | #endif | ||
| 14 | |||
| 11 | namespace AudioCore { | 15 | namespace AudioCore { |
| 12 | 16 | ||
| 13 | // g_sink_details is ordered in terms of desirability, with the best choice at the top. | 17 | // g_sink_details is ordered in terms of desirability, with the best choice at the top. |
| 14 | const std::vector<SinkDetails> g_sink_details = { | 18 | const std::vector<SinkDetails> g_sink_details = { |
| 19 | #ifdef HAVE_SDL2 | ||
| 20 | { "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, | ||
| 21 | #endif | ||
| 15 | { "null", []() { return std::make_unique<NullSink>(); } }, | 22 | { "null", []() { return std::make_unique<NullSink>(); } }, |
| 16 | }; | 23 | }; |
| 17 | 24 | ||
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 0e6171736..49126356f 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -58,7 +58,7 @@ bg_green = | |||
| 58 | 58 | ||
| 59 | [Audio] | 59 | [Audio] |
| 60 | # Which audio output engine to use. | 60 | # Which audio output engine to use. |
| 61 | # auto (default): Auto-select, null: No audio output | 61 | # auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available) |
| 62 | output_engine = | 62 | output_engine = |
| 63 | 63 | ||
| 64 | [Data Storage] | 64 | [Data Storage] |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 3d39f94d5..d7008fc66 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -65,6 +65,7 @@ namespace Log { | |||
| 65 | SUB(Render, OpenGL) \ | 65 | SUB(Render, OpenGL) \ |
| 66 | CLS(Audio) \ | 66 | CLS(Audio) \ |
| 67 | SUB(Audio, DSP) \ | 67 | SUB(Audio, DSP) \ |
| 68 | SUB(Audio, Sink) \ | ||
| 68 | CLS(Loader) | 69 | CLS(Loader) |
| 69 | 70 | ||
| 70 | // GetClassName is a macro defined by Windows.h, grrr... | 71 | // GetClassName is a macro defined by Windows.h, grrr... |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 521362317..c6910b1c7 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -78,8 +78,9 @@ enum class Class : ClassType { | |||
| 78 | Render, ///< Emulator video output and hardware acceleration | 78 | Render, ///< Emulator video output and hardware acceleration |
| 79 | Render_Software, ///< Software renderer backend | 79 | Render_Software, ///< Software renderer backend |
| 80 | Render_OpenGL, ///< OpenGL backend | 80 | Render_OpenGL, ///< OpenGL backend |
| 81 | Audio, ///< Emulator audio output | 81 | Audio, ///< Audio emulation |
| 82 | Audio_DSP, ///< The HLE implementation of the DSP | 82 | Audio_DSP, ///< The HLE implementation of the DSP |
| 83 | Audio_Sink, ///< Emulator audio output backend | ||
| 83 | Loader, ///< ROM loader | 84 | Loader, ///< ROM loader |
| 84 | 85 | ||
| 85 | Count ///< Total number of logging classes | 86 | Count ///< Total number of logging classes |