diff options
Diffstat (limited to 'src/audio_core')
-rw-r--r-- | src/audio_core/sink/sdl2_sink.cpp | 228 | ||||
-rw-r--r-- | src/audio_core/sink/sdl2_sink.h | 18 |
2 files changed, 114 insertions, 132 deletions
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 7dd155ff0..8cacd3386 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include <span> @@ -16,138 +17,9 @@ namespace AudioCore::Sink { /** * SDL sink stream, responsible for sinking samples to hardware. */ -class SDLSinkStream final : public SinkStream { -public: - /** - * Create a new sink stream. - * - * @param device_channels_ - Number of channels supported by the hardware. - * @param system_channels_ - Number of channels the audio systems expect. - * @param output_device - Name of the output device to use for this stream. - * @param input_device - Name of the input device to use for this stream. - * @param type_ - Type of this stream. - * @param system_ - Core system. - * @param event - Event used only for audio renderer, signalled on buffer consume. - */ - SDLSinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, - const std::string& input_device, StreamType type_, Core::System& system_) - : SinkStream{system_, type_} { - system_channels = system_channels_; - device_channels = device_channels_; - - SDL_AudioSpec spec; - spec.freq = TargetSampleRate; - spec.channels = static_cast<u8>(device_channels); - spec.format = AUDIO_S16SYS; - spec.samples = TargetSampleCount * 2; - spec.callback = &SDLSinkStream::DataCallback; - spec.userdata = this; - - std::string device_name{output_device}; - bool capture{false}; - if (type == StreamType::In) { - device_name = input_device; - 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 == 0) { - LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", 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); - } - - /** - * Destroy the sink stream. - */ - ~SDLSinkStream() override { - LOG_DEBUG(Service_Audio, "Destructing SDL stream {}", name); - Finalize(); - } - - /** - * Finalize the sink stream. - */ - void Finalize() override { - if (device == 0) { - return; - } - - Stop(); - SDL_ClearQueuedAudio(device); - SDL_CloseAudioDevice(device); - } - - /** - * Start the sink stream. - * - * @param resume - Set to true if this is resuming the stream a previously-active stream. - * Default false. - */ - void Start(bool resume = false) override { - if (device == 0 || !paused) { - return; - } - - paused = false; - SDL_PauseAudioDevice(device, 0); - } - - /** - * Stop the sink stream. - */ - void Stop() override { - if (device == 0 || paused) { - return; - } - SignalPause(); - SDL_PauseAudioDevice(device, 1); - } - -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) { - auto* impl = static_cast<SDLSinkStream*>(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)}; - - if (impl->type == StreamType::In) { - std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream), - num_frames * frame_size}; - impl->ProcessAudioIn(input_buffer, num_frames); - } else { - std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; - impl->ProcessAudioOutAndRender(output_buffer, num_frames); - } - } - - /// SDL device id of the opened input/output device - SDL_AudioDeviceID device{}; -}; +// class SDLSinkStream final : public SinkStream { +// ... +// } SDLSink::SDLSink(std::string_view target_device_name) { if (!SDL_WasInit(SDL_INIT_AUDIO)) { @@ -268,4 +140,96 @@ bool IsSDLSuitable() { #endif } +SDLSinkStream::SDLSinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, + const std::string& input_device, StreamType type_, Core::System& system_) + : SinkStream{system_, type_} { + system_channels = system_channels_; + device_channels = device_channels_; + + // Add error checking for SDL audio device + if (SDL_GetNumAudioDevices(0) < 1) { + LOG_ERROR(Service_Audio, "No audio devices available"); + return; + } + + SDL_AudioSpec want{}; + want.freq = TargetSampleRate; + want.format = AUDIO_S16LSB; + want.channels = static_cast<u8>(std::min(device_channels, 2u)); // Limit to stereo + want.samples = TargetSampleCount * 2; // Match the sample count from logs + want.callback = nullptr; + + SDL_AudioSpec got; + audio_device_id = SDL_OpenAudioDevice(nullptr, 0, &want, &got, + SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + + if (audio_device_id == 0) { + LOG_ERROR(Service_Audio, "SDL_OpenAudioDevice failed: {}", SDL_GetError()); + return; + } + + // Verify we got the requested format + if (got.format != want.format || got.channels != want.channels) { + LOG_WARNING(Service_Audio, + "SDL audio format mismatch - wanted: {} ch, got: {} ch", + want.channels, got.channels); + } + + running = true; + SDL_PauseAudioDevice(audio_device_id, 0); +} + +SDLSinkStream::~SDLSinkStream() { + LOG_DEBUG(Service_Audio, "Destructing SDL stream {}", name); + Finalize(); +} + +void SDLSinkStream::Finalize() { + if (audio_device_id == 0) { + return; + } + + Stop(); + SDL_ClearQueuedAudio(audio_device_id); + SDL_CloseAudioDevice(audio_device_id); +} + +void SDLSinkStream::Start(bool resume) { + if (audio_device_id == 0 || !paused) { + return; + } + + paused = false; + SDL_PauseAudioDevice(audio_device_id, 0); +} + +void SDLSinkStream::Stop() { + if (audio_device_id == 0 || paused) { + return; + } + SignalPause(); + SDL_PauseAudioDevice(audio_device_id, 1); +} + +void SDLSinkStream::DataCallback(void* userdata, Uint8* stream, int len) { + auto* impl = static_cast<SDLSinkStream*>(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)}; + + if (impl->type == StreamType::In) { + std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream), + num_frames * frame_size}; + impl->ProcessAudioIn(input_buffer, num_frames); + } else { + std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; + impl->ProcessAudioOutAndRender(output_buffer, num_frames); + } +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index 9211d2e97..fef145273 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h @@ -5,6 +5,7 @@ #include <string> #include <vector> +#include <SDL.h> #include "audio_core/sink/sink.h" @@ -95,4 +96,21 @@ std::vector<std::string> ListSDLSinkDevices(bool capture); */ bool IsSDLSuitable(); +class SDLSinkStream final : public SinkStream { +public: + SDLSinkStream(u32 sample_rate, u32 num_channels, const std::string& output_device, + const std::string& input_device, StreamType type, Core::System& system); + ~SDLSinkStream(); + + void Start(bool resume) override; + void Stop() override; + void Finalize(); + +private: + void DataCallback(void* userdata, Uint8* stream, int len); + + bool running{false}; + SDL_AudioDeviceID audio_device_id{}; +}; + } // namespace AudioCore::Sink |