diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 72c02058b3..2d07a35c43 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -13,7 +14,6 @@ #include "audio_core/sink/sdl2_sink.h" #include "audio_core/sink/sink_stream.h" #include "common/logging/log.h" -#include "common/scope_exit.h" #include "core/core.h" namespace AudioCore::Sink { @@ -39,37 +39,53 @@ public: system_channels = system_channels_; device_channels = device_channels_; - SDL_AudioSpec spec; + SDL_AudioSpec spec{}; spec.freq = TargetSampleRate; - spec.channels = static_cast(device_channels); + spec.channels = static_cast(device_channels); spec.format = SDL_AUDIO_S16; - spec.samples = TargetSampleCount * 2; - spec.callback = &SDLSinkStream::DataCallback; - spec.userdata = this; + SDL_AudioDeviceID device_id = 0; std::string device_name{output_device}; - bool capture{false}; + bool is_capture{false}; + if (type == StreamType::In) { device_name = input_device; - capture = true; + is_capture = true; } - SDL_AudioSpec obtained; - if (device_name.empty()) { - device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false); - } else { - device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); + if (!device_name.empty()) { + int count = 0; + SDL_AudioDeviceID* devices = is_capture ? SDL_GetAudioRecordingDevices(&count) + : SDL_GetAudioPlaybackDevices(&count); + + if (devices) { + for (int i = 0; i < count; ++i) { + const char* name = SDL_GetAudioDeviceName(devices[i]); + if (name && device_name == name) { + device_id = devices[i]; + break; + } + } + SDL_free(devices); + } } - if (device == 0) { - LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError()); + if (device_id == 0) { + device_id = + is_capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; + } + + stream = SDL_OpenAudioDeviceStream( + device_id, &spec, + is_capture ? &SDLSinkStream::CaptureCallback : &SDLSinkStream::PlaybackCallback, this); + + if (!stream) { + LOG_CRITICAL(Audio_Sink, "Error opening SDL audio stream: {}", SDL_GetError()); return; } - LOG_INFO(Service_Audio, - "Opening SDL stream {} with: rate {} channels {} (system channels {}) " - " samples {}", - device, obtained.freq, obtained.channels, system_channels, obtained.samples); + LOG_INFO(Service_Audio, "Opening SDL stream with: rate {} channels {} (system channels {})", + spec.freq, spec.channels, system_channels); } /** @@ -84,13 +100,14 @@ public: * Finalize the sink stream. */ void Finalize() override { - if (device == 0) { + if (!stream) { return; } Stop(); - SDL_ClearQueuedAudio(device); - SDL_CloseAudioDevice(device); + SDL_ClearAudioStream(stream); + SDL_DestroyAudioStream(stream); + stream = nullptr; } /** @@ -100,62 +117,80 @@ public: * Default false. */ void Start(bool resume = false) override { - if (device == 0 || !paused) { + if (!stream || !paused) { return; } paused = false; - SDL_PauseAudioDevice(device, 0); + SDL_ResumeAudioStreamDevice(stream); } /** * Stop the sink stream. */ void Stop() override { - if (device == 0 || paused) { + if (!stream || paused) { return; } SignalPause(); - SDL_PauseAudioDevice(device, 1); + SDL_PauseAudioStreamDevice(stream); + paused = true; } private: - /** - * Main callback from SDL. Either expects samples from us (audio render/audio out), or will - * provide samples to be copied (audio in). - * - * @param userdata - Custom data pointer passed along, points to a SDLSinkStream. - * @param stream - Buffer of samples to be filled or read. - * @param len - Length of the stream in bytes. - */ - static void DataCallback(void* userdata, Uint8* stream, int len) { + static void PlaybackCallback(void* userdata, SDL_AudioStream* stream, int additional_amount, + int total_amount) { auto* impl = static_cast(userdata); - if (!impl) { return; } const std::size_t num_channels = impl->GetDeviceChannels(); const std::size_t frame_size = num_channels; - const std::size_t num_frames{len / num_channels / sizeof(s16)}; + const std::size_t num_frames = additional_amount / (sizeof(s16) * frame_size); - if (impl->type == StreamType::In) { - std::span input_buffer{reinterpret_cast(stream), - num_frames * frame_size}; - impl->ProcessAudioIn(input_buffer, num_frames); - } else { - std::span output_buffer{reinterpret_cast(stream), num_frames * frame_size}; - impl->ProcessAudioOutAndRender(output_buffer, num_frames); + if (num_frames == 0) { + return; + } + + std::vector buffer(num_frames * frame_size); + impl->ProcessAudioOutAndRender(buffer, num_frames); + SDL_PutAudioStreamData(stream, buffer.data(), + static_cast(buffer.size() * sizeof(s16))); + } + + static void CaptureCallback(void* userdata, SDL_AudioStream* stream, int additional_amount, + int total_amount) { + auto* impl = static_cast(userdata); + if (!impl) { + return; + } + + const std::size_t num_channels = impl->GetDeviceChannels(); + const std::size_t frame_size = num_channels; + const std::size_t bytes_available = SDL_GetAudioStreamAvailable(stream); + + if (bytes_available == 0) { + return; + } + + const std::size_t num_frames = bytes_available / (sizeof(s16) * frame_size); + std::vector buffer(num_frames * frame_size); + + int bytes_read = + SDL_GetAudioStreamData(stream, buffer.data(), static_cast(bytes_available)); + if (bytes_read > 0) { + const std::size_t frames_read = bytes_read / (sizeof(s16) * frame_size); + impl->ProcessAudioIn(buffer, frames_read); } } - /// SDL device id of the opened input/output device - SDL_AudioDeviceID device{}; + SDL_AudioStream* stream{nullptr}; }; SDLSink::SDLSink(std::string_view target_device_name) { if (!SDL_WasInit(SDL_INIT_AUDIO)) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); return; } @@ -218,66 +253,31 @@ std::vector ListSDLSinkDevices(bool capture) { std::vector device_list; if (!SDL_WasInit(SDL_INIT_AUDIO)) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); return {}; } } - const int device_count = SDL_GetNumAudioDevices(capture); - for (int i = 0; i < device_count; ++i) { - if (const char* name = SDL_GetAudioDeviceName(i, capture)) { - device_list.emplace_back(name); + int count = 0; + SDL_AudioDeviceID* devices = + capture ? SDL_GetAudioRecordingDevices(&count) : SDL_GetAudioPlaybackDevices(&count); + + if (devices) { + for (int i = 0; i < count; ++i) { + const char* name = SDL_GetAudioDeviceName(devices[i]); + if (name) { + device_list.emplace_back(name); + } } + SDL_free(devices); } return device_list; } -/* REVERSION to 3833 - function GetSDLLatency() REINTRODUCED FROM 3833 - DIABLO 3 FIX */ u32 GetSDLLatency() { return TargetSampleCount * 2; } -// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX -/* -bool IsSDLSuitable() { -#if !defined(HAVE_SDL2) - return false; -#else - // Check SDL can init - if (!SDL_WasInit(SDL_INIT_AUDIO)) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { - LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}", - SDL_GetError()); - return false; - } - } - - // We can set any latency frequency we want with SDL, so no need to check that. - - // Check we can open a device with standard parameters - SDL_AudioSpec spec; - spec.freq = TargetSampleRate; - spec.channels = 2u; - spec.format = SDL_AUDIO_S16; - spec.samples = TargetSampleCount * 2; - spec.callback = nullptr; - spec.userdata = nullptr; - - SDL_AudioSpec obtained; - auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false); - - if (device == 0) { - LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}", - SDL_GetError()); - return false; - } - - SDL_CloseAudioDevice(device); - return true; -#endif -} -*/ - -} // namespace AudioCore::Sink +} // namespace AudioCore::Sink \ No newline at end of file