diff options
Diffstat (limited to 'src/audio_core/sink')
| -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, 132 insertions, 114 deletions
| diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 8cacd3386..7dd155ff0 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -1,5 +1,4 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later  #include <span> @@ -17,9 +16,138 @@ namespace AudioCore::Sink {  /**   * SDL sink stream, responsible for sinking samples to hardware.   */ -// class SDLSinkStream final : public SinkStream { -// ... -// } +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{}; +};  SDLSink::SDLSink(std::string_view target_device_name) {      if (!SDL_WasInit(SDL_INIT_AUDIO)) { @@ -140,96 +268,4 @@ 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 fef145273..9211d2e97 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h @@ -5,7 +5,6 @@  #include <string>  #include <vector> -#include <SDL.h>  #include "audio_core/sink/sink.h" @@ -96,21 +95,4 @@ 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 | 
