yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
audio_backend.cc
Go to the documentation of this file.
1// audio_backend.cc - Audio Backend Implementation
2
4
5#include <SDL.h>
6#include <algorithm>
7#include "util/log.h"
8
9namespace yaze {
10namespace emu {
11namespace audio {
12
13// ============================================================================
14// SDL2AudioBackend Implementation
15// ============================================================================
16
20
22 if (initialized_) {
23 LOG_WARN("AudioBackend", "Already initialized, shutting down first");
24 Shutdown();
25 }
26
27 config_ = config;
28
29 SDL_AudioSpec want, have;
30 SDL_memset(&want, 0, sizeof(want));
31
32 want.freq = config.sample_rate;
33 want.format = (config.format == SampleFormat::INT16) ? AUDIO_S16 : AUDIO_F32;
34 want.channels = config.channels;
35 want.samples = config.buffer_frames;
36 want.callback = nullptr; // Use queue-based audio
37
38 device_id_ = SDL_OpenAudioDevice(nullptr, 0, &want, &have, 0);
39
40 if (device_id_ == 0) {
41 LOG_ERROR("AudioBackend", "Failed to open SDL audio device: %s", SDL_GetError());
42 return false;
43 }
44
45 // Verify we got what we asked for
46 if (have.freq != want.freq || have.channels != want.channels) {
47 LOG_WARN("AudioBackend",
48 "Audio spec mismatch - wanted %dHz %dch, got %dHz %dch",
49 want.freq, want.channels, have.freq, have.channels);
50 // Update config with actual values
51 config_.sample_rate = have.freq;
52 config_.channels = have.channels;
53 }
54
55 LOG_INFO("AudioBackend", "SDL2 audio initialized: %dHz, %d channels, %d samples buffer",
56 have.freq, have.channels, have.samples);
57
58 initialized_ = true;
59
60 // Start playback immediately (unpause)
61 SDL_PauseAudioDevice(device_id_, 0);
62
63 return true;
64}
65
67 if (!initialized_) return;
68
69 if (device_id_ != 0) {
70 SDL_PauseAudioDevice(device_id_, 1);
71 SDL_CloseAudioDevice(device_id_);
72 device_id_ = 0;
73 }
74
75 initialized_ = false;
76 LOG_INFO("AudioBackend", "SDL2 audio shut down");
77}
78
80 if (!initialized_) return;
81 SDL_PauseAudioDevice(device_id_, 0);
82}
83
85 if (!initialized_) return;
86 SDL_PauseAudioDevice(device_id_, 1);
87}
88
90 if (!initialized_) return;
91 Clear();
92 SDL_PauseAudioDevice(device_id_, 1);
93}
94
96 if (!initialized_) return;
97 SDL_ClearQueuedAudio(device_id_);
98}
99
100bool SDL2AudioBackend::QueueSamples(const int16_t* samples, int num_samples) {
101 if (!initialized_ || !samples) return false;
102
103 // OPTIMIZATION: Skip volume scaling if volume is 100% (common case)
104 if (volume_ == 1.0f) {
105 // Fast path: No volume adjustment needed
106 int result = SDL_QueueAudio(device_id_, samples, num_samples * sizeof(int16_t));
107 if (result < 0) {
108 LOG_ERROR("AudioBackend", "SDL_QueueAudio failed: %s", SDL_GetError());
109 return false;
110 }
111 return true;
112 }
113
114 // Slow path: Volume scaling required
115 // Use thread-local buffer to avoid repeated allocations
116 thread_local std::vector<int16_t> scaled_samples;
117
118 // Resize only if needed (avoid reallocation on every call)
119 if (scaled_samples.size() < static_cast<size_t>(num_samples)) {
120 scaled_samples.resize(num_samples);
121 }
122
123 // Apply volume scaling with SIMD-friendly loop
124 for (int i = 0; i < num_samples; ++i) {
125 int32_t scaled = static_cast<int32_t>(samples[i] * volume_);
126 // Clamp to prevent overflow
127 if (scaled > 32767) scaled = 32767;
128 else if (scaled < -32768) scaled = -32768;
129 scaled_samples[i] = static_cast<int16_t>(scaled);
130 }
131
132 int result = SDL_QueueAudio(device_id_, scaled_samples.data(),
133 num_samples * sizeof(int16_t));
134 if (result < 0) {
135 LOG_ERROR("AudioBackend", "SDL_QueueAudio failed: %s", SDL_GetError());
136 return false;
137 }
138
139 return true;
140}
141
142bool SDL2AudioBackend::QueueSamples(const float* samples, int num_samples) {
143 if (!initialized_ || !samples) return false;
144
145 // Convert float to int16
146 std::vector<int16_t> int_samples(num_samples);
147 for (int i = 0; i < num_samples; ++i) {
148 float scaled = std::clamp(samples[i] * volume_, -1.0f, 1.0f);
149 int_samples[i] = static_cast<int16_t>(scaled * 32767.0f);
150 }
151
152 return QueueSamples(int_samples.data(), num_samples);
153}
154
156 AudioStatus status;
157
158 if (!initialized_) return status;
159
160 status.is_playing = (SDL_GetAudioDeviceStatus(device_id_) == SDL_AUDIO_PLAYING);
161 status.queued_bytes = SDL_GetQueuedAudioSize(device_id_);
162
163 // Calculate queued frames (each frame = channels * sample_size)
164 int bytes_per_frame = config_.channels *
166 status.queued_frames = status.queued_bytes / bytes_per_frame;
167
168 // Check for underrun (queue too low while playing)
169 if (status.is_playing && status.queued_frames < 100) {
170 status.has_underrun = true;
171 }
172
173 return status;
174}
175
177 return initialized_;
178}
179
183
184void SDL2AudioBackend::SetVolume(float volume) {
185 volume_ = std::clamp(volume, 0.0f, 1.0f);
186}
187
189 return volume_;
190}
191
192// ============================================================================
193// AudioBackendFactory Implementation
194// ============================================================================
195
196std::unique_ptr<IAudioBackend> AudioBackendFactory::Create(BackendType type) {
197 switch (type) {
199 return std::make_unique<SDL2AudioBackend>();
200
202 // TODO: Implement null backend for testing
203 LOG_WARN("AudioBackend", "NULL backend not yet implemented, using SDL2");
204 return std::make_unique<SDL2AudioBackend>();
205
206 default:
207 LOG_ERROR("AudioBackend", "Unknown backend type, using SDL2");
208 return std::make_unique<SDL2AudioBackend>();
209 }
210}
211
212} // namespace audio
213} // namespace emu
214} // namespace yaze
215
static std::unique_ptr< IAudioBackend > Create(BackendType type)
AudioConfig GetConfig() const override
void SetVolume(float volume) override
bool Initialize(const AudioConfig &config) override
bool IsInitialized() const override
AudioStatus GetStatus() const override
bool QueueSamples(const int16_t *samples, int num_samples) override
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
Main namespace for the application.