summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp228
-rw-r--r--src/audio_core/sink/sdl2_sink.h18
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