mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	AudioStream: Re-add SDL backend
This commit is contained in:
		
							parent
							
								
									e70f0e1bc1
								
							
						
					
					
						commit
						9703542775
					
				|  | @ -365,6 +365,11 @@ std::unique_ptr<AudioStream> Host::CreateAudioStream(AudioBackend backend, u32 s | |||
|       return AudioStream::CreateCubebAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef ENABLE_SDL2 | ||||
|     case AudioBackend::SDL: | ||||
|       return AudioStream::CreateSDLAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|     case AudioBackend::XAudio2: | ||||
|       return AudioStream::CreateXAudio2Stream(sample_rate, channels, buffer_ms, latency_ms, stretch); | ||||
|  |  | |||
|  | @ -1559,6 +1559,9 @@ static constexpr const std::array s_audio_backend_names = { | |||
| #ifdef ENABLE_CUBEB | ||||
|   "Cubeb", | ||||
| #endif | ||||
| #ifdef ENABLE_SDL2 | ||||
|   "SDL", | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   "XAudio2", | ||||
| #endif | ||||
|  | @ -1571,6 +1574,9 @@ static constexpr const std::array s_audio_backend_display_names = { | |||
| #ifdef ENABLE_CUBEB | ||||
|   TRANSLATE_NOOP("AudioBackend", "Cubeb"), | ||||
| #endif | ||||
| #ifdef ENABLE_SDL2 | ||||
|   TRANSLATE_NOOP("AudioBackend", "SDL"), | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   TRANSLATE_NOOP("AudioBackend", "XAudio2"), | ||||
| #endif | ||||
|  |  | |||
|  | @ -484,6 +484,8 @@ struct Settings | |||
|   static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::XAudio2; | ||||
| #elif defined(__ANDROID__) | ||||
|   static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::AAudio; | ||||
| #elif defined(ENABLE_SDL2) | ||||
|   static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::SDL; | ||||
| #else | ||||
|   static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Null; | ||||
| #endif | ||||
|  |  | |||
|  | @ -190,6 +190,9 @@ enum class AudioBackend : u8 | |||
| #ifdef ENABLE_CUBEB | ||||
|   Cubeb, | ||||
| #endif | ||||
| #ifdef ENABLE_SDL2 | ||||
|   SDL, | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   XAudio2, | ||||
| #endif | ||||
|  |  | |||
|  | @ -190,6 +190,7 @@ endif() | |||
| 
 | ||||
| if(ENABLE_SDL2) | ||||
|   target_sources(util PRIVATE | ||||
|     sdl_audio_stream.cpp | ||||
|     sdl_input_source.cpp | ||||
|     sdl_input_source.h | ||||
|   ) | ||||
|  |  | |||
|  | @ -89,10 +89,10 @@ u32 AudioStream::GetBufferedFramesRelaxed() const | |||
|   return (wpos + m_buffer_size - rpos) % m_buffer_size; | ||||
| } | ||||
| 
 | ||||
| void AudioStream::ReadFrames(s16* bData, u32 nFrames) | ||||
| void AudioStream::ReadFrames(s16* samples, u32 num_frames) | ||||
| { | ||||
|   const u32 available_frames = GetBufferedFramesRelaxed(); | ||||
|   u32 frames_to_read = nFrames; | ||||
|   u32 frames_to_read = num_frames; | ||||
|   u32 silence_frames = 0; | ||||
| 
 | ||||
|   if (m_filling) | ||||
|  | @ -102,7 +102,7 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) | |||
| 
 | ||||
|     if (available_frames < toFill) | ||||
|     { | ||||
|       silence_frames = nFrames; | ||||
|       silence_frames = num_frames; | ||||
|       frames_to_read = 0; | ||||
|     } | ||||
|     else | ||||
|  | @ -133,7 +133,7 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) | |||
|     // towards the end of the buffer
 | ||||
|     if (end > 0) | ||||
|     { | ||||
|       std::memcpy(bData, &m_buffer[rpos], sizeof(s32) * end); | ||||
|       std::memcpy(samples, &m_buffer[rpos], sizeof(s32) * end); | ||||
|       rpos += end; | ||||
|       rpos = (rpos == m_buffer_size) ? 0 : rpos; | ||||
|     } | ||||
|  | @ -142,7 +142,7 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) | |||
|     const u32 start = frames_to_read - end; | ||||
|     if (start > 0) | ||||
|     { | ||||
|       std::memcpy(&bData[end * 2], &m_buffer[0], sizeof(s32) * start); | ||||
|       std::memcpy(&samples[end * 2], &m_buffer[0], sizeof(s32) * start); | ||||
|       rpos = start; | ||||
|     } | ||||
| 
 | ||||
|  | @ -156,15 +156,15 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) | |||
|       // super basic resampler - spread the input samples evenly across the output samples. will sound like ass and have
 | ||||
|       // aliasing, but better than popping by inserting silence.
 | ||||
|       const u32 increment = | ||||
|         static_cast<u32>(65536.0f * (static_cast<float>(frames_to_read / m_channels) / static_cast<float>(nFrames))); | ||||
|         static_cast<u32>(65536.0f * (static_cast<float>(frames_to_read / m_channels) / static_cast<float>(num_frames))); | ||||
| 
 | ||||
