Duckstation/src/util/sdl_audio_stream.cpp

150 lines
4.6 KiB
C++
Raw Normal View History

2024-03-20 12:46:20 +00:00
// 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/error.h"
2024-05-23 10:55:28 +00:00
#include "common/log.h"
2024-03-20 12:46:20 +00:00
#include <SDL.h>
Log_SetChannel(SDLAudioStream);
namespace {
class SDLAudioStream final : public AudioStream
{
public:
SDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters);
2024-03-20 12:46:20 +00:00
~SDLAudioStream();
void SetPaused(bool paused) override;
bool OpenDevice(Error* error);
2024-03-20 12:46:20 +00:00
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(Error* error)
2024-03-20 12:46:20 +00:00
{
static bool initialized = false;
if (initialized)
return true;
// May as well keep it alive until the process exits.
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
2024-03-20 12:46:20 +00:00
{
Error::SetStringFmt(error, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: {}", SDL_GetError());
2024-03-20 12:46:20 +00:00
return false;
}
std::atexit([]() { SDL_QuitSubSystem(SDL_INIT_AUDIO); });
initialized = true;
return true;
}
SDLAudioStream::SDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters)
: AudioStream(sample_rate, parameters)
2024-03-20 12:46:20 +00:00
{
}
SDLAudioStream::~SDLAudioStream()
{
if (IsOpen())
SDLAudioStream::CloseDevice();
}
2024-05-23 10:55:28 +00:00
std::unique_ptr<AudioStream> AudioStream::CreateSDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters,
Error* error)
2024-03-20 12:46:20 +00:00
{
if (!InitializeSDLAudio(error))
2024-03-20 12:46:20 +00:00
return {};
std::unique_ptr<SDLAudioStream> stream = std::make_unique<SDLAudioStream>(sample_rate, parameters);
if (!stream->OpenDevice(error))
2024-03-20 12:46:20 +00:00
stream.reset();
return stream;
}
bool SDLAudioStream::OpenDevice(Error* error)
2024-03-20 12:46:20 +00:00
{
DebugAssert(!IsOpen());
static constexpr const std::array<SampleReader, static_cast<size_t>(AudioExpansionMode::Count)> sample_readers = {{
// Disabled
&StereoSampleReaderImpl,
// StereoLFE
&SampleReaderImpl<AudioExpansionMode::StereoLFE, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
READ_CHANNEL_LFE>,
// Quadraphonic
&SampleReaderImpl<AudioExpansionMode::Quadraphonic, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
// QuadraphonicLFE
&SampleReaderImpl<AudioExpansionMode::QuadraphonicLFE, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
READ_CHANNEL_LFE, READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
// Surround51
&SampleReaderImpl<AudioExpansionMode::Surround51, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
READ_CHANNEL_FRONT_CENTER, READ_CHANNEL_LFE, READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
// Surround71
&SampleReaderImpl<AudioExpansionMode::Surround71, READ_CHANNEL_FRONT_LEFT, READ_CHANNEL_FRONT_RIGHT,
READ_CHANNEL_FRONT_CENTER, READ_CHANNEL_LFE, READ_CHANNEL_SIDE_LEFT, READ_CHANNEL_SIDE_RIGHT,
READ_CHANNEL_REAR_LEFT, READ_CHANNEL_REAR_RIGHT>,
}};
2024-03-20 12:46:20 +00:00
SDL_AudioSpec spec = {};
spec.freq = m_sample_rate;
spec.channels = m_output_channels;
2024-03-20 12:46:20 +00:00
spec.format = AUDIO_S16;
spec.samples = static_cast<Uint16>(GetBufferSizeForMS(
m_sample_rate, (m_parameters.output_latency_ms == 0) ? m_parameters.buffer_ms : m_parameters.output_latency_ms));
2024-03-20 12:46:20 +00:00
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)
{
Error::SetStringFmt(error, "SDL_OpenAudioDevice() failed: {}", SDL_GetError());
2024-03-20 12:46:20 +00:00
return false;
}
2024-05-23 10:55:28 +00:00
DEV_LOG("Requested {} frame buffer, got {} frame buffer", spec.samples, obtained_spec.samples);
2024-03-20 12:46:20 +00:00
BaseInitialize(sample_readers[static_cast<size_t>(m_parameters.expansion_mode)]);
2024-03-20 12:46:20 +00:00
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_output_channels;
2024-03-20 12:46:20 +00:00
this_ptr->ReadFrames(reinterpret_cast<SampleType*>(stream), num_frames);
}