diff options
author | MerryMage <MerryMage@users.noreply.github.com> | 2016-04-27 11:57:29 +0200 |
---|---|---|
committer | MerryMage <MerryMage@users.noreply.github.com> | 2016-05-07 12:32:48 +0200 |
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 yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.gz yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.bz2 yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.lz yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.xz yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.tar.zst yuzu-920d2cf41d9366a597bbd30d1dea5ba1884b3800.zip |
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 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <list> +#include <vector> + +#include <SDL.h> + +#include "audio_core/audio_core.h" +#include "audio_core/sdl2_sink.h" + +#include "common/assert.h" +#include "common/logging/log.h" +#include <numeric> + +namespace AudioCore { + +struct SDL2Sink::Impl { + unsigned int sample_rate = 0; + + SDL_AudioDeviceID audio_device_id = 0; + + std::list<std::vector<s16>> queue; + + static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes); +}; + +SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed"); + impl->audio_device_id = 0; + return; + } + + SDL_AudioSpec desired_audiospec; + SDL_zero(desired_audiospec); + desired_audiospec.format = AUDIO_S16; + desired_audiospec.channels = 2; + desired_audiospec.freq = native_sample_rate; + desired_audiospec.samples = 1024; + desired_audiospec.userdata = impl.get(); + desired_audiospec.callback = &Impl::Callback; + + SDL_AudioSpec obtained_audiospec; + SDL_zero(obtained_audiospec); + + impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); + if (impl->audio_device_id <= 0) { + LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); + return; + } + + impl->sample_rate = obtained_audiospec.freq; + + // SDL2 audio devices start out paused, unpause it: + SDL_PauseAudioDevice(impl->audio_device_id, 0); +} + +SDL2Sink::~SDL2Sink() { + if (impl->audio_device_id <= 0) + return; + + SDL_CloseAudioDevice(impl->audio_device_id); +} + +unsigned int SDL2Sink::GetNativeSampleRate() const { + if (impl->audio_device_id <= 0) + return native_sample_rate; + + return impl->sample_rate; +} + +void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) { + if (impl->audio_device_id <= 0) + return; + + ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)"); + + SDL_LockAudioDevice(impl->audio_device_id); + impl->queue.emplace_back(samples); + SDL_UnlockAudioDevice(impl->audio_device_id); +} + +size_t SDL2Sink::SamplesInQueue() const { + if (impl->audio_device_id <= 0) + return 0; + + SDL_LockAudioDevice(impl->audio_device_id); + + size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), + [](size_t sum, const auto& buffer) { + // Division by two because each stereo sample is made of two s16. + return sum + buffer.size() / 2; + }); + + SDL_UnlockAudioDevice(impl->audio_device_id); + + return total_size; +} + +void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { + Impl* impl = reinterpret_cast<Impl*>(impl_); + + size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. + + while (remaining_size > 0 && !impl->queue.empty()) { + if (impl->queue.front().size() <= remaining_size) { + memcpy(buffer, impl->queue.front().data(), impl->queue.front().size() * sizeof(s16)); + buffer += impl->queue.front().size() * sizeof(s16); + remaining_size -= impl->queue.front().size(); + impl->queue.pop_front(); + } else { + memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); + buffer += remaining_size * sizeof(s16); + impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); + remaining_size = 0; + } + } + + if (remaining_size > 0) { + memset(buffer, 0, remaining_size * sizeof(s16)); + } +} + +} // namespace AudioCore |