|       s16* resample_ptr = static_cast<s16*>(alloca(sizeof(s16) * frames_to_read)); | ||||
|       std::memcpy(resample_ptr, bData, sizeof(s16) * frames_to_read); | ||||
|       std::memcpy(resample_ptr, samples, sizeof(s16) * frames_to_read); | ||||
| 
 | ||||
|       s16* out_ptr = bData; | ||||
|       s16* out_ptr = samples; | ||||
|       const u32 copy_stride = sizeof(SampleType) * m_channels; | ||||
|       u32 resample_subpos = 0; | ||||
|       for (u32 i = 0; i < nFrames; i++) | ||||
|       for (u32 i = 0; i < num_frames; i++) | ||||
|       { | ||||
|         std::memcpy(out_ptr, resample_ptr, copy_stride); | ||||
|         out_ptr += m_channels; | ||||
|  | @ -174,16 +174,28 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) | |||
|         resample_subpos %= 65536u; | ||||
|       } | ||||
| 
 | ||||
|       Log_VerbosePrintf("Audio buffer underflow, resampled %u frames to %u", frames_to_read, nFrames); | ||||
|       Log_VerbosePrintf("Audio buffer underflow, resampled %u frames to %u", frames_to_read, num_frames); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // no data, fall back to silence
 | ||||
|       std::memset(bData + frames_to_read, 0, sizeof(s32) * silence_frames); | ||||
|       std::memset(samples + frames_to_read, 0, sizeof(s32) * silence_frames); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AudioStream::ApplyVolume(s16* samples, u32 num_frames) | ||||
| { | ||||
|   if (m_volume == 100) | ||||
|     return; | ||||
| 
 | ||||
|   const s32 volume_mult = static_cast<s32>(m_volume) * 32768; | ||||
|   const u32 num_samples = num_frames * m_channels; | ||||
| 
 | ||||
|   while (num_samples > 0) | ||||
|     *samples = static_cast<s16>((static_cast<s16>(*samples) * volume_mult) >> 15); | ||||
| } | ||||
| 
 | ||||
| void AudioStream::InternalWriteFrames(s32* bData, u32 nSamples) | ||||
| { | ||||
|   const u32 free = m_buffer_size - GetBufferedFramesRelaxed(); | ||||
|  |  | |||
|  | @ -85,6 +85,10 @@ public: | |||
|   static std::vector<std::string> GetCubebDriverNames(); | ||||
|   static std::vector<std::pair<std::string, std::string>> GetCubebOutputDevices(const char* driver); | ||||
| #endif | ||||
| #ifdef ENABLE_SDL2 | ||||
|   static std::unique_ptr<AudioStream> CreateSDLAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms, | ||||
|                                                            AudioStretchMode stretch); | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   static std::unique_ptr<AudioStream> CreateXAudio2Stream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms, | ||||
|                                                           AudioStretchMode stretch); | ||||
|  | @ -94,7 +98,8 @@ protected: | |||
|   AudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch); | ||||
|   void BaseInitialize(); | ||||
| 
 | ||||
|   void ReadFrames(s16* bData, u32 nSamples); | ||||
|   void ReadFrames(s16* samples, u32 num_frames); | ||||
|   void ApplyVolume(s16* samples, u32 num_frames); | ||||
| 
 | ||||
|   u32 m_sample_rate = 0; | ||||
|   u32 m_channels = 0; | ||||
|  |  | |||
							
								
								
									
										136
									
								
								src/util/sdl_audio_stream.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/util/sdl_audio_stream.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | |||
| // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #include "audio_stream.h" | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/log.h" | ||||
| 
 | ||||
| #include <SDL.h> | ||||
| 
 | ||||
| Log_SetChannel(SDLAudioStream); | ||||
| 
 | ||||
| namespace { | ||||
| class SDLAudioStream final : public AudioStream | ||||
| { | ||||
| public: | ||||
|   SDLAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch); | ||||
|   ~SDLAudioStream(); | ||||
| 
 | ||||
|   void SetPaused(bool paused) override; | ||||
|   void SetOutputVolume(u32 volume) override; | ||||
| 
 | ||||
|   bool OpenDevice(u32 latency_ms); | ||||
|   void CloseDevice(); | ||||
| 
 | ||||
| protected: | ||||
|   ALWAYS_INLINE bool IsOpen() const { return (m_device_id != 0); } | ||||
| 
 | ||||
|   static void AudioCallback(void* userdata, uint8_t* stream, int len); | ||||
| 
 | ||||
|   u32 m_device_id = 0; | ||||
| }; | ||||
| } // namespace
 | ||||
| 
 | ||||
