1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | #include "SDL_internal.h" |
23 | |
24 | #ifndef SDL_sysaudio_h_ |
25 | #define SDL_sysaudio_h_ |
26 | |
27 | #define DEBUG_AUDIOSTREAM 0 |
28 | #define DEBUG_AUDIO_CONVERT 0 |
29 | |
30 | #if DEBUG_AUDIO_CONVERT |
31 | #define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.", from, to); |
32 | #else |
33 | #define LOG_DEBUG_AUDIO_CONVERT(from, to) |
34 | #endif |
35 | |
36 | // !!! FIXME: These are wordy and unlocalized... |
37 | #define DEFAULT_PLAYBACK_DEVNAME "System audio playback device" |
38 | #define DEFAULT_RECORDING_DEVNAME "System audio recording device" |
39 | |
40 | // these are used when no better specifics are known. We default to CD audio quality. |
41 | #define DEFAULT_AUDIO_PLAYBACK_FORMAT SDL_AUDIO_S16 |
42 | #define DEFAULT_AUDIO_PLAYBACK_CHANNELS 2 |
43 | #define DEFAULT_AUDIO_PLAYBACK_FREQUENCY 44100 |
44 | |
45 | #define DEFAULT_AUDIO_RECORDING_FORMAT SDL_AUDIO_S16 |
46 | #define DEFAULT_AUDIO_RECORDING_CHANNELS 1 |
47 | #define DEFAULT_AUDIO_RECORDING_FREQUENCY 44100 |
48 | |
49 | #define SDL_MAX_CHANNELMAP_CHANNELS 8 // !!! FIXME: if SDL ever supports more channels, clean this out and make those parts dynamic. |
50 | |
51 | typedef struct SDL_AudioDevice SDL_AudioDevice; |
52 | typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice; |
53 | |
54 | // Used by src/SDL.c to initialize a particular audio driver. |
55 | extern bool SDL_InitAudio(const char *driver_name); |
56 | |
57 | // Used by src/SDL.c to shut down previously-initialized audio. |
58 | extern void SDL_QuitAudio(void); |
59 | |
60 | // Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. |
61 | const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format); |
62 | |
63 | // Must be called at least once before using converters. |
64 | extern void SDL_ChooseAudioConverters(void); |
65 | extern void SDL_SetupAudioResampler(void); |
66 | |
67 | /* Backends should call this as devices are added to the system (such as |
68 | a USB headset being plugged in), and should also be called for |
69 | for every device found during DetectDevices(). */ |
70 | extern SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_AudioSpec *spec, void *handle); |
71 | |
72 | /* Backends should call this if an opened audio device is lost. |
73 | This can happen due to i/o errors, or a device being unplugged, etc. */ |
74 | extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device); |
75 | |
76 | // Backends should call this if the system default device changes. |
77 | extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device); |
78 | |
79 | // Backends should call this if a device's format is changing (opened or not); SDL will update state and carry on with the new format. |
80 | extern bool SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames); |
81 | |
82 | // Same as above, but assume the device is already locked. |
83 | extern bool SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames); |
84 | |
85 | // Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE. |
86 | extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle); |
87 | |
88 | // Find an SDL_AudioDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE. |
89 | extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata); |
90 | |
91 | // Backends should call this if they change the device format, channels, freq, or sample_frames to keep other state correct. |
92 | extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device); |
93 | |
94 | // Backends can call this to get a reasonable default sample frame count for a device's sample rate. |
95 | int SDL_GetDefaultSampleFramesFromFreq(const int freq); |
96 | |
97 | // Backends can call this to get a standardized name for a thread to power a specific audio device. |
98 | extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); |
99 | |
100 | // Backends can call these to change a device's refcount. |
101 | extern void RefPhysicalAudioDevice(SDL_AudioDevice *device); |
102 | extern void UnrefPhysicalAudioDevice(SDL_AudioDevice *device); |
103 | |
104 | // These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread. |
105 | extern void SDL_PlaybackAudioThreadSetup(SDL_AudioDevice *device); |
106 | extern bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device); |
107 | extern void SDL_PlaybackAudioThreadShutdown(SDL_AudioDevice *device); |
108 | extern void SDL_RecordingAudioThreadSetup(SDL_AudioDevice *device); |
109 | extern bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device); |
110 | extern void SDL_RecordingAudioThreadShutdown(SDL_AudioDevice *device); |
111 | extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device); |
112 | |
113 | extern void ConvertAudioToFloat(float *dst, const void *src, int num_samples, SDL_AudioFormat src_fmt); |
114 | extern void ConvertAudioFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt); |
115 | extern void ConvertAudioSwapEndian(void* dst, const void* src, int num_samples, int bitsize); |
116 | |
117 | extern bool SDL_ChannelMapIsDefault(const int *map, int channels); |
118 | extern bool SDL_ChannelMapIsBogus(const int *map, int channels); |
119 | |
120 | // this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it! |
121 | extern void ConvertAudio(int num_frames, |
122 | const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map, |
123 | void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, |
124 | void* scratch, float gain); |
125 | |
126 | // Compare two SDL_AudioSpecs, return true if they match exactly. |
127 | // Using SDL_memcmp directly isn't safe, since potential padding might not be initialized. |
128 | // either channel map can be NULL for the default (and both should be if you don't care about them). |
129 | extern bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b); |
130 | |
131 | // See if two channel maps match |
132 | // either channel map can be NULL for the default (and both should be if you don't care about them). |
133 | extern bool SDL_AudioChannelMapsEqual(int channels, const int *channel_map_a, const int *channel_map_b); |
134 | |
135 | // allocate+copy a channel map. |
136 | extern int *SDL_ChannelMapDup(const int *origchmap, int channels); |
137 | |
138 | // Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this. |
139 | extern void OnAudioStreamCreated(SDL_AudioStream *stream); |
140 | extern void OnAudioStreamDestroy(SDL_AudioStream *stream); |
141 | |
142 | // This just lets audio playback apply logical device gain at the same time as audiostream gain, so it's one multiplication instead of thousands. |
143 | extern int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float ); |
144 | |
145 | // This is the bulk of `SDL_SetAudioStream*putChannelMap`'s work, but it lets you skip the check about changing the device end of a stream if isinput==-1. |
146 | extern bool SetAudioStreamChannelMap(SDL_AudioStream *stream, const SDL_AudioSpec *spec, int **stream_chmap, const int *chmap, int channels, int isinput); |
147 | |
148 | |
149 | typedef struct SDL_AudioDriverImpl |
150 | { |
151 | void (*DetectDevices)(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); |
152 | bool (*OpenDevice)(SDL_AudioDevice *device); |
153 | void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start |
154 | void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end |
155 | bool (*WaitDevice)(SDL_AudioDevice *device); |
156 | bool (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience. |
157 | Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); |
158 | bool (*WaitRecordingDevice)(SDL_AudioDevice *device); |
159 | int (*RecordDevice)(SDL_AudioDevice *device, void *buffer, int buflen); |
160 | void (*FlushRecording)(SDL_AudioDevice *device); |
161 | void (*CloseDevice)(SDL_AudioDevice *device); |
162 | void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice() |
163 | void (*DeinitializeStart)(void); // SDL calls this, then starts destroying objects, then calls Deinitialize. This is a good place to stop hotplug detection. |
164 | void (*Deinitialize)(void); |
165 | |
166 | // Some flags to push duplicate code into the core and reduce #ifdefs. |
167 | bool ProvidesOwnCallbackThread; // !!! FIXME: rename this, it's not a callback thread anymore. |
168 | bool HasRecordingSupport; |
169 | bool OnlyHasDefaultPlaybackDevice; |
170 | bool OnlyHasDefaultRecordingDevice; // !!! FIXME: is there ever a time where you'd have a default playback and not a default recording (or vice versa)? |
171 | } SDL_AudioDriverImpl; |
172 | |
173 | |
174 | typedef struct SDL_PendingAudioDeviceEvent |
175 | { |
176 | Uint32 type; |
177 | SDL_AudioDeviceID devid; |
178 | struct SDL_PendingAudioDeviceEvent *next; |
179 | } SDL_PendingAudioDeviceEvent; |
180 | |
181 | typedef struct SDL_AudioDriver |
182 | { |
183 | const char *name; // The name of this audio driver |
184 | const char *desc; // The description of this audio driver |
185 | SDL_AudioDriverImpl impl; // the backend's interface |
186 | SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash` |
187 | SDL_HashTable *device_hash; // the collection of currently-available audio devices (recording, playback, logical and physical!) |
188 | SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. |
189 | SDL_AudioDeviceID default_playback_device_id; |
190 | SDL_AudioDeviceID default_recording_device_id; |
191 | SDL_PendingAudioDeviceEvent pending_events; |
192 | SDL_PendingAudioDeviceEvent *pending_events_tail; |
193 | |
194 | // !!! FIXME: most (all?) of these don't have to be atomic. |
195 | SDL_AtomicInt playback_device_count; |
196 | SDL_AtomicInt recording_device_count; |
197 | SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. |
198 | } SDL_AudioDriver; |
199 | |
200 | struct SDL_AudioQueue; // forward decl. |
201 | |
202 | struct SDL_AudioStream |
203 | { |
204 | SDL_Mutex* lock; |
205 | |
206 | SDL_PropertiesID props; |
207 | |
208 | SDL_AudioStreamCallback get_callback; |
209 | void *get_callback_userdata; |
210 | SDL_AudioStreamCallback put_callback; |
211 | void *put_callback_userdata; |
212 | |
213 | SDL_AudioSpec src_spec; |
214 | SDL_AudioSpec dst_spec; |
215 | int *src_chmap; |
216 | int *dst_chmap; |
217 | float freq_ratio; |
218 | float gain; |
219 | |
220 | struct SDL_AudioQueue* queue; |
221 | |
222 | SDL_AudioSpec input_spec; // The spec of input data currently being processed |
223 | int *input_chmap; |
224 | int input_chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations. |
225 | Sint64 resample_offset; |
226 | |
227 | Uint8 *work_buffer; // used for scratch space during data conversion/resampling. |
228 | size_t work_buffer_allocation; |
229 | |
230 | bool simplified; // true if created via SDL_OpenAudioDeviceStream |
231 | |
232 | SDL_LogicalAudioDevice *bound_device; |
233 | SDL_AudioStream *next_binding; |
234 | SDL_AudioStream *prev_binding; |
235 | |
236 | SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown). |
237 | SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown). |
238 | }; |
239 | |
240 | /* Logical devices are an abstraction in SDL3; you can open the same physical |
241 | device multiple times, and each will result in an object with its own set |
242 | of bound audio streams, etc, even though internally these are all processed |
243 | as a group when mixing the final output for the physical device. */ |
244 | struct SDL_LogicalAudioDevice |
245 | { |
246 | // the unique instance ID of this device. |
247 | SDL_AudioDeviceID instance_id; |
248 | |
249 | // The physical device associated with this opened device. |
250 | SDL_AudioDevice *physical_device; |
251 | |
252 | // If whole logical device is paused (process no streams bound to this device). |
253 | SDL_AtomicInt paused; |
254 | |
255 | // Volume of the device output. |
256 | float gain; |
257 | |
258 | // double-linked list of all audio streams currently bound to this opened device. |
259 | SDL_AudioStream *bound_streams; |
260 | |
261 | // true if this was opened as a default device. |
262 | bool opened_as_default; |
263 | |
264 | // true if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc). |
265 | bool simplified; |
266 | |
267 | // If non-NULL, callback into the app that lets them access the final postmix buffer. |
268 | SDL_AudioPostmixCallback postmix; |
269 | |
270 | // App-supplied pointer for postmix callback. |
271 | void *postmix_userdata; |
272 | |
273 | // double-linked list of opened devices on the same physical device. |
274 | SDL_LogicalAudioDevice *next; |
275 | SDL_LogicalAudioDevice *prev; |
276 | }; |
277 | |
278 | struct SDL_AudioDevice |
279 | { |
280 | // A mutex for locking access to this struct |
281 | SDL_Mutex *lock; |
282 | |
283 | // A condition variable to protect device close, where we can't hold the device lock forever. |
284 | SDL_Condition *close_cond; |
285 | |
286 | // Reference count of the device; logical devices, device threads, etc, add to this. |
287 | SDL_AtomicInt refcount; |
288 | |
289 | // These are, initially, set from current_audio, but we might swap them out with Zombie versions on disconnect/failure. |
290 | bool (*WaitDevice)(SDL_AudioDevice *device); |
291 | bool (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); |
292 | Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); |
293 | bool (*WaitRecordingDevice)(SDL_AudioDevice *device); |
294 | int (*RecordDevice)(SDL_AudioDevice *device, void *buffer, int buflen); |
295 | void (*FlushRecording)(SDL_AudioDevice *device); |
296 | |
297 | // human-readable name of the device. ("SoundBlaster Pro 16") |
298 | char *name; |
299 | |
300 | // the unique instance ID of this device. |
301 | SDL_AudioDeviceID instance_id; |
302 | |
303 | // a way for the backend to identify this device _when not opened_ |
304 | void *handle; |
305 | |
306 | // The device's current audio specification |
307 | SDL_AudioSpec spec; |
308 | |
309 | // The size, in bytes, of the device's playback/recording buffer. |
310 | int buffer_size; |
311 | |
312 | // The device's channel map, or NULL for SDL default layout. |
313 | int *chmap; |
314 | |
315 | // The device's default audio specification |
316 | SDL_AudioSpec default_spec; |
317 | |
318 | // Number of sample frames the devices wants per-buffer. |
319 | int sample_frames; |
320 | |
321 | // Value to use for SDL_memset to silence a buffer in this device's format |
322 | int silence_value; |
323 | |
324 | // non-zero if we are signaling the audio thread to end. |
325 | SDL_AtomicInt shutdown; |
326 | |
327 | // non-zero if this was a disconnected device and we're waiting for it to be decommissioned. |
328 | SDL_AtomicInt zombie; |
329 | |
330 | // true if this is a recording device instead of an playback device |
331 | bool recording; |
332 | |
333 | // true if audio thread can skip silence/mix/convert stages and just do a basic memcpy. |
334 | bool simple_copy; |
335 | |
336 | // Scratch buffers used for mixing. |
337 | Uint8 *work_buffer; |
338 | Uint8 *mix_buffer; |
339 | float *postmix_buffer; |
340 | |
341 | // Size of work_buffer (and mix_buffer) in bytes. |
342 | int work_buffer_size; |
343 | |
344 | // A thread to feed the audio device |
345 | SDL_Thread *thread; |
346 | |
347 | // true if this physical device is currently opened by the backend. |
348 | bool currently_opened; |
349 | |
350 | // Data private to this driver |
351 | struct SDL_PrivateAudioData *hidden; |
352 | |
353 | // All logical devices associated with this physical device. |
354 | SDL_LogicalAudioDevice *logical_devices; |
355 | }; |
356 | |
357 | typedef struct AudioBootStrap |
358 | { |
359 | const char *name; |
360 | const char *desc; |
361 | bool (*init)(SDL_AudioDriverImpl *impl); |
362 | bool demand_only; // if true: request explicitly, or it won't be available. |
363 | bool is_preferred; |
364 | } AudioBootStrap; |
365 | |
366 | // Not all of these are available in a given build. Use #ifdefs, etc. |
367 | extern AudioBootStrap PRIVATEAUDIO_bootstrap; |
368 | extern AudioBootStrap PIPEWIRE_PREFERRED_bootstrap; |
369 | extern AudioBootStrap PIPEWIRE_bootstrap; |
370 | extern AudioBootStrap PULSEAUDIO_bootstrap; |
371 | extern AudioBootStrap ALSA_bootstrap; |
372 | extern AudioBootStrap JACK_bootstrap; |
373 | extern AudioBootStrap SNDIO_bootstrap; |
374 | extern AudioBootStrap NETBSDAUDIO_bootstrap; |
375 | extern AudioBootStrap DSP_bootstrap; |
376 | extern AudioBootStrap WASAPI_bootstrap; |
377 | extern AudioBootStrap DSOUND_bootstrap; |
378 | extern AudioBootStrap WINMM_bootstrap; |
379 | extern AudioBootStrap HAIKUAUDIO_bootstrap; |
380 | extern AudioBootStrap COREAUDIO_bootstrap; |
381 | extern AudioBootStrap DISKAUDIO_bootstrap; |
382 | extern AudioBootStrap DUMMYAUDIO_bootstrap; |
383 | extern AudioBootStrap AAUDIO_bootstrap; |
384 | extern AudioBootStrap OPENSLES_bootstrap; |
385 | extern AudioBootStrap PS2AUDIO_bootstrap; |
386 | extern AudioBootStrap PSPAUDIO_bootstrap; |
387 | extern AudioBootStrap VITAAUD_bootstrap; |
388 | extern AudioBootStrap N3DSAUDIO_bootstrap; |
389 | extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; |
390 | extern AudioBootStrap QSAAUDIO_bootstrap; |
391 | |
392 | #endif // SDL_sysaudio_h_ |
393 | |