diff options
| author | 2016-04-27 10:57:29 +0100 | |
|---|---|---|
| committer | 2016-05-07 11:32:48 +0100 | |
| commit | 920d2cf41d9366a597bbd30d1dea5ba1884b3800 (patch) | |
| tree | ca4d930691a4f10b2c6aea5be078b41bdfe76c3e /src/audio_core/sdl2_sink.cpp | |
| parent | Merge pull request #1729 from MerryMage/null-sink (diff) | |
| download | yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.gz yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.xz yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.zip | |
AudioCore: SDL2 Sink
Diffstat (limited to 'src/audio_core/sdl2_sink.cpp')
| -rw-r--r-- | src/audio_core/sdl2_sink.cpp | 126 |
1 files changed, 126 insertions, 0 deletions
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 | ||