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 | #include "SDL_internal.h" |
22 | |
23 | #include "SDL_audio_c.h" |
24 | #include "SDL_sysaudio.h" |
25 | #include "../thread/SDL_systhread.h" |
26 | |
27 | // Available audio drivers |
28 | static const AudioBootStrap *const bootstrap[] = { |
29 | #ifdef SDL_AUDIO_DRIVER_PRIVATE |
30 | &PRIVATEAUDIO_bootstrap, |
31 | #endif |
32 | #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO |
33 | #ifdef SDL_AUDIO_DRIVER_PIPEWIRE |
34 | &PIPEWIRE_PREFERRED_bootstrap, |
35 | #endif |
36 | &PULSEAUDIO_bootstrap, |
37 | #endif |
38 | #ifdef SDL_AUDIO_DRIVER_PIPEWIRE |
39 | &PIPEWIRE_bootstrap, |
40 | #endif |
41 | #ifdef SDL_AUDIO_DRIVER_ALSA |
42 | &ALSA_bootstrap, |
43 | #endif |
44 | #ifdef SDL_AUDIO_DRIVER_SNDIO |
45 | &SNDIO_bootstrap, |
46 | #endif |
47 | #ifdef SDL_AUDIO_DRIVER_NETBSD |
48 | &NETBSDAUDIO_bootstrap, |
49 | #endif |
50 | #ifdef SDL_AUDIO_DRIVER_WASAPI |
51 | &WASAPI_bootstrap, |
52 | #endif |
53 | #ifdef SDL_AUDIO_DRIVER_DSOUND |
54 | &DSOUND_bootstrap, |
55 | #endif |
56 | #ifdef SDL_AUDIO_DRIVER_HAIKU |
57 | &HAIKUAUDIO_bootstrap, |
58 | #endif |
59 | #ifdef SDL_AUDIO_DRIVER_COREAUDIO |
60 | &COREAUDIO_bootstrap, |
61 | #endif |
62 | #ifdef SDL_AUDIO_DRIVER_AAUDIO |
63 | &AAUDIO_bootstrap, |
64 | #endif |
65 | #ifdef SDL_AUDIO_DRIVER_OPENSLES |
66 | &OPENSLES_bootstrap, |
67 | #endif |
68 | #ifdef SDL_AUDIO_DRIVER_PS2 |
69 | &PS2AUDIO_bootstrap, |
70 | #endif |
71 | #ifdef SDL_AUDIO_DRIVER_PSP |
72 | &PSPAUDIO_bootstrap, |
73 | #endif |
74 | #ifdef SDL_AUDIO_DRIVER_VITA |
75 | &VITAAUD_bootstrap, |
76 | #endif |
77 | #ifdef SDL_AUDIO_DRIVER_N3DS |
78 | &N3DSAUDIO_bootstrap, |
79 | #endif |
80 | #ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN |
81 | &EMSCRIPTENAUDIO_bootstrap, |
82 | #endif |
83 | #ifdef SDL_AUDIO_DRIVER_JACK |
84 | &JACK_bootstrap, |
85 | #endif |
86 | #ifdef SDL_AUDIO_DRIVER_OSS |
87 | &DSP_bootstrap, |
88 | #endif |
89 | #ifdef SDL_AUDIO_DRIVER_QNX |
90 | &QSAAUDIO_bootstrap, |
91 | #endif |
92 | #ifdef SDL_AUDIO_DRIVER_DISK |
93 | &DISKAUDIO_bootstrap, |
94 | #endif |
95 | #ifdef SDL_AUDIO_DRIVER_DUMMY |
96 | &DUMMYAUDIO_bootstrap, |
97 | #endif |
98 | NULL |
99 | }; |
100 | |
101 | static SDL_AudioDriver current_audio; |
102 | |
103 | // Deduplicated list of audio bootstrap drivers. |
104 | static const AudioBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1]; |
105 | |
106 | int SDL_GetNumAudioDrivers(void) |
107 | { |
108 | static int num_drivers = -1; |
109 | |
110 | if (num_drivers >= 0) { |
111 | return num_drivers; |
112 | } |
113 | |
114 | num_drivers = 0; |
115 | |
116 | // Build a list of unique audio drivers. |
117 | for (int i = 0; bootstrap[i] != NULL; ++i) { |
118 | bool duplicate = false; |
119 | for (int j = 0; j < i; ++j) { |
120 | if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) { |
121 | duplicate = true; |
122 | break; |
123 | } |
124 | } |
125 | |
126 | if (!duplicate) { |
127 | deduped_bootstrap[num_drivers++] = bootstrap[i]; |
128 | } |
129 | } |
130 | |
131 | return num_drivers; |
132 | } |
133 | |
134 | const char *SDL_GetAudioDriver(int index) |
135 | { |
136 | if (index >= 0 && index < SDL_GetNumAudioDrivers()) { |
137 | return deduped_bootstrap[index]->name; |
138 | } |
139 | SDL_InvalidParamError("index" ); |
140 | return NULL; |
141 | } |
142 | |
143 | const char *SDL_GetCurrentAudioDriver(void) |
144 | { |
145 | return current_audio.name; |
146 | } |
147 | |
148 | int SDL_GetDefaultSampleFramesFromFreq(const int freq) |
149 | { |
150 | const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES); |
151 | if (hint) { |
152 | const int val = SDL_atoi(hint); |
153 | if (val > 0) { |
154 | return val; |
155 | } |
156 | } |
157 | |
158 | if (freq <= 22050) { |
159 | return 512; |
160 | } else if (freq <= 48000) { |
161 | return 1024; |
162 | } else if (freq <= 96000) { |
163 | return 2048; |
164 | } else { |
165 | return 4096; |
166 | } |
167 | } |
168 | |
169 | int *SDL_ChannelMapDup(const int *origchmap, int channels) |
170 | { |
171 | const size_t chmaplen = sizeof (*origchmap) * channels; |
172 | int *chmap = (int *)SDL_malloc(chmaplen); |
173 | if (chmap) { |
174 | SDL_memcpy(chmap, origchmap, chmaplen); |
175 | } |
176 | return chmap; |
177 | } |
178 | |
179 | void OnAudioStreamCreated(SDL_AudioStream *stream) |
180 | { |
181 | SDL_assert(stream != NULL); |
182 | |
183 | // NOTE that you can create an audio stream without initializing the audio subsystem, |
184 | // but it will not be automatically destroyed during a later call to SDL_Quit! |
185 | // You must explicitly destroy it yourself! |
186 | if (current_audio.device_hash_lock) { |
187 | // this isn't really part of the "device list" but it's a convenient lock to use here. |
188 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
189 | if (current_audio.existing_streams) { |
190 | current_audio.existing_streams->prev = stream; |
191 | } |
192 | stream->prev = NULL; |
193 | stream->next = current_audio.existing_streams; |
194 | current_audio.existing_streams = stream; |
195 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
196 | } |
197 | } |
198 | |
199 | void OnAudioStreamDestroy(SDL_AudioStream *stream) |
200 | { |
201 | SDL_assert(stream != NULL); |
202 | |
203 | // NOTE that you can create an audio stream without initializing the audio subsystem, |
204 | // but it will not be automatically destroyed during a later call to SDL_Quit! |
205 | // You must explicitly destroy it yourself! |
206 | if (current_audio.device_hash_lock) { |
207 | // this isn't really part of the "device list" but it's a convenient lock to use here. |
208 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
209 | if (stream->prev) { |
210 | stream->prev->next = stream->next; |
211 | } |
212 | if (stream->next) { |
213 | stream->next->prev = stream->prev; |
214 | } |
215 | if (stream == current_audio.existing_streams) { |
216 | current_audio.existing_streams = stream->next; |
217 | } |
218 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
219 | } |
220 | } |
221 | |
222 | // device should be locked when calling this. |
223 | static bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device) |
224 | { |
225 | SDL_assert(device != NULL); |
226 | return ( |
227 | device->logical_devices && // there's a logical device |
228 | !device->logical_devices->next && // there's only _ONE_ logical device |
229 | !device->logical_devices->postmix && // there isn't a postmix callback |
230 | device->logical_devices->bound_streams && // there's a bound stream |
231 | !device->logical_devices->bound_streams->next_binding // there's only _ONE_ bound stream. |
232 | ); |
233 | } |
234 | |
235 | // should hold device->lock before calling. |
236 | static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device) |
237 | { |
238 | if (!device) { |
239 | return; |
240 | } |
241 | |
242 | const bool recording = device->recording; |
243 | SDL_AudioSpec spec; |
244 | SDL_copyp(&spec, &device->spec); |
245 | |
246 | const SDL_AudioFormat devformat = spec.format; |
247 | |
248 | if (!recording) { |
249 | const bool simple_copy = AudioDeviceCanUseSimpleCopy(device); |
250 | device->simple_copy = simple_copy; |
251 | if (!simple_copy) { |
252 | spec.format = SDL_AUDIO_F32; // mixing and postbuf operates in float32 format. |
253 | } |
254 | } |
255 | |
256 | for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { |
257 | if (recording) { |
258 | const bool need_float32 = (logdev->postmix || logdev->gain != 1.0f); |
259 | spec.format = need_float32 ? SDL_AUDIO_F32 : devformat; |
260 | } |
261 | |
262 | for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { |
263 | // set the proper end of the stream to the device's format. |
264 | // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. |
265 | SDL_AudioSpec *streamspec = recording ? &stream->src_spec : &stream->dst_spec; |
266 | int **streamchmap = recording ? &stream->src_chmap : &stream->dst_chmap; |
267 | SDL_LockMutex(stream->lock); |
268 | SDL_copyp(streamspec, &spec); |
269 | SetAudioStreamChannelMap(stream, streamspec, streamchmap, device->chmap, device->spec.channels, -1); // this should be fast for normal cases, though! |
270 | SDL_UnlockMutex(stream->lock); |
271 | } |
272 | } |
273 | } |
274 | |
275 | bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b) |
276 | { |
277 | if ((a->format != b->format) || (a->channels != b->channels) || (a->freq != b->freq) || ((channel_map_a != NULL) != (channel_map_b != NULL))) { |
278 | return false; |
279 | } else if (channel_map_a && (SDL_memcmp(channel_map_a, channel_map_b, sizeof (*channel_map_a) * a->channels) != 0)) { |
280 | return false; |
281 | } |
282 | return true; |
283 | } |
284 | |
285 | bool SDL_AudioChannelMapsEqual(int channels, const int *channel_map_a, const int *channel_map_b) |
286 | { |
287 | if (channel_map_a == channel_map_b) { |
288 | return true; |
289 | } else if ((channel_map_a != NULL) != (channel_map_b != NULL)) { |
290 | return false; |
291 | } else if (channel_map_a && (SDL_memcmp(channel_map_a, channel_map_b, sizeof (*channel_map_a) * channels) != 0)) { |
292 | return false; |
293 | } |
294 | return true; |
295 | } |
296 | |
297 | |
298 | // Zombie device implementation... |
299 | |
300 | // These get used when a device is disconnected or fails, so audiostreams don't overflow with data that isn't being |
301 | // consumed and apps relying on audio callbacks don't stop making progress. |
302 | static bool ZombieWaitDevice(SDL_AudioDevice *device) |
303 | { |
304 | if (!SDL_GetAtomicInt(&device->shutdown)) { |
305 | const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); |
306 | SDL_Delay((frames * 1000) / device->spec.freq); |
307 | } |
308 | return true; |
309 | } |
310 | |
311 | static bool ZombiePlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) |
312 | { |
313 | return true; // no-op, just throw the audio away. |
314 | } |
315 | |
316 | static Uint8 *ZombieGetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) |
317 | { |
318 | return device->work_buffer; |
319 | } |
320 | |
321 | static int ZombieRecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) |
322 | { |
323 | // return a full buffer of silence every time. |
324 | SDL_memset(buffer, device->silence_value, buflen); |
325 | return buflen; |
326 | } |
327 | |
328 | static void ZombieFlushRecording(SDL_AudioDevice *device) |
329 | { |
330 | // no-op, this is all imaginary. |
331 | } |
332 | |
333 | |
334 | |
335 | // device management and hotplug... |
336 | |
337 | |
338 | /* SDL_AudioDevice, in SDL3, represents a piece of physical hardware, whether it is in use or not, so these objects exist as long as |
339 | the system-level device is available. |
340 | |
341 | Physical devices get destroyed for three reasons: |
342 | - They were lost to the system (a USB cable is kicked out, etc). |
343 | - They failed for some other unlikely reason at the API level (which is _also_ probably a USB cable being kicked out). |
344 | - We are shutting down, so all allocated resources are being freed. |
345 | |
346 | They are _not_ destroyed because we are done using them (when we "close" a playing device). |
347 | */ |
348 | static void ClosePhysicalAudioDevice(SDL_AudioDevice *device); |
349 | |
350 | |
351 | SDL_COMPILE_TIME_ASSERT(check_lowest_audio_default_value, SDL_AUDIO_DEVICE_DEFAULT_RECORDING < SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK); |
352 | |
353 | static SDL_AtomicInt last_device_instance_id; // increments on each device add to provide unique instance IDs |
354 | static SDL_AudioDeviceID AssignAudioDeviceInstanceId(bool recording, bool islogical) |
355 | { |
356 | /* Assign an instance id! Start at 2, in case there are things from the SDL2 era that still think 1 is a special value. |
357 | Also, make sure we don't assign SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, etc. */ |
358 | |
359 | // The bottom two bits of the instance id tells you if it's an playback device (1<<0), and if it's a physical device (1<<1). |
360 | const SDL_AudioDeviceID flags = (recording ? 0 : (1<<0)) | (islogical ? 0 : (1<<1)); |
361 | |
362 | const SDL_AudioDeviceID instance_id = (((SDL_AudioDeviceID) (SDL_AtomicIncRef(&last_device_instance_id) + 1)) << 2) | flags; |
363 | SDL_assert( (instance_id >= 2) && (instance_id < SDL_AUDIO_DEVICE_DEFAULT_RECORDING) ); |
364 | return instance_id; |
365 | } |
366 | |
367 | bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid) |
368 | { |
369 | return (devid & (1 << 1)) != 0; |
370 | } |
371 | |
372 | bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid) |
373 | { |
374 | return (devid & (1 << 0)) != 0; |
375 | } |
376 | |
377 | static void ObtainPhysicalAudioDeviceObj(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE |
378 | { |
379 | if (device) { |
380 | RefPhysicalAudioDevice(device); |
381 | SDL_LockMutex(device->lock); |
382 | } |
383 | } |
384 | |
385 | static void ReleaseAudioDevice(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_RELEASE |
386 | { |
387 | if (device) { |
388 | SDL_UnlockMutex(device->lock); |
389 | UnrefPhysicalAudioDevice(device); |
390 | } |
391 | } |
392 | |
393 | // If found, this locks _the physical device_ this logical device is associated with, before returning. |
394 | static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_AudioDevice **_device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_ACQUIRE |
395 | { |
396 | SDL_assert(_device != NULL); |
397 | |
398 | if (!SDL_GetCurrentAudioDriver()) { |
399 | SDL_SetError("Audio subsystem is not initialized" ); |
400 | *_device = NULL; |
401 | return NULL; |
402 | } |
403 | |
404 | SDL_AudioDevice *device = NULL; |
405 | SDL_LogicalAudioDevice *logdev = NULL; |
406 | |
407 | // bit #1 of devid is set for physical devices and unset for logical. |
408 | const bool islogical = !(devid & (1<<1)); |
409 | if (islogical) { // don't bother looking if it's not a logical device id value. |
410 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
411 | SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &logdev); |
412 | if (logdev) { |
413 | device = logdev->physical_device; |
414 | SDL_assert(device != NULL); |
415 | RefPhysicalAudioDevice(device); // reference it, in case the logical device migrates to a new default. |
416 | } |
417 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
418 | |
419 | if (logdev) { |
420 | // we have to release the device_hash_lock before we take the device lock, to avoid deadlocks, so do a loop |
421 | // to make sure the correct physical device gets locked, in case we're in a race with the default changing. |
422 | while (true) { |
423 | SDL_LockMutex(device->lock); |
424 | SDL_AudioDevice *recheck_device = (SDL_AudioDevice *) SDL_GetAtomicPointer((void **) &logdev->physical_device); |
425 | if (device == recheck_device) { |
426 | break; |
427 | } |
428 | |
429 | // default changed from under us! Try again! |
430 | RefPhysicalAudioDevice(recheck_device); |
431 | SDL_UnlockMutex(device->lock); |
432 | UnrefPhysicalAudioDevice(device); |
433 | device = recheck_device; |
434 | } |
435 | } |
436 | } |
437 | |
438 | if (!logdev) { |
439 | SDL_SetError("Invalid audio device instance ID" ); |
440 | } |
441 | |
442 | *_device = device; |
443 | return logdev; |
444 | } |
445 | |
446 | |
447 | /* this finds the physical device associated with `devid` and locks it for use. |
448 | Note that a logical device instance id will return its associated physical device! */ |
449 | static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // !!! FIXME: SDL_ACQUIRE |
450 | { |
451 | SDL_AudioDevice *device = NULL; |
452 | |
453 | // bit #1 of devid is set for physical devices and unset for logical. |
454 | const bool islogical = !(devid & (1<<1)); |
455 | if (islogical) { |
456 | ObtainLogicalAudioDevice(devid, &device); |
457 | } else if (!SDL_GetCurrentAudioDriver()) { // (the `islogical` path, above, checks this in ObtainLogicalAudioDevice.) |
458 | SDL_SetError("Audio subsystem is not initialized" ); |
459 | } else { |
460 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
461 | SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &device); |
462 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
463 | |
464 | if (!device) { |
465 | SDL_SetError("Invalid audio device instance ID" ); |
466 | } else { |
467 | ObtainPhysicalAudioDeviceObj(device); |
468 | } |
469 | } |
470 | |
471 | return device; |
472 | } |
473 | |
474 | static SDL_AudioDevice *ObtainPhysicalAudioDeviceDefaultAllowed(SDL_AudioDeviceID devid) // !!! FIXME: SDL_ACQUIRE |
475 | { |
476 | const bool wants_default = ((devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) || (devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING)); |
477 | if (!wants_default) { |
478 | return ObtainPhysicalAudioDevice(devid); |
479 | } |
480 | |
481 | const SDL_AudioDeviceID orig_devid = devid; |
482 | |
483 | while (true) { |
484 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
485 | if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) { |
486 | devid = current_audio.default_playback_device_id; |
487 | } else if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) { |
488 | devid = current_audio.default_recording_device_id; |
489 | } |
490 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
491 | |
492 | if (devid == 0) { |
493 | SDL_SetError("No default audio device available" ); |
494 | break; |
495 | } |
496 | |
497 | SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); |
498 | if (!device) { |
499 | break; |
500 | } |
501 | |
502 | // make sure the default didn't change while we were waiting for the lock... |
503 | bool got_it = false; |
504 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
505 | if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) && (devid == current_audio.default_playback_device_id)) { |
506 | got_it = true; |
507 | } else if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) && (devid == current_audio.default_recording_device_id)) { |
508 | got_it = true; |
509 | } |
510 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
511 | |
512 | if (got_it) { |
513 | return device; |
514 | } |
515 | |
516 | ReleaseAudioDevice(device); // let it go and try again. |
517 | } |
518 | |
519 | return NULL; |
520 | } |
521 | |
522 | // this assumes you hold the _physical_ device lock for this logical device! This will not unlock the lock or close the physical device! |
523 | // It also will not unref the physical device, since we might be shutting down; SDL_CloseAudioDevice handles the unref. |
524 | static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev) |
525 | { |
526 | // Remove ourselves from the device_hash hashtable. |
527 | if (current_audio.device_hash) { // will be NULL while shutting down. |
528 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
529 | SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) logdev->instance_id); |
530 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
531 | } |
532 | |
533 | // remove ourselves from the physical device's list of logical devices. |
534 | if (logdev->next) { |
535 | logdev->next->prev = logdev->prev; |
536 | } |
537 | if (logdev->prev) { |
538 | logdev->prev->next = logdev->next; |
539 | } |
540 | if (logdev->physical_device->logical_devices == logdev) { |
541 | logdev->physical_device->logical_devices = logdev->next; |
542 | } |
543 | |
544 | // unbind any still-bound streams... |
545 | SDL_AudioStream *next; |
546 | for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = next) { |
547 | SDL_LockMutex(stream->lock); |
548 | next = stream->next_binding; |
549 | stream->next_binding = NULL; |
550 | stream->prev_binding = NULL; |
551 | stream->bound_device = NULL; |
552 | SDL_UnlockMutex(stream->lock); |
553 | } |
554 | |
555 | UpdateAudioStreamFormatsPhysical(logdev->physical_device); |
556 | SDL_free(logdev); |
557 | } |
558 | |
559 | // this must not be called while `device` is still in a device list, or while a device's audio thread is still running. |
560 | static void DestroyPhysicalAudioDevice(SDL_AudioDevice *device) |
561 | { |
562 | if (!device) { |
563 | return; |
564 | } |
565 | |
566 | // Destroy any logical devices that still exist... |
567 | SDL_LockMutex(device->lock); // don't use ObtainPhysicalAudioDeviceObj because we don't want to change refcounts while destroying. |
568 | while (device->logical_devices) { |
569 | DestroyLogicalAudioDevice(device->logical_devices); |
570 | } |
571 | |
572 | ClosePhysicalAudioDevice(device); |
573 | |
574 | current_audio.impl.FreeDeviceHandle(device); |
575 | |
576 | SDL_UnlockMutex(device->lock); // don't use ReleaseAudioDevice because we don't want to change refcounts while destroying. |
577 | |
578 | SDL_DestroyMutex(device->lock); |
579 | SDL_DestroyCondition(device->close_cond); |
580 | SDL_free(device->work_buffer); |
581 | SDL_free(device->chmap); |
582 | SDL_free(device->name); |
583 | SDL_free(device); |
584 | } |
585 | |
586 | // Don't hold the device lock when calling this, as we may destroy the device! |
587 | void UnrefPhysicalAudioDevice(SDL_AudioDevice *device) |
588 | { |
589 | if (SDL_AtomicDecRef(&device->refcount)) { |
590 | // take it out of the device list. |
591 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
592 | if (SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id)) { |
593 | SDL_AddAtomicInt(device->recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count, -1); |
594 | } |
595 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
596 | DestroyPhysicalAudioDevice(device); // ...and nuke it. |
597 | } |
598 | } |
599 | |
600 | void RefPhysicalAudioDevice(SDL_AudioDevice *device) |
601 | { |
602 | SDL_AtomicIncRef(&device->refcount); |
603 | } |
604 | |
605 | static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recording, const SDL_AudioSpec *spec, void *handle, SDL_AtomicInt *device_count) |
606 | { |
607 | SDL_assert(name != NULL); |
608 | |
609 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
610 | const int shutting_down = SDL_GetAtomicInt(¤t_audio.shutting_down); |
611 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
612 | if (shutting_down) { |
613 | return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. |
614 | } |
615 | |
616 | SDL_AudioDevice *device = (SDL_AudioDevice *)SDL_calloc(1, sizeof(SDL_AudioDevice)); |
617 | if (!device) { |
618 | return NULL; |
619 | } |
620 | |
621 | device->name = SDL_strdup(name); |
622 | if (!device->name) { |
623 | SDL_free(device); |
624 | return NULL; |
625 | } |
626 | |
627 | device->lock = SDL_CreateMutex(); |
628 | if (!device->lock) { |
629 | SDL_free(device->name); |
630 | SDL_free(device); |
631 | return NULL; |
632 | } |
633 | |
634 | device->close_cond = SDL_CreateCondition(); |
635 | if (!device->close_cond) { |
636 | SDL_DestroyMutex(device->lock); |
637 | SDL_free(device->name); |
638 | SDL_free(device); |
639 | return NULL; |
640 | } |
641 | |
642 | SDL_SetAtomicInt(&device->shutdown, 0); |
643 | SDL_SetAtomicInt(&device->zombie, 0); |
644 | device->recording = recording; |
645 | SDL_copyp(&device->spec, spec); |
646 | SDL_copyp(&device->default_spec, spec); |
647 | device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); |
648 | device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); |
649 | device->handle = handle; |
650 | |
651 | device->instance_id = AssignAudioDeviceInstanceId(recording, /*islogical=*/false); |
652 | |
653 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
654 | if (SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id, device, false)) { |
655 | SDL_AddAtomicInt(device_count, 1); |
656 | } else { |
657 | SDL_DestroyCondition(device->close_cond); |
658 | SDL_DestroyMutex(device->lock); |
659 | SDL_free(device->name); |
660 | SDL_free(device); |
661 | device = NULL; |
662 | } |
663 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
664 | |
665 | RefPhysicalAudioDevice(device); // unref'd on device disconnect. |
666 | return device; |
667 | } |
668 | |
669 | static SDL_AudioDevice *CreateAudioRecordingDevice(const char *name, const SDL_AudioSpec *spec, void *handle) |
670 | { |
671 | SDL_assert(current_audio.impl.HasRecordingSupport); |
672 | return CreatePhysicalAudioDevice(name, true, spec, handle, ¤t_audio.recording_device_count); |
673 | } |
674 | |
675 | static SDL_AudioDevice *CreateAudioPlaybackDevice(const char *name, const SDL_AudioSpec *spec, void *handle) |
676 | { |
677 | return CreatePhysicalAudioDevice(name, false, spec, handle, ¤t_audio.playback_device_count); |
678 | } |
679 | |
680 | // The audio backends call this when a new device is plugged in. |
681 | SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_AudioSpec *inspec, void *handle) |
682 | { |
683 | // device handles MUST be unique! If the target reuses the same handle for hardware with both recording and playback interfaces, wrap it in a pointer you SDL_malloc'd! |
684 | SDL_assert(SDL_FindPhysicalAudioDeviceByHandle(handle) == NULL); |
685 | |
686 | const SDL_AudioFormat default_format = recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT; |
687 | const int default_channels = recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; |
688 | const int default_freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; |
689 | |
690 | SDL_AudioSpec spec; |
691 | SDL_zero(spec); |
692 | if (!inspec) { |
693 | spec.format = default_format; |
694 | spec.channels = default_channels; |
695 | spec.freq = default_freq; |
696 | } else { |
697 | spec.format = (inspec->format != 0) ? inspec->format : default_format; |
698 | spec.channels = (inspec->channels != 0) ? inspec->channels : default_channels; |
699 | spec.freq = (inspec->freq != 0) ? inspec->freq : default_freq; |
700 | } |
701 | |
702 | SDL_AudioDevice *device = recording ? CreateAudioRecordingDevice(name, &spec, handle) : CreateAudioPlaybackDevice(name, &spec, handle); |
703 | |
704 | // Add a device add event to the pending list, to be pushed when the event queue is pumped (away from any of our internal threads). |
705 | if (device) { |
706 | SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); |
707 | if (p) { // if allocation fails, you won't get an event, but we can't help that. |
708 | p->type = SDL_EVENT_AUDIO_DEVICE_ADDED; |
709 | p->devid = device->instance_id; |
710 | p->next = NULL; |
711 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
712 | SDL_assert(current_audio.pending_events_tail != NULL); |
713 | SDL_assert(current_audio.pending_events_tail->next == NULL); |
714 | current_audio.pending_events_tail->next = p; |
715 | current_audio.pending_events_tail = p; |
716 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
717 | } |
718 | } |
719 | |
720 | return device; |
721 | } |
722 | |
723 | // Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the audio device's thread. |
724 | void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) |
725 | { |
726 | if (!device) { |
727 | return; |
728 | } |
729 | |
730 | // Save off removal info in a list so we can send events for each, next |
731 | // time the event queue pumps, in case something tries to close a device |
732 | // from an event filter, as this would risk deadlocks and other disasters |
733 | // if done from the device thread. |
734 | SDL_PendingAudioDeviceEvent pending; |
735 | pending.next = NULL; |
736 | SDL_PendingAudioDeviceEvent *pending_tail = &pending; |
737 | |
738 | ObtainPhysicalAudioDeviceObj(device); |
739 | |
740 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
741 | const SDL_AudioDeviceID devid = device->instance_id; |
742 | const bool is_default_device = ((devid == current_audio.default_playback_device_id) || (devid == current_audio.default_recording_device_id)); |
743 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
744 | |
745 | const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 1); |
746 | if (first_disconnect) { // if already disconnected this device, don't do it twice. |
747 | // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep |
748 | // making progress until the app closes it. Otherwise, streams might continue to |
749 | // accumulate waste data that never drains, apps that depend on audio callbacks to |
750 | // progress will freeze, etc. |
751 | device->WaitDevice = ZombieWaitDevice; |
752 | device->GetDeviceBuf = ZombieGetDeviceBuf; |
753 | device->PlayDevice = ZombiePlayDevice; |
754 | device->WaitRecordingDevice = ZombieWaitDevice; |
755 | device->RecordDevice = ZombieRecordDevice; |
756 | device->FlushRecording = ZombieFlushRecording; |
757 | |
758 | // on default devices, dump any logical devices that explicitly opened this device. Things that opened the system default can stay. |
759 | // on non-default devices, dump everything. |
760 | // (by "dump" we mean send a REMOVED event; the zombie will keep consuming audio data for these logical devices until explicitly closed.) |
761 | for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { |
762 | if (!is_default_device || !logdev->opened_as_default) { // if opened as a default, leave it on the zombie device for later migration. |
763 | SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); |
764 | if (p) { // if this failed, no event for you, but you have deeper problems anyhow. |
765 | p->type = SDL_EVENT_AUDIO_DEVICE_REMOVED; |
766 | p->devid = logdev->instance_id; |
767 | p->next = NULL; |
768 | pending_tail->next = p; |
769 | pending_tail = p; |
770 | } |
771 | } |
772 | } |
773 | |
774 | SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); |
775 | if (p) { // if this failed, no event for you, but you have deeper problems anyhow. |
776 | p->type = SDL_EVENT_AUDIO_DEVICE_REMOVED; |
777 | p->devid = device->instance_id; |
778 | p->next = NULL; |
779 | pending_tail->next = p; |
780 | pending_tail = p; |
781 | } |
782 | } |
783 | |
784 | ReleaseAudioDevice(device); |
785 | |
786 | if (first_disconnect) { |
787 | if (pending.next) { // NULL if event is disabled or disaster struck. |
788 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
789 | SDL_assert(current_audio.pending_events_tail != NULL); |
790 | SDL_assert(current_audio.pending_events_tail->next == NULL); |
791 | current_audio.pending_events_tail->next = pending.next; |
792 | current_audio.pending_events_tail = pending_tail; |
793 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
794 | } |
795 | |
796 | UnrefPhysicalAudioDevice(device); |
797 | } |
798 | } |
799 | |
800 | |
801 | // stubs for audio drivers that don't need a specific entry point... |
802 | |
803 | static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ } |
804 | static bool SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { return true; /* no-op. */ } |
805 | static bool SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return true; /* no-op. */ } |
806 | static bool SDL_AudioWaitRecordingDevice_Default(SDL_AudioDevice *device) { return true; /* no-op. */ } |
807 | static void SDL_AudioFlushRecording_Default(SDL_AudioDevice *device) { /* no-op. */ } |
808 | static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } |
809 | static void SDL_AudioDeinitializeStart_Default(void) { /* no-op. */ } |
810 | static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ } |
811 | static void SDL_AudioFreeDeviceHandle_Default(SDL_AudioDevice *device) { /* no-op. */ } |
812 | |
813 | static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) |
814 | { |
815 | SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); |
816 | } |
817 | |
818 | static void SDL_AudioDetectDevices_Default(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) |
819 | { |
820 | // you have to write your own implementation if these assertions fail. |
821 | SDL_assert(current_audio.impl.OnlyHasDefaultPlaybackDevice); |
822 | SDL_assert(current_audio.impl.OnlyHasDefaultRecordingDevice || !current_audio.impl.HasRecordingSupport); |
823 | |
824 | *default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)((size_t)0x1)); |
825 | if (current_audio.impl.HasRecordingSupport) { |
826 | *default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)((size_t)0x2)); |
827 | } |
828 | } |
829 | |
830 | static Uint8 *SDL_AudioGetDeviceBuf_Default(SDL_AudioDevice *device, int *buffer_size) |
831 | { |
832 | *buffer_size = 0; |
833 | return NULL; |
834 | } |
835 | |
836 | static int SDL_AudioRecordDevice_Default(SDL_AudioDevice *device, void *buffer, int buflen) |
837 | { |
838 | SDL_Unsupported(); |
839 | return -1; |
840 | } |
841 | |
842 | static bool SDL_AudioOpenDevice_Default(SDL_AudioDevice *device) |
843 | { |
844 | return SDL_Unsupported(); |
845 | } |
846 | |
847 | // Fill in stub functions for unused driver entry points. This lets us blindly call them without having to check for validity first. |
848 | static void CompleteAudioEntryPoints(void) |
849 | { |
850 | #define FILL_STUB(x) if (!current_audio.impl.x) { current_audio.impl.x = SDL_Audio##x##_Default; } |
851 | FILL_STUB(DetectDevices); |
852 | FILL_STUB(OpenDevice); |
853 | FILL_STUB(ThreadInit); |
854 | FILL_STUB(ThreadDeinit); |
855 | FILL_STUB(WaitDevice); |
856 | FILL_STUB(PlayDevice); |
857 | FILL_STUB(GetDeviceBuf); |
858 | FILL_STUB(WaitRecordingDevice); |
859 | FILL_STUB(RecordDevice); |
860 | FILL_STUB(FlushRecording); |
861 | FILL_STUB(CloseDevice); |
862 | FILL_STUB(FreeDeviceHandle); |
863 | FILL_STUB(DeinitializeStart); |
864 | FILL_STUB(Deinitialize); |
865 | #undef FILL_STUB |
866 | } |
867 | |
868 | typedef struct FindLowestDeviceIDData |
869 | { |
870 | const bool recording; |
871 | SDL_AudioDeviceID highest; |
872 | SDL_AudioDevice *result; |
873 | } FindLowestDeviceIDData; |
874 | |
875 | static bool SDLCALL FindLowestDeviceID(void *userdata, const SDL_HashTable *table, const void *key, const void *value) |
876 | { |
877 | FindLowestDeviceIDData *data = (FindLowestDeviceIDData *) userdata; |
878 | const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; |
879 | // bit #0 of devid is set for playback devices and unset for recording. |
880 | // bit #1 of devid is set for physical devices and unset for logical. |
881 | const bool devid_recording = !(devid & (1 << 0)); |
882 | const bool isphysical = !!(devid & (1 << 1)); |
883 | if (isphysical && (devid_recording == data->recording) && (devid < data->highest)) { |
884 | data->highest = devid; |
885 | data->result = (SDL_AudioDevice *) value; |
886 | } |
887 | return true; // keep iterating. |
888 | } |
889 | |
890 | static SDL_AudioDevice *GetFirstAddedAudioDevice(const bool recording) |
891 | { |
892 | const SDL_AudioDeviceID highest = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; // According to AssignAudioDeviceInstanceId, nothing can have a value this large. |
893 | |
894 | // (Device IDs increase as new devices are added, so the first device added has the lowest SDL_AudioDeviceID value.) |
895 | FindLowestDeviceIDData data = { recording, highest, NULL }; |
896 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
897 | SDL_IterateHashTable(current_audio.device_hash, FindLowestDeviceID, &data); |
898 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
899 | return data.result; |
900 | } |
901 | |
902 | static Uint32 SDLCALL HashAudioDeviceID(void *userdata, const void *key) |
903 | { |
904 | // shift right 2, to dump the first two bits, since these are flags |
905 | // (recording vs playback, logical vs physical) and the rest are unique incrementing integers. |
906 | return ((Uint32) ((uintptr_t) key)) >> 2; |
907 | } |
908 | |
909 | // !!! FIXME: the video subsystem does SDL_VideoInit, not SDL_InitVideo. Make this match. |
910 | bool SDL_InitAudio(const char *driver_name) |
911 | { |
912 | if (SDL_GetCurrentAudioDriver()) { |
913 | SDL_QuitAudio(); // shutdown driver if already running. |
914 | } |
915 | |
916 | // make sure device IDs start at 2 (because of SDL2 legacy interface), but don't reset the counter on each init, in case the app is holding an old device ID somewhere. |
917 | SDL_CompareAndSwapAtomicInt(&last_device_instance_id, 0, 2); |
918 | |
919 | SDL_ChooseAudioConverters(); |
920 | SDL_SetupAudioResampler(); |
921 | |
922 | SDL_RWLock *device_hash_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem. |
923 | if (!device_hash_lock) { |
924 | return false; |
925 | } |
926 | |
927 | SDL_HashTable *device_hash = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); |
928 | if (!device_hash) { |
929 | SDL_DestroyRWLock(device_hash_lock); |
930 | return false; |
931 | } |
932 | |
933 | // Select the proper audio driver |
934 | if (!driver_name) { |
935 | driver_name = SDL_GetHint(SDL_HINT_AUDIO_DRIVER); |
936 | } |
937 | |
938 | bool initialized = false; |
939 | bool tried_to_init = false; |
940 | |
941 | if (driver_name && *driver_name != 0) { |
942 | char *driver_name_copy = SDL_strdup(driver_name); |
943 | const char *driver_attempt = driver_name_copy; |
944 | |
945 | if (!driver_name_copy) { |
946 | SDL_DestroyRWLock(device_hash_lock); |
947 | SDL_DestroyHashTable(device_hash); |
948 | return false; |
949 | } |
950 | |
951 | while (driver_attempt && *driver_attempt != 0 && !initialized) { |
952 | char *driver_attempt_end = SDL_strchr(driver_attempt, ','); |
953 | if (driver_attempt_end) { |
954 | *driver_attempt_end = '\0'; |
955 | } |
956 | |
957 | // SDL 1.2 uses the name "dsound", so we'll support both. |
958 | if (SDL_strcmp(driver_attempt, "dsound" ) == 0) { |
959 | driver_attempt = "directsound" ; |
960 | } else if (SDL_strcmp(driver_attempt, "pulse" ) == 0) { // likewise, "pulse" was renamed to "pulseaudio" |
961 | driver_attempt = "pulseaudio" ; |
962 | } |
963 | |
964 | for (int i = 0; bootstrap[i]; ++i) { |
965 | if (!bootstrap[i]->is_preferred && SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { |
966 | tried_to_init = true; |
967 | SDL_zero(current_audio); |
968 | current_audio.pending_events_tail = ¤t_audio.pending_events; |
969 | current_audio.device_hash_lock = device_hash_lock; |
970 | current_audio.device_hash = device_hash; |
971 | if (bootstrap[i]->init(¤t_audio.impl)) { |
972 | current_audio.name = bootstrap[i]->name; |
973 | current_audio.desc = bootstrap[i]->desc; |
974 | initialized = true; |
975 | break; |
976 | } |
977 | } |
978 | } |
979 | |
980 | driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; |
981 | } |
982 | |
983 | SDL_free(driver_name_copy); |
984 | } else { |
985 | for (int i = 0; (!initialized) && (bootstrap[i]); ++i) { |
986 | if (bootstrap[i]->demand_only) { |
987 | continue; |
988 | } |
989 | |
990 | tried_to_init = true; |
991 | SDL_zero(current_audio); |
992 | current_audio.pending_events_tail = ¤t_audio.pending_events; |
993 | current_audio.device_hash_lock = device_hash_lock; |
994 | current_audio.device_hash = device_hash; |
995 | if (bootstrap[i]->init(¤t_audio.impl)) { |
996 | current_audio.name = bootstrap[i]->name; |
997 | current_audio.desc = bootstrap[i]->desc; |
998 | initialized = true; |
999 | } |
1000 | } |
1001 | } |
1002 | |
1003 | if (!initialized) { |
1004 | // specific drivers will set the error message if they fail, but otherwise we do it here. |
1005 | if (!tried_to_init) { |
1006 | if (driver_name) { |
1007 | SDL_SetError("Audio target '%s' not available" , driver_name); |
1008 | } else { |
1009 | SDL_SetError("No available audio device" ); |
1010 | } |
1011 | } |
1012 | |
1013 | SDL_DestroyRWLock(device_hash_lock); |
1014 | SDL_DestroyHashTable(device_hash); |
1015 | SDL_zero(current_audio); |
1016 | return false; // No driver was available, so fail. |
1017 | } |
1018 | |
1019 | CompleteAudioEntryPoints(); |
1020 | |
1021 | // Make sure we have a list of devices available at startup... |
1022 | SDL_AudioDevice *default_playback = NULL; |
1023 | SDL_AudioDevice *default_recording = NULL; |
1024 | current_audio.impl.DetectDevices(&default_playback, &default_recording); |
1025 | |
1026 | // If no default was _ever_ specified, just take the first device we see, if any. |
1027 | if (!default_playback) { |
1028 | default_playback = GetFirstAddedAudioDevice(/*recording=*/false); |
1029 | } |
1030 | |
1031 | if (!default_recording) { |
1032 | default_recording = GetFirstAddedAudioDevice(/*recording=*/true); |
1033 | } |
1034 | |
1035 | if (default_playback) { |
1036 | current_audio.default_playback_device_id = default_playback->instance_id; |
1037 | RefPhysicalAudioDevice(default_playback); // extra ref on default devices. |
1038 | } |
1039 | |
1040 | if (default_recording) { |
1041 | current_audio.default_recording_device_id = default_recording->instance_id; |
1042 | RefPhysicalAudioDevice(default_recording); // extra ref on default devices. |
1043 | } |
1044 | |
1045 | return true; |
1046 | } |
1047 | |
1048 | static bool SDLCALL DestroyOnePhysicalAudioDevice(void *userdata, const SDL_HashTable *table, const void *key, const void *value) |
1049 | { |
1050 | // bit #1 of devid is set for physical devices and unset for logical. |
1051 | const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; |
1052 | const bool isphysical = !!(devid & (1<<1)); |
1053 | if (isphysical) { |
1054 | DestroyPhysicalAudioDevice((SDL_AudioDevice *) value); |
1055 | } |
1056 | return true; // keep iterating. |
1057 | } |
1058 | |
1059 | void SDL_QuitAudio(void) |
1060 | { |
1061 | if (!current_audio.name) { // not initialized?! |
1062 | return; |
1063 | } |
1064 | |
1065 | current_audio.impl.DeinitializeStart(); |
1066 | |
1067 | // Destroy any audio streams that still exist... |
1068 | while (current_audio.existing_streams) { |
1069 | SDL_DestroyAudioStream(current_audio.existing_streams); |
1070 | } |
1071 | |
1072 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
1073 | SDL_SetAtomicInt(¤t_audio.shutting_down, 1); |
1074 | SDL_HashTable *device_hash = current_audio.device_hash; |
1075 | current_audio.device_hash = NULL; |
1076 | SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; |
1077 | current_audio.pending_events.next = NULL; |
1078 | SDL_SetAtomicInt(¤t_audio.playback_device_count, 0); |
1079 | SDL_SetAtomicInt(¤t_audio.recording_device_count, 0); |
1080 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
1081 | |
1082 | SDL_PendingAudioDeviceEvent *pending_next = NULL; |
1083 | for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { |
1084 | pending_next = i->next; |
1085 | SDL_free(i); |
1086 | } |
1087 | |
1088 | SDL_IterateHashTable(device_hash, DestroyOnePhysicalAudioDevice, NULL); |
1089 | |
1090 | // Free the driver data |
1091 | current_audio.impl.Deinitialize(); |
1092 | |
1093 | SDL_DestroyRWLock(current_audio.device_hash_lock); |
1094 | SDL_DestroyHashTable(device_hash); |
1095 | |
1096 | SDL_zero(current_audio); |
1097 | } |
1098 | |
1099 | |
1100 | void SDL_AudioThreadFinalize(SDL_AudioDevice *device) |
1101 | { |
1102 | } |
1103 | |
1104 | static void MixFloat32Audio(float *dst, const float *src, const int buffer_size) |
1105 | { |
1106 | if (!SDL_MixAudio((Uint8 *) dst, (const Uint8 *) src, SDL_AUDIO_F32, buffer_size, 1.0f)) { |
1107 | SDL_assert(!"This shouldn't happen." ); |
1108 | } |
1109 | } |
1110 | |
1111 | |
1112 | // Playback device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. |
1113 | |
1114 | void SDL_PlaybackAudioThreadSetup(SDL_AudioDevice *device) |
1115 | { |
1116 | SDL_assert(!device->recording); |
1117 | current_audio.impl.ThreadInit(device); |
1118 | } |
1119 | |
1120 | bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device) |
1121 | { |
1122 | SDL_assert(!device->recording); |
1123 | |
1124 | SDL_LockMutex(device->lock); |
1125 | |
1126 | if (SDL_GetAtomicInt(&device->shutdown)) { |
1127 | SDL_UnlockMutex(device->lock); |
1128 | return false; // we're done, shut it down. |
1129 | } |
1130 | |
1131 | bool failed = false; |
1132 | int buffer_size = device->buffer_size; |
1133 | Uint8 *device_buffer = device->GetDeviceBuf(device, &buffer_size); |
1134 | if (buffer_size == 0) { |
1135 | // WASAPI (maybe others, later) does this to say "just abandon this iteration and try again next time." |
1136 | } else if (!device_buffer) { |
1137 | failed = true; |
1138 | } else { |
1139 | SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more. |
1140 | SDL_assert(AudioDeviceCanUseSimpleCopy(device) == device->simple_copy); // make sure this hasn't gotten out of sync. |
1141 | |
1142 | // can we do a basic copy without silencing/mixing the buffer? This is an extremely likely scenario, so we special-case it. |
1143 | if (device->simple_copy) { |
1144 | SDL_LogicalAudioDevice *logdev = device->logical_devices; |
1145 | SDL_AudioStream *stream = logdev->bound_streams; |
1146 | |
1147 | // We should have updated this elsewhere if the format changed! |
1148 | SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec, NULL, NULL)); |
1149 | |
1150 | const int br = SDL_GetAtomicInt(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain); |
1151 | if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. |
1152 | failed = true; |
1153 | SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die. |
1154 | } else if (br < buffer_size) { |
1155 | SDL_memset(device_buffer + br, device->silence_value, buffer_size - br); // silence whatever we didn't write to. |
1156 | } |
1157 | |
1158 | // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout. |
1159 | if ((br > 0) && (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->dst_chmap, device->chmap))) { |
1160 | ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), device_buffer, device->spec.format, device->spec.channels, NULL, |
1161 | device_buffer, device->spec.format, device->spec.channels, device->chmap, NULL, 1.0f); |
1162 | } |
1163 | } else { // need to actually mix (or silence the buffer) |
1164 | float *final_mix_buffer = (float *) ((device->spec.format == SDL_AUDIO_F32) ? device_buffer : device->mix_buffer); |
1165 | const int needed_samples = buffer_size / SDL_AUDIO_BYTESIZE(device->spec.format); |
1166 | const int work_buffer_size = needed_samples * sizeof (float); |
1167 | SDL_AudioSpec outspec; |
1168 | |
1169 | SDL_assert(work_buffer_size <= device->work_buffer_size); |
1170 | |
1171 | SDL_copyp(&outspec, &device->spec); |
1172 | outspec.format = SDL_AUDIO_F32; |
1173 | |
1174 | SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence. |
1175 | |
1176 | for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { |
1177 | if (SDL_GetAtomicInt(&logdev->paused)) { |
1178 | continue; // paused? Skip this logical device. |
1179 | } |
1180 | |
1181 | const SDL_AudioPostmixCallback postmix = logdev->postmix; |
1182 | float *mix_buffer = final_mix_buffer; |
1183 | if (postmix) { |
1184 | mix_buffer = device->postmix_buffer; |
1185 | SDL_memset(mix_buffer, '\0', work_buffer_size); // start with silence. |
1186 | } |
1187 | |
1188 | for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { |
1189 | // We should have updated this elsewhere if the format changed! |
1190 | SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec, NULL, NULL)); |
1191 | |
1192 | /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams |
1193 | for iterating here because the binding linked list can only change while the device lock is held. |
1194 | (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind |
1195 | the same stream to different devices at the same time, though.) */ |
1196 | const int br = SDL_GetAudioStreamDataAdjustGain(stream, device->work_buffer, work_buffer_size, logdev->gain); |
1197 | if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. |
1198 | failed = true; |
1199 | break; |
1200 | } else if (br > 0) { // it's okay if we get less than requested, we mix what we have. |
1201 | // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout. |
1202 | if (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->dst_chmap, device->chmap)) { |
1203 | ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), device->work_buffer, device->spec.format, device->spec.channels, NULL, |
1204 | device->work_buffer, device->spec.format, device->spec.channels, device->chmap, NULL, 1.0f); |
1205 | } |
1206 | MixFloat32Audio(mix_buffer, (float *) device->work_buffer, br); |
1207 | } |
1208 | } |
1209 | |
1210 | if (postmix) { |
1211 | SDL_assert(mix_buffer == device->postmix_buffer); |
1212 | postmix(logdev->postmix_userdata, &outspec, mix_buffer, work_buffer_size); |
1213 | MixFloat32Audio(final_mix_buffer, mix_buffer, work_buffer_size); |
1214 | } |
1215 | } |
1216 | |
1217 | if (((Uint8 *) final_mix_buffer) != device_buffer) { |
1218 | // !!! FIXME: we can't promise the device buf is aligned/padded for SIMD. |
1219 | //ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f); |
1220 | ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device->work_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f); |
1221 | SDL_memcpy(device_buffer, device->work_buffer, buffer_size); |
1222 | } |
1223 | } |
1224 | |
1225 | // PlayDevice SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! |
1226 | if (!device->PlayDevice(device, device_buffer, buffer_size)) { |
1227 | failed = true; |
1228 | } |
1229 | } |
1230 | |
1231 | SDL_UnlockMutex(device->lock); |
1232 | |
1233 | if (failed) { |
1234 | SDL_AudioDeviceDisconnected(device); // doh. |
1235 | } |
1236 | |
1237 | return true; // always go on if not shutting down, even if device failed. |
1238 | } |
1239 | |
1240 | void SDL_PlaybackAudioThreadShutdown(SDL_AudioDevice *device) |
1241 | { |
1242 | SDL_assert(!device->recording); |
1243 | const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); |
1244 | // Wait for the audio to drain if device didn't die. |
1245 | if (!SDL_GetAtomicInt(&device->zombie)) { |
1246 | SDL_Delay(((frames * 1000) / device->spec.freq) * 2); |
1247 | } |
1248 | current_audio.impl.ThreadDeinit(device); |
1249 | SDL_AudioThreadFinalize(device); |
1250 | } |
1251 | |
1252 | static int SDLCALL PlaybackAudioThread(void *devicep) // thread entry point |
1253 | { |
1254 | SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; |
1255 | SDL_assert(device != NULL); |
1256 | SDL_assert(!device->recording); |
1257 | SDL_PlaybackAudioThreadSetup(device); |
1258 | |
1259 | do { |
1260 | if (!device->WaitDevice(device)) { |
1261 | SDL_AudioDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) |
1262 | } |
1263 | } while (SDL_PlaybackAudioThreadIterate(device)); |
1264 | |
1265 | SDL_PlaybackAudioThreadShutdown(device); |
1266 | return 0; |
1267 | } |
1268 | |
1269 | |
1270 | |
1271 | // Recording device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. |
1272 | |
1273 | void SDL_RecordingAudioThreadSetup(SDL_AudioDevice *device) |
1274 | { |
1275 | SDL_assert(device->recording); |
1276 | current_audio.impl.ThreadInit(device); |
1277 | } |
1278 | |
1279 | bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device) |
1280 | { |
1281 | SDL_assert(device->recording); |
1282 | |
1283 | SDL_LockMutex(device->lock); |
1284 | |
1285 | if (SDL_GetAtomicInt(&device->shutdown)) { |
1286 | SDL_UnlockMutex(device->lock); |
1287 | return false; // we're done, shut it down. |
1288 | } |
1289 | |
1290 | bool failed = false; |
1291 | |
1292 | if (!device->logical_devices) { |
1293 | device->FlushRecording(device); // nothing wants data, dump anything pending. |
1294 | } else { |
1295 | // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitRecordingDevice! |
1296 | int br = device->RecordDevice(device, device->work_buffer, device->buffer_size); |
1297 | if (br < 0) { // uhoh, device failed for some reason! |
1298 | failed = true; |
1299 | } else if (br > 0) { // queue the new data to each bound stream. |
1300 | for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { |
1301 | if (SDL_GetAtomicInt(&logdev->paused)) { |
1302 | continue; // paused? Skip this logical device. |
1303 | } |
1304 | |
1305 | void *output_buffer = device->work_buffer; |
1306 | |
1307 | // I don't know why someone would want a postmix on a recording device, but we offer it for API consistency. |
1308 | if (logdev->postmix || (logdev->gain != 1.0f)) { |
1309 | // move to float format. |
1310 | SDL_AudioSpec outspec; |
1311 | SDL_copyp(&outspec, &device->spec); |
1312 | outspec.format = SDL_AUDIO_F32; |
1313 | output_buffer = device->postmix_buffer; |
1314 | const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec); |
1315 | br = frames * SDL_AUDIO_FRAMESIZE(outspec); |
1316 | ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, NULL, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL, NULL, logdev->gain); |
1317 | if (logdev->postmix) { |
1318 | logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br); |
1319 | } |
1320 | } |
1321 | |
1322 | for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { |
1323 | // We should have updated this elsewhere if the format changed! |
1324 | SDL_assert(stream->src_spec.format == ((logdev->postmix || (logdev->gain != 1.0f)) ? SDL_AUDIO_F32 : device->spec.format)); |
1325 | SDL_assert(stream->src_spec.channels == device->spec.channels); |
1326 | SDL_assert(stream->src_spec.freq == device->spec.freq); |
1327 | |
1328 | void *final_buf = output_buffer; |
1329 | |
1330 | // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to stream layout. |
1331 | if (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->src_chmap, device->chmap)) { |
1332 | final_buf = device->mix_buffer; // this is otherwise unused on recording devices, so it makes convenient scratch space here. |
1333 | ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), output_buffer, device->spec.format, device->spec.channels, NULL, |
1334 | final_buf, device->spec.format, device->spec.channels, stream->src_chmap, NULL, 1.0f); |
1335 | } |
1336 | |
1337 | /* this will hold a lock on `stream` while putting. We don't explicitly lock the streams |
1338 | for iterating here because the binding linked list can only change while the device lock is held. |
1339 | (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind |
1340 | the same stream to different devices at the same time, though.) */ |
1341 | if (!SDL_PutAudioStreamData(stream, final_buf, br)) { |
1342 | // oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow. |
1343 | failed = true; |
1344 | break; |
1345 | } |
1346 | } |
1347 | } |
1348 | } |
1349 | } |
1350 | |
1351 | SDL_UnlockMutex(device->lock); |
1352 | |
1353 | if (failed) { |
1354 | SDL_AudioDeviceDisconnected(device); // doh. |
1355 | } |
1356 | |
1357 | return true; // always go on if not shutting down, even if device failed. |
1358 | } |
1359 | |
1360 | void SDL_RecordingAudioThreadShutdown(SDL_AudioDevice *device) |
1361 | { |
1362 | SDL_assert(device->recording); |
1363 | device->FlushRecording(device); |
1364 | current_audio.impl.ThreadDeinit(device); |
1365 | SDL_AudioThreadFinalize(device); |
1366 | } |
1367 | |
1368 | static int SDLCALL RecordingAudioThread(void *devicep) // thread entry point |
1369 | { |
1370 | SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; |
1371 | SDL_assert(device != NULL); |
1372 | SDL_assert(device->recording); |
1373 | SDL_RecordingAudioThreadSetup(device); |
1374 | |
1375 | do { |
1376 | if (!device->WaitRecordingDevice(device)) { |
1377 | SDL_AudioDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) |
1378 | } |
1379 | } while (SDL_RecordingAudioThreadIterate(device)); |
1380 | |
1381 | SDL_RecordingAudioThreadShutdown(device); |
1382 | return 0; |
1383 | } |
1384 | |
1385 | typedef struct CountAudioDevicesData |
1386 | { |
1387 | int devs_seen; |
1388 | const int num_devices; |
1389 | SDL_AudioDeviceID *result; |
1390 | const bool recording; |
1391 | } CountAudioDevicesData; |
1392 | |
1393 | static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table, const void *key, const void *value) |
1394 | { |
1395 | CountAudioDevicesData *data = (CountAudioDevicesData *) userdata; |
1396 | const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; |
1397 | // bit #0 of devid is set for playback devices and unset for recording. |
1398 | // bit #1 of devid is set for physical devices and unset for logical. |
1399 | const bool devid_recording = !(devid & (1<<0)); |
1400 | const bool isphysical = !!(devid & (1<<1)); |
1401 | if (isphysical && (devid_recording == data->recording)) { |
1402 | SDL_assert(data->devs_seen < data->num_devices); |
1403 | data->result[data->devs_seen++] = devid; |
1404 | } |
1405 | return true; // keep iterating. |
1406 | } |
1407 | |
1408 | static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) |
1409 | { |
1410 | SDL_AudioDeviceID *result = NULL; |
1411 | int num_devices = 0; |
1412 | |
1413 | if (SDL_GetCurrentAudioDriver()) { |
1414 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
1415 | { |
1416 | num_devices = SDL_GetAtomicInt(recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count); |
1417 | result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); |
1418 | if (result) { |
1419 | CountAudioDevicesData data = { 0, num_devices, result, recording }; |
1420 | SDL_IterateHashTable(current_audio.device_hash, CountAudioDevices, &data); |
1421 | SDL_assert(data.devs_seen == num_devices); |
1422 | result[data.devs_seen] = 0; // null-terminated. |
1423 | } |
1424 | } |
1425 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
1426 | } else { |
1427 | SDL_SetError("Audio subsystem is not initialized" ); |
1428 | } |
1429 | |
1430 | if (count) { |
1431 | if (result) { |
1432 | *count = num_devices; |
1433 | } else { |
1434 | *count = 0; |
1435 | } |
1436 | } |
1437 | return result; |
1438 | } |
1439 | |
1440 | SDL_AudioDeviceID *SDL_GetAudioPlaybackDevices(int *count) |
1441 | { |
1442 | return GetAudioDevices(count, false); |
1443 | } |
1444 | |
1445 | SDL_AudioDeviceID *SDL_GetAudioRecordingDevices(int *count) |
1446 | { |
1447 | return GetAudioDevices(count, true); |
1448 | } |
1449 | |
1450 | typedef struct FindAudioDeviceByCallbackData |
1451 | { |
1452 | bool (*callback)(SDL_AudioDevice *device, void *userdata); |
1453 | void *userdata; |
1454 | SDL_AudioDevice *retval; |
1455 | } FindAudioDeviceByCallbackData; |
1456 | |
1457 | static bool SDLCALL FindAudioDeviceByCallback(void *userdata, const SDL_HashTable *table, const void *key, const void *value) |
1458 | { |
1459 | FindAudioDeviceByCallbackData *data = (FindAudioDeviceByCallbackData *) userdata; |
1460 | const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; |
1461 | // bit #1 of devid is set for physical devices and unset for logical. |
1462 | const bool isphysical = !!(devid & (1<<1)); |
1463 | if (isphysical) { |
1464 | SDL_AudioDevice *device = (SDL_AudioDevice *) value; |
1465 | if (data->callback(device, data->userdata)) { // found it? |
1466 | data->retval = device; |
1467 | return false; // stop iterating, we found it. |
1468 | } |
1469 | } |
1470 | return true; // keep iterating. |
1471 | } |
1472 | |
1473 | // !!! FIXME: SDL convention is for userdata to come first in the callback's params. Fix this at some point. |
1474 | SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata) |
1475 | { |
1476 | if (!SDL_GetCurrentAudioDriver()) { |
1477 | SDL_SetError("Audio subsystem is not initialized" ); |
1478 | return NULL; |
1479 | } |
1480 | |
1481 | FindAudioDeviceByCallbackData data = { callback, userdata, NULL }; |
1482 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
1483 | SDL_IterateHashTable(current_audio.device_hash, FindAudioDeviceByCallback, &data); |
1484 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
1485 | |
1486 | if (!data.retval) { |
1487 | SDL_SetError("Device not found" ); |
1488 | } |
1489 | |
1490 | return data.retval; |
1491 | } |
1492 | |
1493 | static bool TestDeviceHandleCallback(SDL_AudioDevice *device, void *handle) |
1494 | { |
1495 | return device->handle == handle; |
1496 | } |
1497 | |
1498 | SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle) |
1499 | { |
1500 | return SDL_FindPhysicalAudioDeviceByCallback(TestDeviceHandleCallback, handle); |
1501 | } |
1502 | |
1503 | const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) |
1504 | { |
1505 | const char *result = NULL; |
1506 | SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); |
1507 | if (device) { |
1508 | result = SDL_GetPersistentString(device->name); |
1509 | } |
1510 | ReleaseAudioDevice(device); |
1511 | |
1512 | return result; |
1513 | } |
1514 | |
1515 | bool SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames) |
1516 | { |
1517 | if (!spec) { |
1518 | return SDL_InvalidParamError("spec" ); |
1519 | } |
1520 | |
1521 | bool result = false; |
1522 | SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); |
1523 | if (device) { |
1524 | SDL_copyp(spec, &device->spec); |
1525 | if (sample_frames) { |
1526 | *sample_frames = device->sample_frames; |
1527 | } |
1528 | result = true; |
1529 | } |
1530 | ReleaseAudioDevice(device); |
1531 | |
1532 | return result; |
1533 | } |
1534 | |
1535 | int *SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count) |
1536 | { |
1537 | int *result = NULL; |
1538 | int channels = 0; |
1539 | SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); |
1540 | if (device) { |
1541 | channels = device->spec.channels; |
1542 | result = SDL_ChannelMapDup(device->chmap, channels); |
1543 | } |
1544 | ReleaseAudioDevice(device); |
1545 | |
1546 | if (count) { |
1547 | *count = channels; |
1548 | } |
1549 | |
1550 | return result; |
1551 | } |
1552 | |
1553 | |
1554 | // this is awkward, but this makes sure we can release the device lock |
1555 | // so the device thread can terminate but also not have two things |
1556 | // race to close or open the device while the lock is unprotected. |
1557 | // you hold the lock when calling this, it will release the lock and |
1558 | // wait while the shutdown flag is set. |
1559 | // BE CAREFUL WITH THIS. |
1560 | static void SerializePhysicalDeviceClose(SDL_AudioDevice *device) |
1561 | { |
1562 | while (SDL_GetAtomicInt(&device->shutdown)) { |
1563 | SDL_WaitCondition(device->close_cond, device->lock); |
1564 | } |
1565 | } |
1566 | |
1567 | // this expects the device lock to be held. |
1568 | static void ClosePhysicalAudioDevice(SDL_AudioDevice *device) |
1569 | { |
1570 | SerializePhysicalDeviceClose(device); |
1571 | |
1572 | SDL_SetAtomicInt(&device->shutdown, 1); |
1573 | |
1574 | // YOU MUST PROTECT KEY POINTS WITH SerializePhysicalDeviceClose() WHILE THE THREAD JOINS |
1575 | SDL_UnlockMutex(device->lock); |
1576 | |
1577 | if (device->thread) { |
1578 | SDL_WaitThread(device->thread, NULL); |
1579 | device->thread = NULL; |
1580 | } |
1581 | |
1582 | if (device->currently_opened) { |
1583 | current_audio.impl.CloseDevice(device); // if ProvidesOwnCallbackThread, this must join on any existing device thread before returning! |
1584 | device->currently_opened = false; |
1585 | device->hidden = NULL; // just in case. |
1586 | } |
1587 | |
1588 | SDL_LockMutex(device->lock); |
1589 | SDL_SetAtomicInt(&device->shutdown, 0); // ready to go again. |
1590 | SDL_BroadcastCondition(device->close_cond); // release anyone waiting in SerializePhysicalDeviceClose; they'll still block until we release device->lock, though. |
1591 | |
1592 | SDL_aligned_free(device->work_buffer); |
1593 | device->work_buffer = NULL; |
1594 | |
1595 | SDL_aligned_free(device->mix_buffer); |
1596 | device->mix_buffer = NULL; |
1597 | |
1598 | SDL_aligned_free(device->postmix_buffer); |
1599 | device->postmix_buffer = NULL; |
1600 | |
1601 | SDL_copyp(&device->spec, &device->default_spec); |
1602 | device->sample_frames = 0; |
1603 | device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); |
1604 | } |
1605 | |
1606 | void SDL_CloseAudioDevice(SDL_AudioDeviceID devid) |
1607 | { |
1608 | SDL_AudioDevice *device = NULL; |
1609 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1610 | if (logdev) { |
1611 | DestroyLogicalAudioDevice(logdev); |
1612 | } |
1613 | |
1614 | if (device) { |
1615 | if (!device->logical_devices) { // no more logical devices? Close the physical device, too. |
1616 | ClosePhysicalAudioDevice(device); |
1617 | } |
1618 | UnrefPhysicalAudioDevice(device); // one reference for each logical device. |
1619 | } |
1620 | |
1621 | ReleaseAudioDevice(device); |
1622 | } |
1623 | |
1624 | |
1625 | static SDL_AudioFormat ParseAudioFormatString(const char *string) |
1626 | { |
1627 | if (string) { |
1628 | #define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) { return SDL_AUDIO_##x; } |
1629 | CHECK_FMT_STRING(U8); |
1630 | CHECK_FMT_STRING(S8); |
1631 | CHECK_FMT_STRING(S16LE); |
1632 | CHECK_FMT_STRING(S16BE); |
1633 | CHECK_FMT_STRING(S16); |
1634 | CHECK_FMT_STRING(S32LE); |
1635 | CHECK_FMT_STRING(S32BE); |
1636 | CHECK_FMT_STRING(S32); |
1637 | CHECK_FMT_STRING(F32LE); |
1638 | CHECK_FMT_STRING(F32BE); |
1639 | CHECK_FMT_STRING(F32); |
1640 | #undef CHECK_FMT_STRING |
1641 | } |
1642 | return SDL_AUDIO_UNKNOWN; |
1643 | } |
1644 | |
1645 | static void PrepareAudioFormat(bool recording, SDL_AudioSpec *spec) |
1646 | { |
1647 | if (spec->freq == 0) { |
1648 | spec->freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; |
1649 | |
1650 | const char *hint = SDL_GetHint(SDL_HINT_AUDIO_FREQUENCY); |
1651 | if (hint) { |
1652 | const int val = SDL_atoi(hint); |
1653 | if (val > 0) { |
1654 | spec->freq = val; |
1655 | } |
1656 | } |
1657 | } |
1658 | |
1659 | if (spec->channels == 0) { |
1660 | spec->channels = recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; |
1661 | |
1662 | const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CHANNELS); |
1663 | if (hint) { |
1664 | const int val = SDL_atoi(hint); |
1665 | if (val > 0) { |
1666 | spec->channels = val; |
1667 | } |
1668 | } |
1669 | } |
1670 | |
1671 | if (spec->format == 0) { |
1672 | const SDL_AudioFormat val = ParseAudioFormatString(SDL_GetHint(SDL_HINT_AUDIO_FORMAT)); |
1673 | spec->format = (val != SDL_AUDIO_UNKNOWN) ? val : (recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT); |
1674 | } |
1675 | } |
1676 | |
1677 | void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device) |
1678 | { |
1679 | device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); |
1680 | device->buffer_size = device->sample_frames * SDL_AUDIO_FRAMESIZE(device->spec); |
1681 | device->work_buffer_size = device->sample_frames * sizeof (float) * device->spec.channels; |
1682 | device->work_buffer_size = SDL_max(device->buffer_size, device->work_buffer_size); // just in case we end up with a 64-bit audio format at some point. |
1683 | } |
1684 | |
1685 | char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen) |
1686 | { |
1687 | (void)SDL_snprintf(buf, buflen, "SDLAudio%c%d" , (device->recording) ? 'C' : 'P', (int) device->instance_id); |
1688 | return buf; |
1689 | } |
1690 | |
1691 | |
1692 | // this expects the device lock to be held. |
1693 | static bool OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec *inspec) |
1694 | { |
1695 | SerializePhysicalDeviceClose(device); // make sure another thread that's closing didn't release the lock to let the device thread join... |
1696 | |
1697 | if (device->currently_opened) { |
1698 | return true; // we're already good. |
1699 | } |
1700 | |
1701 | // Just pretend to open a zombie device. It can still collect logical devices on a default device under the assumption they will all migrate when the default device is officially changed. |
1702 | if (SDL_GetAtomicInt(&device->zombie)) { |
1703 | return true; // Braaaaaaaaains. |
1704 | } |
1705 | |
1706 | // These start with the backend's implementation, but we might swap them out with zombie versions later. |
1707 | device->WaitDevice = current_audio.impl.WaitDevice; |
1708 | device->PlayDevice = current_audio.impl.PlayDevice; |
1709 | device->GetDeviceBuf = current_audio.impl.GetDeviceBuf; |
1710 | device->WaitRecordingDevice = current_audio.impl.WaitRecordingDevice; |
1711 | device->RecordDevice = current_audio.impl.RecordDevice; |
1712 | device->FlushRecording = current_audio.impl.FlushRecording; |
1713 | |
1714 | SDL_AudioSpec spec; |
1715 | SDL_copyp(&spec, inspec ? inspec : &device->default_spec); |
1716 | PrepareAudioFormat(device->recording, &spec); |
1717 | |
1718 | /* We allow the device format to change if it's better than the current settings (by various definitions of "better"). This prevents |
1719 | something low quality, like an old game using S8/8000Hz audio, from ruining a music thing playing at CD quality that tries to open later. |
1720 | (or some VoIP library that opens for mono output ruining your surround-sound game because it got there first). |
1721 | These are just requests! The backend may change any of these values during OpenDevice method! */ |
1722 | device->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format; |
1723 | device->spec.freq = SDL_max(device->default_spec.freq, spec.freq); |
1724 | device->spec.channels = SDL_max(device->default_spec.channels, spec.channels); |
1725 | device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); |
1726 | SDL_UpdatedAudioDeviceFormat(device); // start this off sane. |
1727 | |
1728 | device->currently_opened = true; // mark this true even if impl.OpenDevice fails, so we know to clean up. |
1729 | if (!current_audio.impl.OpenDevice(device)) { |
1730 | ClosePhysicalAudioDevice(device); // clean up anything the backend left half-initialized. |
1731 | return false; |
1732 | } |
1733 | |
1734 | SDL_UpdatedAudioDeviceFormat(device); // in case the backend changed things and forgot to call this. |
1735 | |
1736 | // Allocate a scratch audio buffer |
1737 | device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
1738 | if (!device->work_buffer) { |
1739 | ClosePhysicalAudioDevice(device); |
1740 | return false; |
1741 | } |
1742 | |
1743 | if (device->spec.format != SDL_AUDIO_F32) { |
1744 | device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
1745 | if (!device->mix_buffer) { |
1746 | ClosePhysicalAudioDevice(device); |
1747 | return false; |
1748 | } |
1749 | } |
1750 | |
1751 | // Start the audio thread if necessary |
1752 | if (!current_audio.impl.ProvidesOwnCallbackThread) { |
1753 | char threadname[64]; |
1754 | SDL_GetAudioThreadName(device, threadname, sizeof (threadname)); |
1755 | device->thread = SDL_CreateThread(device->recording ? RecordingAudioThread : PlaybackAudioThread, threadname, device); |
1756 | |
1757 | if (!device->thread) { |
1758 | ClosePhysicalAudioDevice(device); |
1759 | return SDL_SetError("Couldn't create audio thread" ); |
1760 | } |
1761 | } |
1762 | |
1763 | return true; |
1764 | } |
1765 | |
1766 | SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec) |
1767 | { |
1768 | if (!SDL_GetCurrentAudioDriver()) { |
1769 | SDL_SetError("Audio subsystem is not initialized" ); |
1770 | return 0; |
1771 | } |
1772 | |
1773 | bool wants_default = ((devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) || (devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING)); |
1774 | |
1775 | // this will let you use a logical device to make a new logical device on the parent physical device. Could be useful? |
1776 | SDL_AudioDevice *device = NULL; |
1777 | const bool islogical = (!wants_default && !(devid & (1<<1))); |
1778 | if (!islogical) { |
1779 | device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); |
1780 | } else { |
1781 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1782 | if (logdev) { |
1783 | wants_default = logdev->opened_as_default; // was the original logical device meant to be a default? Make this one, too. |
1784 | } |
1785 | } |
1786 | |
1787 | SDL_AudioDeviceID result = 0; |
1788 | |
1789 | if (device) { |
1790 | SDL_LogicalAudioDevice *logdev = NULL; |
1791 | if (!wants_default && SDL_GetAtomicInt(&device->zombie)) { |
1792 | // uhoh, this device is undead, and just waiting to be cleaned up. Refuse explicit opens. |
1793 | SDL_SetError("Device was already lost and can't accept new opens" ); |
1794 | } else if ((logdev = (SDL_LogicalAudioDevice *) SDL_calloc(1, sizeof (SDL_LogicalAudioDevice))) == NULL) { |
1795 | // SDL_calloc already called SDL_OutOfMemory |
1796 | } else if (!OpenPhysicalAudioDevice(device, spec)) { // if this is the first thing using this physical device, open at the OS level if necessary... |
1797 | SDL_free(logdev); |
1798 | } else { |
1799 | RefPhysicalAudioDevice(device); // unref'd on successful SDL_CloseAudioDevice |
1800 | SDL_SetAtomicInt(&logdev->paused, 0); |
1801 | result = logdev->instance_id = AssignAudioDeviceInstanceId(device->recording, /*islogical=*/true); |
1802 | logdev->physical_device = device; |
1803 | logdev->gain = 1.0f; |
1804 | logdev->opened_as_default = wants_default; |
1805 | logdev->next = device->logical_devices; |
1806 | if (device->logical_devices) { |
1807 | device->logical_devices->prev = logdev; |
1808 | } |
1809 | device->logical_devices = logdev; |
1810 | UpdateAudioStreamFormatsPhysical(device); |
1811 | } |
1812 | ReleaseAudioDevice(device); |
1813 | |
1814 | if (result) { |
1815 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
1816 | const bool inserted = SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) result, logdev, false); |
1817 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
1818 | if (!inserted) { |
1819 | SDL_CloseAudioDevice(result); |
1820 | result = 0; |
1821 | } |
1822 | } |
1823 | } |
1824 | |
1825 | return result; |
1826 | } |
1827 | |
1828 | static bool SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value) |
1829 | { |
1830 | SDL_AudioDevice *device = NULL; |
1831 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1832 | if (logdev) { |
1833 | SDL_SetAtomicInt(&logdev->paused, value); |
1834 | } |
1835 | ReleaseAudioDevice(device); |
1836 | return logdev ? true : false; // ObtainLogicalAudioDevice will have set an error. |
1837 | } |
1838 | |
1839 | bool SDL_PauseAudioDevice(SDL_AudioDeviceID devid) |
1840 | { |
1841 | return SetLogicalAudioDevicePauseState(devid, 1); |
1842 | } |
1843 | |
1844 | bool SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid) |
1845 | { |
1846 | return SetLogicalAudioDevicePauseState(devid, 0); |
1847 | } |
1848 | |
1849 | bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid) |
1850 | { |
1851 | SDL_AudioDevice *device = NULL; |
1852 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1853 | bool result = false; |
1854 | if (logdev && SDL_GetAtomicInt(&logdev->paused)) { |
1855 | result = true; |
1856 | } |
1857 | ReleaseAudioDevice(device); |
1858 | return result; |
1859 | } |
1860 | |
1861 | float SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid) |
1862 | { |
1863 | SDL_AudioDevice *device = NULL; |
1864 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1865 | const float result = logdev ? logdev->gain : -1.0f; |
1866 | ReleaseAudioDevice(device); |
1867 | return result; |
1868 | } |
1869 | |
1870 | bool SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain) |
1871 | { |
1872 | if (gain < 0.0f) { |
1873 | return SDL_InvalidParamError("gain" ); |
1874 | } |
1875 | |
1876 | SDL_AudioDevice *device = NULL; |
1877 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1878 | bool result = false; |
1879 | if (logdev) { |
1880 | logdev->gain = gain; |
1881 | UpdateAudioStreamFormatsPhysical(device); |
1882 | result = true; |
1883 | } |
1884 | ReleaseAudioDevice(device); |
1885 | return result; |
1886 | } |
1887 | |
1888 | bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata) |
1889 | { |
1890 | SDL_AudioDevice *device = NULL; |
1891 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); |
1892 | bool result = true; |
1893 | if (logdev) { |
1894 | if (callback && !device->postmix_buffer) { |
1895 | device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
1896 | if (!device->postmix_buffer) { |
1897 | result = false; |
1898 | } |
1899 | } |
1900 | |
1901 | if (result) { |
1902 | logdev->postmix = callback; |
1903 | logdev->postmix_userdata = userdata; |
1904 | } |
1905 | |
1906 | UpdateAudioStreamFormatsPhysical(device); |
1907 | } |
1908 | ReleaseAudioDevice(device); |
1909 | return result; |
1910 | } |
1911 | |
1912 | bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams) |
1913 | { |
1914 | const bool islogical = !(devid & (1<<1)); |
1915 | SDL_AudioDevice *device = NULL; |
1916 | SDL_LogicalAudioDevice *logdev = NULL; |
1917 | bool result = true; |
1918 | |
1919 | if (num_streams == 0) { |
1920 | return true; // nothing to do |
1921 | } else if (num_streams < 0) { |
1922 | return SDL_InvalidParamError("num_streams" ); |
1923 | } else if (!streams) { |
1924 | return SDL_InvalidParamError("streams" ); |
1925 | } else if (!islogical) { |
1926 | return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices" ); |
1927 | } |
1928 | |
1929 | logdev = ObtainLogicalAudioDevice(devid, &device); |
1930 | if (!logdev) { |
1931 | result = false; // ObtainLogicalAudioDevice set the error string. |
1932 | } else if (logdev->simplified) { |
1933 | result = SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream" ); |
1934 | } else { |
1935 | |
1936 | // !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet. |
1937 | // !!! FIXME: Actually, why do we allow there to be an invalid format, again? |
1938 | |
1939 | // make sure start of list is sane. |
1940 | SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL)); |
1941 | |
1942 | // lock all the streams upfront, so we can verify they aren't bound elsewhere and add them all in one block, as this is intended to add everything or nothing. |
1943 | for (int i = 0; i < num_streams; i++) { |
1944 | SDL_AudioStream *stream = streams[i]; |
1945 | if (!stream) { |
1946 | SDL_SetError("Stream #%d is NULL" , i); |
1947 | result = false; // to pacify the static analyzer, that doesn't realize SDL_SetError() always returns false. |
1948 | } else { |
1949 | SDL_LockMutex(stream->lock); |
1950 | SDL_assert((stream->bound_device == NULL) == ((stream->prev_binding == NULL) || (stream->next_binding == NULL))); |
1951 | if (stream->bound_device) { |
1952 | result = SDL_SetError("Stream #%d is already bound to a device" , i); |
1953 | } else if (stream->simplified) { // You can get here if you closed the device instead of destroying the stream. |
1954 | result = SDL_SetError("Cannot change binding on a stream created with SDL_OpenAudioDeviceStream" ); |
1955 | } |
1956 | } |
1957 | |
1958 | if (!result) { |
1959 | int j; |
1960 | for (j = 0; j < i; j++) { |
1961 | SDL_UnlockMutex(streams[j]->lock); |
1962 | } |
1963 | if (stream) { |
1964 | SDL_UnlockMutex(stream->lock); |
1965 | } |
1966 | break; |
1967 | } |
1968 | } |
1969 | } |
1970 | |
1971 | if (result) { |
1972 | // Now that everything is verified, chain everything together. |
1973 | for (int i = 0; i < num_streams; i++) { |
1974 | SDL_AudioStream *stream = streams[i]; |
1975 | if (stream) { // shouldn't be NULL, but just in case... |
1976 | stream->bound_device = logdev; |
1977 | stream->prev_binding = NULL; |
1978 | stream->next_binding = logdev->bound_streams; |
1979 | if (logdev->bound_streams) { |
1980 | logdev->bound_streams->prev_binding = stream; |
1981 | } |
1982 | logdev->bound_streams = stream; |
1983 | SDL_UnlockMutex(stream->lock); |
1984 | } |
1985 | } |
1986 | } |
1987 | |
1988 | UpdateAudioStreamFormatsPhysical(device); |
1989 | |
1990 | ReleaseAudioDevice(device); |
1991 | |
1992 | return result; |
1993 | } |
1994 | |
1995 | bool SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream) |
1996 | { |
1997 | return SDL_BindAudioStreams(devid, &stream, 1); |
1998 | } |
1999 | |
2000 | // !!! FIXME: this and BindAudioStreams are mutex nightmares. :/ |
2001 | void SDL_UnbindAudioStreams(SDL_AudioStream * const *streams, int num_streams) |
2002 | { |
2003 | if (num_streams <= 0 || !streams) { |
2004 | return; // nothing to do |
2005 | } |
2006 | |
2007 | /* to prevent deadlock when holding both locks, we _must_ lock the device first, and the stream second, as that is the order the audio thread will do it. |
2008 | But this means we have an unlikely, pathological case where a stream could change its binding between when we lookup its bound device and when we lock everything, |
2009 | so we double-check here. */ |
2010 | for (int i = 0; i < num_streams; i++) { |
2011 | SDL_AudioStream *stream = streams[i]; |
2012 | if (!stream) { |
2013 | continue; // nothing to do, it's a NULL stream. |
2014 | } |
2015 | |
2016 | while (true) { |
2017 | SDL_LockMutex(stream->lock); // lock to check this and then release it, in case the device isn't locked yet. |
2018 | SDL_LogicalAudioDevice *bounddev = stream->bound_device; |
2019 | SDL_UnlockMutex(stream->lock); |
2020 | |
2021 | // lock in correct order. |
2022 | if (bounddev) { |
2023 | SDL_LockMutex(bounddev->physical_device->lock); // this requires recursive mutexes, since we're likely locking the same device multiple times. |
2024 | } |
2025 | SDL_LockMutex(stream->lock); |
2026 | |
2027 | if (bounddev == stream->bound_device) { |
2028 | break; // the binding didn't change in the small window where it could, so we're good. |
2029 | } else { |
2030 | SDL_UnlockMutex(stream->lock); // it changed bindings! Try again. |
2031 | if (bounddev) { |
2032 | SDL_UnlockMutex(bounddev->physical_device->lock); |
2033 | } |
2034 | } |
2035 | } |
2036 | } |
2037 | |
2038 | // everything is locked, start unbinding streams. |
2039 | for (int i = 0; i < num_streams; i++) { |
2040 | SDL_AudioStream *stream = streams[i]; |
2041 | // don't allow unbinding from "simplified" devices (opened with SDL_OpenAudioDeviceStream). Just ignore them. |
2042 | if (stream && stream->bound_device && !stream->bound_device->simplified) { |
2043 | if (stream->bound_device->bound_streams == stream) { |
2044 | SDL_assert(!stream->prev_binding); |
2045 | stream->bound_device->bound_streams = stream->next_binding; |
2046 | } |
2047 | if (stream->prev_binding) { |
2048 | stream->prev_binding->next_binding = stream->next_binding; |
2049 | } |
2050 | if (stream->next_binding) { |
2051 | stream->next_binding->prev_binding = stream->prev_binding; |
2052 | } |
2053 | stream->prev_binding = stream->next_binding = NULL; |
2054 | } |
2055 | } |
2056 | |
2057 | // Finalize and unlock everything. |
2058 | for (int i = 0; i < num_streams; i++) { |
2059 | SDL_AudioStream *stream = streams[i]; |
2060 | if (stream) { |
2061 | SDL_LogicalAudioDevice *logdev = stream->bound_device; |
2062 | stream->bound_device = NULL; |
2063 | SDL_UnlockMutex(stream->lock); |
2064 | if (logdev) { |
2065 | UpdateAudioStreamFormatsPhysical(logdev->physical_device); |
2066 | SDL_UnlockMutex(logdev->physical_device->lock); |
2067 | } |
2068 | } |
2069 | } |
2070 | } |
2071 | |
2072 | void SDL_UnbindAudioStream(SDL_AudioStream *stream) |
2073 | { |
2074 | SDL_UnbindAudioStreams(&stream, 1); |
2075 | } |
2076 | |
2077 | SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream) |
2078 | { |
2079 | SDL_AudioDeviceID result = 0; |
2080 | |
2081 | if (!stream) { |
2082 | SDL_InvalidParamError("stream" ); |
2083 | return 0; |
2084 | } |
2085 | |
2086 | SDL_LockMutex(stream->lock); |
2087 | if (stream->bound_device) { |
2088 | result = stream->bound_device->instance_id; |
2089 | } else { |
2090 | SDL_SetError("Audio stream not bound to an audio device" ); |
2091 | } |
2092 | SDL_UnlockMutex(stream->lock); |
2093 | |
2094 | return result; |
2095 | } |
2096 | |
2097 | SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata) |
2098 | { |
2099 | SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec); |
2100 | if (!logdevid) { |
2101 | return NULL; // error string should already be set. |
2102 | } |
2103 | |
2104 | bool failed = false; |
2105 | SDL_AudioStream *stream = NULL; |
2106 | SDL_AudioDevice *device = NULL; |
2107 | SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid, &device); |
2108 | if (!logdev) { // this shouldn't happen, but just in case. |
2109 | failed = true; |
2110 | } else { |
2111 | SDL_SetAtomicInt(&logdev->paused, 1); // start the device paused, to match SDL2. |
2112 | |
2113 | SDL_assert(device != NULL); |
2114 | const bool recording = device->recording; |
2115 | |
2116 | // if the app didn't request a format _at all_, just make a stream that does no conversion; they can query for it later. |
2117 | SDL_AudioSpec tmpspec; |
2118 | if (!spec) { |
2119 | SDL_copyp(&tmpspec, &device->spec); |
2120 | spec = &tmpspec; |
2121 | } |
2122 | |
2123 | if (recording) { |
2124 | stream = SDL_CreateAudioStream(&device->spec, spec); |
2125 | } else { |
2126 | stream = SDL_CreateAudioStream(spec, &device->spec); |
2127 | } |
2128 | |
2129 | if (!stream) { |
2130 | failed = true; |
2131 | } else { |
2132 | // don't do all the complicated validation and locking of SDL_BindAudioStream just to set a few fields here. |
2133 | logdev->bound_streams = stream; |
2134 | logdev->simplified = true; // forbid further binding changes on this logical device. |
2135 | |
2136 | stream->bound_device = logdev; |
2137 | stream->simplified = true; // so we know to close the audio device when this is destroyed. |
2138 | |
2139 | UpdateAudioStreamFormatsPhysical(device); |
2140 | |
2141 | if (callback) { |
2142 | bool rc; |
2143 | if (recording) { |
2144 | rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata); |
2145 | } else { |
2146 | rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata); |
2147 | } |
2148 | SDL_assert(rc); // should only fail if stream==NULL atm. |
2149 | } |
2150 | } |
2151 | } |
2152 | |
2153 | ReleaseAudioDevice(device); |
2154 | |
2155 | if (failed) { |
2156 | SDL_DestroyAudioStream(stream); |
2157 | SDL_CloseAudioDevice(logdevid); |
2158 | stream = NULL; |
2159 | } |
2160 | |
2161 | return stream; |
2162 | } |
2163 | |
2164 | bool SDL_PauseAudioStreamDevice(SDL_AudioStream *stream) |
2165 | { |
2166 | SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); |
2167 | if (!devid) { |
2168 | return false; |
2169 | } |
2170 | |
2171 | return SDL_PauseAudioDevice(devid); |
2172 | } |
2173 | |
2174 | bool SDL_ResumeAudioStreamDevice(SDL_AudioStream *stream) |
2175 | { |
2176 | SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); |
2177 | if (!devid) { |
2178 | return false; |
2179 | } |
2180 | |
2181 | return SDL_ResumeAudioDevice(devid); |
2182 | } |
2183 | |
2184 | bool SDL_AudioStreamDevicePaused(SDL_AudioStream *stream) |
2185 | { |
2186 | SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); |
2187 | if (!devid) { |
2188 | return false; |
2189 | } |
2190 | |
2191 | return SDL_AudioDevicePaused(devid); |
2192 | } |
2193 | |
2194 | #if SDL_BYTEORDER == SDL_LIL_ENDIAN |
2195 | #define NATIVE(type) SDL_AUDIO_##type##LE |
2196 | #define SWAPPED(type) SDL_AUDIO_##type##BE |
2197 | #else |
2198 | #define NATIVE(type) SDL_AUDIO_##type##BE |
2199 | #define SWAPPED(type) SDL_AUDIO_##type##LE |
2200 | #endif |
2201 | |
2202 | #define NUM_FORMATS 8 |
2203 | // always favor Float32 in native byte order, since we're probably going to convert to that for processing anyhow. |
2204 | static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = { |
2205 | { SDL_AUDIO_U8, NATIVE(F32), SWAPPED(F32), SDL_AUDIO_S8, NATIVE(S16), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_UNKNOWN }, |
2206 | { SDL_AUDIO_S8, NATIVE(F32), SWAPPED(F32), SDL_AUDIO_U8, NATIVE(S16), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_UNKNOWN }, |
2207 | { NATIVE(S16), NATIVE(F32), SWAPPED(F32), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2208 | { SWAPPED(S16), NATIVE(F32), SWAPPED(F32), NATIVE(S16), SWAPPED(S32), NATIVE(S32), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2209 | { NATIVE(S32), NATIVE(F32), SWAPPED(F32), SWAPPED(S32), NATIVE(S16), SWAPPED(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2210 | { SWAPPED(S32), NATIVE(F32), SWAPPED(F32), NATIVE(S32), SWAPPED(S16), NATIVE(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2211 | { NATIVE(F32), SWAPPED(F32), NATIVE(S32), SWAPPED(S32), NATIVE(S16), SWAPPED(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2212 | { SWAPPED(F32), NATIVE(F32), SWAPPED(S32), NATIVE(S32), SWAPPED(S16), NATIVE(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, |
2213 | }; |
2214 | |
2215 | #undef NATIVE |
2216 | #undef SWAPPED |
2217 | |
2218 | const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format) |
2219 | { |
2220 | for (int i = 0; i < NUM_FORMATS; i++) { |
2221 | if (format_list[i][0] == format) { |
2222 | return &format_list[i][0]; |
2223 | } |
2224 | } |
2225 | return &format_list[0][NUM_FORMATS]; // not found; return what looks like a list with only a zero in it. |
2226 | } |
2227 | |
2228 | const char *SDL_GetAudioFormatName(SDL_AudioFormat format) |
2229 | { |
2230 | switch (format) { |
2231 | #define CASE(X) \ |
2232 | case X: return #X; |
2233 | CASE(SDL_AUDIO_U8) |
2234 | CASE(SDL_AUDIO_S8) |
2235 | CASE(SDL_AUDIO_S16LE) |
2236 | CASE(SDL_AUDIO_S16BE) |
2237 | CASE(SDL_AUDIO_S32LE) |
2238 | CASE(SDL_AUDIO_S32BE) |
2239 | CASE(SDL_AUDIO_F32LE) |
2240 | CASE(SDL_AUDIO_F32BE) |
2241 | #undef CASE |
2242 | default: |
2243 | return "SDL_AUDIO_UNKNOWN" ; |
2244 | } |
2245 | } |
2246 | |
2247 | int SDL_GetSilenceValueForFormat(SDL_AudioFormat format) |
2248 | { |
2249 | return (format == SDL_AUDIO_U8) ? 0x80 : 0x00; |
2250 | } |
2251 | |
2252 | // called internally by backends when the system default device changes. |
2253 | void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) |
2254 | { |
2255 | if (!new_default_device) { // !!! FIXME: what should we do in this case? Maybe all devices are lost, so there _isn't_ a default? |
2256 | return; // uhoh. |
2257 | } |
2258 | |
2259 | const bool recording = new_default_device->recording; |
2260 | |
2261 | // change the official default over right away, so new opens will go to the new device. |
2262 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
2263 | const SDL_AudioDeviceID current_devid = recording ? current_audio.default_recording_device_id : current_audio.default_playback_device_id; |
2264 | const bool is_already_default = (new_default_device->instance_id == current_devid); |
2265 | if (!is_already_default) { |
2266 | if (recording) { |
2267 | current_audio.default_recording_device_id = new_default_device->instance_id; |
2268 | } else { |
2269 | current_audio.default_playback_device_id = new_default_device->instance_id; |
2270 | } |
2271 | } |
2272 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2273 | |
2274 | if (is_already_default) { |
2275 | return; // this is already the default. |
2276 | } |
2277 | |
2278 | // Queue up events to push to the queue next time it pumps (presumably |
2279 | // in a safer thread). |
2280 | // !!! FIXME: this duplicates some code we could probably refactor. |
2281 | SDL_PendingAudioDeviceEvent pending; |
2282 | pending.next = NULL; |
2283 | SDL_PendingAudioDeviceEvent *pending_tail = &pending; |
2284 | |
2285 | // Default device gets an extra ref, so it lives until a new default replaces it, even if disconnected. |
2286 | RefPhysicalAudioDevice(new_default_device); |
2287 | |
2288 | ObtainPhysicalAudioDeviceObj(new_default_device); |
2289 | |
2290 | SDL_AudioDevice *current_default_device = ObtainPhysicalAudioDevice(current_devid); |
2291 | |
2292 | if (current_default_device) { |
2293 | // migrate any logical devices that were opened as a default to the new physical device... |
2294 | |
2295 | SDL_assert(current_default_device->recording == recording); |
2296 | |
2297 | // See if we have to open the new physical device, and if so, find the best audiospec for it. |
2298 | SDL_AudioSpec spec; |
2299 | bool needs_migration = false; |
2300 | SDL_zero(spec); |
2301 | |
2302 | for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = logdev->next) { |
2303 | if (logdev->opened_as_default) { |
2304 | needs_migration = true; |
2305 | for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { |
2306 | const SDL_AudioSpec *streamspec = recording ? &stream->dst_spec : &stream->src_spec; |
2307 | if (SDL_AUDIO_BITSIZE(streamspec->format) > SDL_AUDIO_BITSIZE(spec.format)) { |
2308 | spec.format = streamspec->format; |
2309 | } |
2310 | if (streamspec->channels > spec.channels) { |
2311 | spec.channels = streamspec->channels; |
2312 | } |
2313 | if (streamspec->freq > spec.freq) { |
2314 | spec.freq = streamspec->freq; |
2315 | } |
2316 | } |
2317 | } |
2318 | } |
2319 | |
2320 | if (needs_migration) { |
2321 | // New default physical device not been opened yet? Open at the OS level... |
2322 | if (!OpenPhysicalAudioDevice(new_default_device, &spec)) { |
2323 | needs_migration = false; // uhoh, just leave everything on the old default, nothing to be done. |
2324 | } |
2325 | } |
2326 | |
2327 | if (needs_migration) { |
2328 | // we don't currently report channel map changes, so we'll leave them as NULL for now. |
2329 | const bool spec_changed = !SDL_AudioSpecsEqual(¤t_default_device->spec, &new_default_device->spec, NULL, NULL); |
2330 | SDL_LogicalAudioDevice *next = NULL; |
2331 | for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = next) { |
2332 | next = logdev->next; |
2333 | |
2334 | if (!logdev->opened_as_default) { |
2335 | continue; // not opened as a default, leave it on the current physical device. |
2336 | } |
2337 | |
2338 | // now migrate the logical device. Hold device_hash_lock so ObtainLogicalAudioDevice doesn't get a device in the middle of transition. |
2339 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
2340 | if (logdev->next) { |
2341 | logdev->next->prev = logdev->prev; |
2342 | } |
2343 | if (logdev->prev) { |
2344 | logdev->prev->next = logdev->next; |
2345 | } |
2346 | if (current_default_device->logical_devices == logdev) { |
2347 | current_default_device->logical_devices = logdev->next; |
2348 | } |
2349 | |
2350 | logdev->physical_device = new_default_device; |
2351 | logdev->prev = NULL; |
2352 | logdev->next = new_default_device->logical_devices; |
2353 | new_default_device->logical_devices = logdev; |
2354 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2355 | |
2356 | SDL_assert(SDL_GetAtomicInt(¤t_default_device->refcount) > 1); // we should hold at least one extra reference to this device, beyond logical devices, during this phase... |
2357 | RefPhysicalAudioDevice(new_default_device); |
2358 | UnrefPhysicalAudioDevice(current_default_device); |
2359 | |
2360 | SDL_SetAudioPostmixCallback(logdev->instance_id, logdev->postmix, logdev->postmix_userdata); |
2361 | |
2362 | SDL_PendingAudioDeviceEvent *p; |
2363 | |
2364 | // Queue an event for each logical device we moved. |
2365 | if (spec_changed) { |
2366 | p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); |
2367 | if (p) { // if this failed, no event for you, but you have deeper problems anyhow. |
2368 | p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; |
2369 | p->devid = logdev->instance_id; |
2370 | p->next = NULL; |
2371 | pending_tail->next = p; |
2372 | pending_tail = p; |
2373 | } |
2374 | } |
2375 | } |
2376 | |
2377 | UpdateAudioStreamFormatsPhysical(current_default_device); |
2378 | UpdateAudioStreamFormatsPhysical(new_default_device); |
2379 | |
2380 | if (!current_default_device->logical_devices) { // nothing left on the current physical device, close it. |
2381 | ClosePhysicalAudioDevice(current_default_device); |
2382 | } |
2383 | } |
2384 | |
2385 | ReleaseAudioDevice(current_default_device); |
2386 | } |
2387 | |
2388 | ReleaseAudioDevice(new_default_device); |
2389 | |
2390 | // Default device gets an extra ref, so it lives until a new default replaces it, even if disconnected. |
2391 | if (current_default_device) { // (despite the name, it's no longer current at this point) |
2392 | UnrefPhysicalAudioDevice(current_default_device); |
2393 | } |
2394 | |
2395 | if (pending.next) { |
2396 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
2397 | SDL_assert(current_audio.pending_events_tail != NULL); |
2398 | SDL_assert(current_audio.pending_events_tail->next == NULL); |
2399 | current_audio.pending_events_tail->next = pending.next; |
2400 | current_audio.pending_events_tail = pending_tail; |
2401 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2402 | } |
2403 | } |
2404 | |
2405 | bool SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames) |
2406 | { |
2407 | const int orig_work_buffer_size = device->work_buffer_size; |
2408 | |
2409 | // we don't currently have any place where channel maps change from under you, but we can check that if necessary later. |
2410 | if (SDL_AudioSpecsEqual(&device->spec, newspec, NULL, NULL) && (new_sample_frames == device->sample_frames)) { |
2411 | return true; // we're already in that format. |
2412 | } |
2413 | |
2414 | SDL_copyp(&device->spec, newspec); |
2415 | UpdateAudioStreamFormatsPhysical(device); |
2416 | |
2417 | bool kill_device = false; |
2418 | |
2419 | device->sample_frames = new_sample_frames; |
2420 | SDL_UpdatedAudioDeviceFormat(device); |
2421 | if (device->work_buffer && (device->work_buffer_size > orig_work_buffer_size)) { |
2422 | SDL_aligned_free(device->work_buffer); |
2423 | device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
2424 | if (!device->work_buffer) { |
2425 | kill_device = true; |
2426 | } |
2427 | |
2428 | if (device->postmix_buffer) { |
2429 | SDL_aligned_free(device->postmix_buffer); |
2430 | device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
2431 | if (!device->postmix_buffer) { |
2432 | kill_device = true; |
2433 | } |
2434 | } |
2435 | |
2436 | SDL_aligned_free(device->mix_buffer); |
2437 | device->mix_buffer = NULL; |
2438 | if (device->spec.format != SDL_AUDIO_F32) { |
2439 | device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); |
2440 | if (!device->mix_buffer) { |
2441 | kill_device = true; |
2442 | } |
2443 | } |
2444 | } |
2445 | |
2446 | // Post an event for the physical device, and each logical device on this physical device. |
2447 | if (!kill_device) { |
2448 | // Queue up events to push to the queue next time it pumps (presumably |
2449 | // in a safer thread). |
2450 | // !!! FIXME: this duplicates some code we could probably refactor. |
2451 | SDL_PendingAudioDeviceEvent pending; |
2452 | pending.next = NULL; |
2453 | SDL_PendingAudioDeviceEvent *pending_tail = &pending; |
2454 | |
2455 | SDL_PendingAudioDeviceEvent *p; |
2456 | |
2457 | p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); |
2458 | if (p) { // if this failed, no event for you, but you have deeper problems anyhow. |
2459 | p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; |
2460 | p->devid = device->instance_id; |
2461 | p->next = NULL; |
2462 | pending_tail->next = p; |
2463 | pending_tail = p; |
2464 | } |
2465 | |
2466 | for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { |
2467 | p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); |
2468 | if (p) { // if this failed, no event for you, but you have deeper problems anyhow. |
2469 | p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; |
2470 | p->devid = logdev->instance_id; |
2471 | p->next = NULL; |
2472 | pending_tail->next = p; |
2473 | pending_tail = p; |
2474 | } |
2475 | } |
2476 | |
2477 | if (pending.next) { |
2478 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
2479 | SDL_assert(current_audio.pending_events_tail != NULL); |
2480 | SDL_assert(current_audio.pending_events_tail->next == NULL); |
2481 | current_audio.pending_events_tail->next = pending.next; |
2482 | current_audio.pending_events_tail = pending_tail; |
2483 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2484 | } |
2485 | } |
2486 | |
2487 | if (kill_device) { |
2488 | return false; |
2489 | } |
2490 | return true; |
2491 | } |
2492 | |
2493 | bool SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames) |
2494 | { |
2495 | ObtainPhysicalAudioDeviceObj(device); |
2496 | const bool result = SDL_AudioDeviceFormatChangedAlreadyLocked(device, newspec, new_sample_frames); |
2497 | ReleaseAudioDevice(device); |
2498 | return result; |
2499 | } |
2500 | |
2501 | // This is an internal function, so SDL_PumpEvents() can check for pending audio device events. |
2502 | // ("UpdateSubsystem" is the same naming that the other things that hook into PumpEvents use.) |
2503 | void SDL_UpdateAudio(void) |
2504 | { |
2505 | SDL_LockRWLockForReading(current_audio.device_hash_lock); |
2506 | SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; |
2507 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2508 | |
2509 | if (!pending_events) { |
2510 | return; // nothing to do, check next time. |
2511 | } |
2512 | |
2513 | // okay, let's take this whole list of events so we can dump the lock, and new ones can queue up for a later update. |
2514 | SDL_LockRWLockForWriting(current_audio.device_hash_lock); |
2515 | pending_events = current_audio.pending_events.next; // in case this changed... |
2516 | current_audio.pending_events.next = NULL; |
2517 | current_audio.pending_events_tail = ¤t_audio.pending_events; |
2518 | SDL_UnlockRWLock(current_audio.device_hash_lock); |
2519 | |
2520 | SDL_PendingAudioDeviceEvent *pending_next = NULL; |
2521 | for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { |
2522 | pending_next = i->next; |
2523 | if (SDL_EventEnabled(i->type)) { |
2524 | SDL_Event event; |
2525 | SDL_zero(event); |
2526 | event.type = i->type; |
2527 | event.adevice.which = (Uint32) i->devid; |
2528 | event.adevice.recording = ((i->devid & (1<<0)) == 0); // bit #0 of devid is set for playback devices and unset for recording. |
2529 | SDL_PushEvent(&event); |
2530 | } |
2531 | SDL_free(i); |
2532 | } |
2533 | } |
2534 | |
2535 | |