| static bool InitializeSDLAudio() | ||||
| { | ||||
|   static bool initialized = false; | ||||
|   if (initialized) | ||||
|     return true; | ||||
| 
 | ||||
|   // May as well keep it alive until the process exits.
 | ||||
|   const int error = SDL_InitSubSystem(SDL_INIT_AUDIO); | ||||
|   if (error != 0) | ||||
|   { | ||||
|     Log_ErrorFmt("SDL_InitSubSystem(SDL_INIT_AUDIO) returned {}", error); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   std::atexit([]() { SDL_QuitSubSystem(SDL_INIT_AUDIO); }); | ||||
| 
 | ||||
|   initialized = true; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| SDLAudioStream::SDLAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch) | ||||
|   : AudioStream(sample_rate, channels, buffer_ms, stretch) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| SDLAudioStream::~SDLAudioStream() | ||||
| { | ||||
|   if (IsOpen()) | ||||
|     SDLAudioStream::CloseDevice(); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<AudioStream> AudioStream::CreateSDLAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, | ||||
|                                                                u32 latency_ms, AudioStretchMode stretch) | ||||
| { | ||||
|   if (!InitializeSDLAudio()) | ||||
|     return {}; | ||||
| 
 | ||||
|   std::unique_ptr<SDLAudioStream> stream = std::make_unique<SDLAudioStream>(sample_rate, channels, buffer_ms, stretch); | ||||
|   if (!stream->OpenDevice(latency_ms)) | ||||
|     stream.reset(); | ||||
| 
 | ||||
|   return stream; | ||||
| } | ||||
| 
 | ||||
| bool SDLAudioStream::OpenDevice(u32 latency_ms) | ||||
| { | ||||
|   DebugAssert(!IsOpen()); | ||||
| 
 | ||||
|   SDL_AudioSpec spec = {}; | ||||
|   spec.freq = m_sample_rate; | ||||
|   spec.channels = static_cast<Uint8>(m_channels); | ||||
|   spec.format = AUDIO_S16; | ||||
|   spec.samples = static_cast<Uint16>(GetBufferSizeForMS(m_sample_rate, (latency_ms == 0) ? m_buffer_ms : latency_ms)); | ||||
|   spec.callback = AudioCallback; | ||||
|   spec.userdata = static_cast<void*>(this); | ||||
| 
 | ||||
|   SDL_AudioSpec obtained_spec = {}; | ||||
|   m_device_id = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained_spec, SDL_AUDIO_ALLOW_SAMPLES_CHANGE); | ||||
|   if (m_device_id == 0) | ||||
|   { | ||||
|     Log_ErrorFmt("SDL_OpenAudioDevice() failed: {}", SDL_GetError()); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   Log_DevFmt("Requested {} frame buffer, got {} frame buffer", spec.samples, obtained_spec.samples); | ||||
| 
 | ||||
|   BaseInitialize(); | ||||
|   m_volume = 100; | ||||
|   m_paused = false; | ||||
|   SDL_PauseAudioDevice(m_device_id, 0); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void SDLAudioStream::SetPaused(bool paused) | ||||
| { | ||||
|   if (m_paused == paused) | ||||
|     return; | ||||
| 
 | ||||
|   SDL_PauseAudioDevice(m_device_id, paused ? 1 : 0); | ||||
|   m_paused = paused; | ||||
| } | ||||
| 
 | ||||
| void SDLAudioStream::CloseDevice() | ||||
| { | ||||
|   SDL_CloseAudioDevice(m_device_id); | ||||
|   m_device_id = 0; | ||||
| } | ||||
| 
 | ||||
| void SDLAudioStream::AudioCallback(void* userdata, uint8_t* stream, int len) | ||||
| { | ||||
|   SDLAudioStream* const this_ptr = static_cast<SDLAudioStream*>(userdata); | ||||
|   const u32 num_frames = len / sizeof(SampleType) / this_ptr->m_channels; | ||||
| 
 | ||||
|   this_ptr->ReadFrames(reinterpret_cast<SampleType*>(stream), num_frames); | ||||
|   this_ptr->ApplyVolume(reinterpret_cast<SampleType*>(stream), num_frames); | ||||
| } | ||||
| 
 | ||||
| void SDLAudioStream::SetOutputVolume(u32 volume) | ||||
| { | ||||
|   m_volume = volume; | ||||
| } | ||||
|  | @ -203,6 +203,7 @@ | |||
|     <ClCompile Include="postprocessing_shader.cpp" /> | ||||
|     <ClCompile Include="postprocessing_shader_fx.cpp" /> | ||||
|     <ClCompile Include="postprocessing_shader_glsl.cpp" /> | ||||
|     <ClCompile Include="sdl_audio_stream.cpp" /> | ||||
|     <ClCompile Include="sdl_input_source.cpp" /> | ||||
|     <ClCompile Include="shadergen.cpp" /> | ||||
|     <ClCompile Include="shiftjis.cpp" /> | ||||
|  |  | |||
|  | @ -156,6 +156,7 @@ | |||
|     <ClCompile Include="opengl_context_egl_x11.cpp" /> | ||||
|     <ClCompile Include="opengl_context_wgl.cpp" /> | ||||
|     <ClCompile Include="image.cpp" /> | ||||
|     <ClCompile Include="sdl_audio_stream.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="metal_shaders.metal" /> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Stenzek
						Stenzek