duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

sdl_audio_stream.cpp (4685B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "audio_stream.h"
      5 
      6 #include "common/assert.h"
      7 #include "common/error.h"
      8 #include "common/log.h"
      9 
     10 #include <SDL.h>
     11 
     12 Log_SetChannel(SDLAudioStream);
     13 
     14 namespace {
     15 class SDLAudioStream final : public AudioStream
     16 {
     17 public:
     18   SDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters);
     19   ~SDLAudioStream();
     20 
     21   void SetPaused(bool paused) override;
     22 
     23   bool OpenDevice(Error* error);
     24   void CloseDevice();
     25 
     26 protected:
     27   ALWAYS_INLINE bool IsOpen() const { return (m_device_id != 0); }
     28 
     29   static void AudioCallback(void* userdata, uint8_t* stream, int len);
     30 
     31   u32 m_device_id = 0;
     32 };
     33 } // namespace
     34 
     35 static bool InitializeSDLAudio(Error* error)
     36 {
     37   static bool initialized = false;
     38   if (initialized)
     39     return true;
     40 
     41   // May as well keep it alive until the process exits.
     42   if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
     43   {
     44     Error::SetStringFmt(error, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: {}", SDL_GetError());
     45     return false;
     46   }
     47 
     48   std::atexit([]() { SDL_QuitSubSystem(SDL_INIT_AUDIO); });
     49 
     50   initialized = true;
     51   return true;
     52 }
     53 
     54 SDLAudioStream::SDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters)
     55   : AudioStream(sample_rate, parameters)
     56 {
     57 }
     58 
     59 SDLAudioStream::~SDLAudioStream()
     60 {
     61   if (IsOpen())
     62     SDLAudioStream::CloseDevice();
     63 }
     64 
     65 std::unique_ptr<AudioStream> AudioStream::CreateSDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters,
     66                                                                Error* error)
     67 {
     68   if (!InitializeSDLAudio(error))
     69     return {};
     70 
     71   std::unique_ptr<SDLAudioStream> stream = std::make_unique<SDLAudioStream>(sample_rate, parameters);
     72   if (!stream->OpenDevice(error))
     73     stream.reset();
     74 
     75   return stream;
     76 }
     77 
     78 bool SDLAudioStream::OpenDevice(Error* error)
     79 {
     80   DebugAssert(!IsOpen());
     81 
     82   static constexpr const std::array<SampleReader, static_cast<size_t>(AudioExpansionMode::Count)> sample_readers = {{
     83     // Disabled
     84     &StereoSampleReaderImpl,
     85     // StereoLFE
     86     &SampleReaderImpl<AudioExpansionMode::StereoLFE, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
     87                       READ_CHANNEL_LFE>,
     88     // Quadraphonic
     89     &SampleReaderImpl<AudioExpansionMode::Quadraphonic, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
     90                       READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
     91     // QuadraphonicLFE
     92     &SampleReaderImpl<AudioExpansionMode::QuadraphonicLFE, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
     93                       READ_CHANNEL_LFE, READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
     94     // Surround51
     95     &SampleReaderImpl<AudioExpansionMode::Surround51, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
     96                       READ_CHANNEL_FRONT_CENTER, READ_CHANNEL_LFE, READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
     97     // Surround71
     98     &SampleReaderImpl<AudioExpansionMode::Surround71, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
     99                       READ_CHANNEL_FRONT_CENTER, READ_CHANNEL_LFE, READ_CHANNEL_SIDE_LEFT, READ_CHANNEL_SIDE_RIGHT,
    100                       READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
    101   }};
    102 
    103   SDL_AudioSpec spec = {};
    104   spec.freq = m_sample_rate;
    105   spec.channels = m_output_channels;
    106   spec.format = AUDIO_S16;
    107   spec.samples = static_cast<Uint16>(GetBufferSizeForMS(
    108     m_sample_rate, (m_parameters.output_latency_ms == 0) ? m_parameters.buffer_ms : m_parameters.output_latency_ms));
    109   spec.callback = AudioCallback;
    110   spec.userdata = static_cast<void*>(this);
    111 
    112   SDL_AudioSpec obtained_spec = {};
    113   m_device_id = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained_spec, SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
    114   if (m_device_id == 0)
    115   {
    116     Error::SetStringFmt(error, "SDL_OpenAudioDevice() failed: {}", SDL_GetError());
    117     return false;
    118   }
    119 
    120   DEV_LOG("Requested {} frame buffer, got {} frame buffer", spec.samples, obtained_spec.samples);
    121 
    122   BaseInitialize(sample_readers[static_cast<size_t>(m_parameters.expansion_mode)]);
    123   SDL_PauseAudioDevice(m_device_id, 0);
    124 
    125   return true;
    126 }
    127 
    128 void SDLAudioStream::SetPaused(bool paused)
    129 {
    130   if (m_paused == paused)
    131     return;
    132 
    133   SDL_PauseAudioDevice(m_device_id, paused ? 1 : 0);
    134   m_paused = paused;
    135 }
    136 
    137 void SDLAudioStream::CloseDevice()
    138 {
    139   SDL_CloseAudioDevice(m_device_id);
    140   m_device_id = 0;
    141 }
    142 
    143 void SDLAudioStream::AudioCallback(void* userdata, uint8_t* stream, int len)
    144 {
    145   SDLAudioStream* const this_ptr = static_cast<SDLAudioStream*>(userdata);
    146   const u32 num_frames = len / sizeof(SampleType) / this_ptr->m_output_channels;
    147 
    148   this_ptr->ReadFrames(reinterpret_cast<SampleType*>(stream), num_frames);
    149 }