36 "SDL2AudioBackend is unavailable when building with SDL3");
84 LOG_WARN(
"AudioBackend",
"Already initialized, shutting down first");
90 SDL_AudioSpec want, have;
91 SDL_memset(&want, 0,
sizeof(want));
100 want.callback =
nullptr;
111 int allowed_changes = SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
112 device_id_ = SDL_OpenAudioDevice(
nullptr, 0, &want, &have, allowed_changes);
115 LOG_ERROR(
"AudioBackend",
"Failed to open SDL audio device: %s",
125 if (have.freq != want.freq || have.channels != want.channels) {
127 "Audio spec mismatch - wanted %dHz %dch, got %dHz %dch", want.freq,
128 want.channels, have.freq, have.channels);
135 "SDL2 audio initialized: %dHz, %d channels, buffer: want=%d, have=%d",
136 have.freq, have.channels, want.samples, have.samples);
138 "Device actual: freq=%d, format=0x%04X, channels=%d (device_id=%u)",
175 LOG_INFO(
"AudioBackend",
"SDL2 audio shut down");
180 LOG_WARN(
"AudioBackend",
"Play() called but not initialized!");
183 SDL_AudioStatus status_before = SDL_GetAudioDeviceStatus(
device_id_);
185 SDL_AudioStatus status_after = SDL_GetAudioDeviceStatus(
device_id_);
186 LOG_INFO(
"AudioBackend",
"Play() device=%u: status %d -> %d (0=stopped,1=playing,2=paused)",
206 uint32_t before = SDL_GetQueuedAudioSize(
device_id_);
211 uint32_t after = SDL_GetQueuedAudioSize(
device_id_);
212 LOG_INFO(
"AudioBackend",
"Clear() device=%u: queue %u -> %u bytes",
device_id_, before, after);
220 static int queue_log = 0;
221 if (++queue_log % 2000 == 0) {
222 LOG_DEBUG(
"AudioBackend",
"QueueSamples: %d samples to device %u", num_samples,
device_id_);
228 SDL_QueueAudio(
device_id_, samples, num_samples *
sizeof(int16_t));
230 static int error_log = 0;
231 if (++error_log % 60 == 0) {
232 LOG_ERROR(
"AudioBackend",
"SDL_QueueAudio failed: %s", SDL_GetError());
241 thread_local std::vector<int16_t> scaled_samples;
244 if (scaled_samples.size() <
static_cast<size_t>(num_samples)) {
245 scaled_samples.resize(num_samples);
249 for (
int i = 0; i < num_samples; ++i) {
250 int32_t scaled =
static_cast<int32_t
>(samples[i] *
volume_);
254 else if (scaled < -32768)
256 scaled_samples[i] =
static_cast<int16_t
>(scaled);
259 int result = SDL_QueueAudio(
device_id_, scaled_samples.data(),
260 num_samples *
sizeof(int16_t));
262 static int error_log = 0;
263 if (++error_log % 60 == 0) {
264 LOG_ERROR(
"AudioBackend",
"SDL_QueueAudio failed: %s", SDL_GetError());
277 std::vector<int16_t> int_samples(num_samples);
278 for (
int i = 0; i < num_samples; ++i) {
279 float scaled = std::clamp(samples[i] *
volume_, -1.0f, 1.0f);
280 int_samples[i] =
static_cast<int16_t
>(scaled * 32767.0f);
287 int frames_per_channel,
int channels,
293 "QueueSamplesNative [device=%u]: frames=%d, ch=%d, native=%dHz, "
294 "stream_enabled=%d, stream=%p, device_freq=%dHz, calls=%d",
295 device_id_, frames_per_channel, channels, native_rate,
301 static int init_fail_log = 0;
302 if (++init_fail_log % 300 == 0) {
303 LOG_WARN(
"AudioBackend",
"QueueSamplesNative: FAILED (init=%d, samples=%p)",
310 static int stream_fail_log = 0;
311 if (++stream_fail_log % 600 == 0) {
312 LOG_WARN(
"AudioBackend",
"QueueSamplesNative: STREAM DISABLED (enabled=%d, stream=%p) - Audio will play at WRONG SPEED!",
318 static int native_log = 0;
319 if (++native_log % 300 == 0) {
320 LOG_DEBUG(
"AudioBackend",
"QueueSamplesNative: %d frames (Native: %d, Stream: %d)", frames_per_channel, native_rate,
stream_native_rate_);
331 frames_per_channel * channels *
static_cast<int>(
sizeof(int16_t));
333 if (SDL_AudioStreamPut(
audio_stream_, samples, bytes_in) < 0) {
334 static int put_log = 0;
335 if (++put_log % 60 == 0) {
336 LOG_ERROR(
"AudioBackend",
"SDL_AudioStreamPut failed: %s", SDL_GetError());
341 const int available_bytes = SDL_AudioStreamAvailable(
audio_stream_);
342 if (available_bytes < 0) {
343 static int avail_log = 0;
344 if (++avail_log % 60 == 0) {
345 LOG_ERROR(
"AudioBackend",
"SDL_AudioStreamAvailable failed: %s",
351 if (available_bytes == 0) {
355 const int available_samples =
356 available_bytes /
static_cast<int>(
sizeof(int16_t));
357 if (
static_cast<int>(
stream_buffer_.size()) < available_samples) {
363 if (bytes_read < 0) {
364 static int get_log = 0;
365 if (++get_log % 60 == 0) {
366 LOG_ERROR(
"AudioBackend",
"SDL_AudioStreamGet failed: %s", SDL_GetError());
372 static int log_counter = 0;
373 if (++log_counter % 600 == 0) {
375 "Resample trace: In=%d bytes (%dHz), Out=%d bytes (%dHz)", bytes_in,
389 (SDL_GetAudioDeviceStatus(
device_id_) == SDL_AUDIO_PLAYING);
393 int bytes_per_frame =
400 static int underrun_log = 0;
401 if (++underrun_log % 300 == 0) {
402 LOG_WARN(
"AudioBackend",
"Audio underrun risk: queued_frames=%u (device=%u)",
438 if (!needs_recreate) {
449 SDL_NewAudioStream(AUDIO_S16, channels, native_rate,
device_format_,
452 LOG_ERROR(
"AudioBackend",
"SDL_NewAudioStream failed: %s", SDL_GetError());
458 LOG_INFO(
"AudioBackend",
"SDL_AudioStream created: %dHz %dch -> %dHz %dch",
468 volume_ = std::clamp(volume, 0.0f, 1.0f);
492 LOG_INFO(
"AudioBackend",
"Null audio backend initialized (%dHz, %d channels)",
500 LOG_INFO(
"AudioBackend",
"Null audio backend shut down");
539 int frames_per_channel,
int channels,
570 volume_ = std::clamp(volume, 0.0f, 1.0f);
582 LOG_INFO(
"AudioBackend",
"Null backend: resampling %s (%dHz -> %dHz)",
605 return std::make_unique<SDL3AudioBackend>();
607 return std::make_unique<SDL2AudioBackend>();
612 return std::make_unique<SDL3AudioBackend>();
614 LOG_ERROR(
"AudioBackend",
"SDL3 backend requested but not compiled with SDL3 support");
615 return std::make_unique<SDL2AudioBackend>();
620 return std::make_unique<WasmAudioBackend>();
622 LOG_ERROR(
"AudioBackend",
"WASM backend requested but not compiled for Emscripten");
623 return std::make_unique<SDL2AudioBackend>();
627 return std::make_unique<NullAudioBackend>();
630 LOG_ERROR(
"AudioBackend",
"Unknown backend type, using default backend");
632 return std::make_unique<SDL3AudioBackend>();
634 return std::make_unique<SDL2AudioBackend>();
static std::unique_ptr< IAudioBackend > Create(BackendType type)
bool QueueSamplesNative(const int16_t *samples, int frames_per_channel, int channels, int native_rate) override
uint64_t total_queued_frames_
float GetVolume() const override
AudioStatus GetStatus() const override
void SetAudioStreamResampling(bool enable, int native_rate, int channels) override
bool audio_stream_enabled_
bool IsAudioStreamEnabled() const override
AudioConfig GetConfig() const override
bool IsInitialized() const override
uint64_t total_queued_samples_
bool QueueSamples(const int16_t *samples, int num_samples) override
bool Initialize(const AudioConfig &config) override
void SetVolume(float volume) override
uint64_t current_queued_bytes_
AudioConfig GetConfig() const override
SDL_AudioFormat device_format_
bool IsAudioStreamEnabled() const override
void SetVolume(float volume) override
bool Initialize(const AudioConfig &config) override
~SDL2AudioBackend() override
bool IsInitialized() const override
std::vector< int16_t > stream_buffer_
bool audio_stream_enabled_
SDL_AudioStream * audio_stream_
AudioStatus GetStatus() const override
float GetVolume() const override
bool QueueSamplesNative(const int16_t *samples, int frames_per_channel, int channels, int native_rate) override
bool QueueSamples(const int16_t *samples, int num_samples) override
void SetAudioStreamResampling(bool enable, int native_rate, int channels) override
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
SDL2/SDL3 compatibility layer.