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 }