From c7c99905f4ba5f3d7b824ec2bc6f46d755d46d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gallet?= Date: Sat, 5 Jun 2021 15:35:57 +0200 Subject: Add SDL2 audio backend --- src/audio_core/sdl2_sink.cpp | 167 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/audio_core/sdl2_sink.cpp (limited to 'src/audio_core/sdl2_sink.cpp') diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp new file mode 100644 index 000000000..75c6202ef --- /dev/null +++ b/src/audio_core/sdl2_sink.cpp @@ -0,0 +1,167 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "audio_core/sdl2_sink.h" +#include "audio_core/stream.h" +#include "audio_core/time_stretch.h" +#include "common/assert.h" +#include "common/logging/log.h" +//#include "common/settings.h" + +// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#endif +#include +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace AudioCore { + +class SDLSinkStream final : public SinkStream { +public: + SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) + : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { + + SDL_AudioSpec spec; + spec.freq = sample_rate; + spec.channels = static_cast(num_channels); + spec.format = AUDIO_S16SYS; + spec.samples = 4096; + spec.callback = nullptr; + + SDL_AudioSpec obtained; + if (output_device.empty()) + dev = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained, 0); + else + dev = SDL_OpenAudioDevice(output_device.c_str(), 0, &spec, &obtained, 0); + + if (dev == 0) { + LOG_CRITICAL(Audio_Sink, "Error opening sdl audio device: {}", SDL_GetError()); + return; + } + + SDL_PauseAudioDevice(dev, 0); + } + + ~SDLSinkStream() override { + if (dev == 0) { + return; + } + + SDL_PauseAudioDevice(dev, 1); + SDL_CloseAudioDevice(dev); + } + + void EnqueueSamples(u32 source_num_channels, const std::vector& samples) override { + if (source_num_channels > num_channels) { + // Downsample 6 channels to 2 + ASSERT_MSG(source_num_channels == 6, "Channel count must be 6"); + + std::vector buf; + buf.reserve(samples.size() * num_channels / source_num_channels); + for (std::size_t i = 0; i < samples.size(); i += source_num_channels) { + // Downmixing implementation taken from the ATSC standard + const s16 left{samples[i + 0]}; + const s16 right{samples[i + 1]}; + const s16 center{samples[i + 2]}; + const s16 surround_left{samples[i + 4]}; + const s16 surround_right{samples[i + 5]}; + // Not used in the ATSC reference implementation + [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]}; + + constexpr s32 clev{707}; // center mixing level coefficient + constexpr s32 slev{707}; // surround mixing level coefficient + + buf.push_back(static_cast(left + (clev * center / 1000) + + (slev * surround_left / 1000))); + buf.push_back(static_cast(right + (clev * center / 1000) + + (slev * surround_right / 1000))); + } + int ret = SDL_QueueAudio(dev, static_cast(buf.data()), + static_cast(buf.size() * sizeof(s16))); + if (ret < 0) + LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError()); + return; + } + + int ret = SDL_QueueAudio(dev, static_cast(samples.data()), + static_cast(samples.size() * sizeof(s16))); + if (ret < 0) + LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError()); + } + + std::size_t SamplesInQueue(u32 channel_count) const override { + if (dev == 0) + return 0; + + return SDL_GetQueuedAudioSize(dev) / (channel_count * sizeof(s16)); + } + + void Flush() override { + should_flush = true; + } + + u32 GetNumChannels() const { + return num_channels; + } + +private: + SDL_AudioDeviceID dev = 0; + u32 num_channels{}; + std::atomic should_flush{}; + TimeStretcher time_stretch; +}; + +SDLSink::SDLSink(std::string_view target_device_name) { + if (!SDL_WasInit(SDL_INIT_AUDIO)) { + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); + return; + } + } + + if (target_device_name != auto_device_name && !target_device_name.empty()) { + output_device = target_device_name; + } else { + output_device.clear(); + } +} + +SDLSink::~SDLSink() { + for (auto& sink_stream : sink_streams) { + sink_stream.reset(); + } +} + +SinkStream& SDLSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, const std::string&) { + sink_streams.push_back( + std::make_unique(sample_rate, num_channels, output_device)); + return *sink_streams.back(); +} + +std::vector ListSDLSinkDevices() { + std::vector device_list; + + if (!SDL_WasInit(SDL_INIT_AUDIO)) { + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); + return std::vector(); + } + } + + int device_count = SDL_GetNumAudioDevices(0); + for (int i = 0; i < device_count; ++i) { + device_list.emplace_back(SDL_GetAudioDeviceName(i, 0)); + } + + return device_list; +} + +} // namespace AudioCore -- cgit v1.2.3 From f611506dca7004cd86086f0e22acd5a55f0ca25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gallet?= Date: Mon, 7 Jun 2021 12:51:59 +0200 Subject: Various suggestions by v1993 and lioncash --- src/audio_core/sdl2_sink.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'src/audio_core/sdl2_sink.cpp') diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index 75c6202ef..62d3716a6 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp @@ -37,10 +37,11 @@ public: spec.callback = nullptr; SDL_AudioSpec obtained; - if (output_device.empty()) + if (output_device.empty()) { dev = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained, 0); - else + } else { dev = SDL_OpenAudioDevice(output_device.c_str(), 0, &spec, &obtained, 0); + } if (dev == 0) { LOG_CRITICAL(Audio_Sink, "Error opening sdl audio device: {}", SDL_GetError()); @@ -55,7 +56,6 @@ public: return; } - SDL_PauseAudioDevice(dev, 1); SDL_CloseAudioDevice(dev); } @@ -134,11 +134,7 @@ SDLSink::SDLSink(std::string_view target_device_name) { } } -SDLSink::~SDLSink() { - for (auto& sink_stream : sink_streams) { - sink_stream.reset(); - } -} +SDLSink::~SDLSink() = default; SinkStream& SDLSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, const std::string&) { sink_streams.push_back( @@ -152,11 +148,11 @@ std::vector ListSDLSinkDevices() { if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); - return std::vector(); + return {}; } } - int device_count = SDL_GetNumAudioDevices(0); + const int device_count = SDL_GetNumAudioDevices(0); for (int i = 0; i < device_count; ++i) { device_list.emplace_back(SDL_GetAudioDeviceName(i, 0)); } -- cgit v1.2.3