1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 | /* Allow access to a raw mixing buffer */ |
24 | |
25 | #include "SDL.h" |
26 | #include "SDL_audio.h" |
27 | #include "SDL_audio_c.h" |
28 | #include "SDL_sysaudio.h" |
29 | #include "../thread/SDL_systhread.h" |
30 | |
31 | #define _THIS SDL_AudioDevice *_this |
32 | |
33 | static SDL_AudioDriver current_audio; |
34 | static SDL_AudioDevice *open_devices[16]; |
35 | |
36 | /* Available audio drivers */ |
37 | static const AudioBootStrap *const bootstrap[] = { |
38 | #if SDL_AUDIO_DRIVER_PULSEAUDIO |
39 | &PULSEAUDIO_bootstrap, |
40 | #endif |
41 | #if SDL_AUDIO_DRIVER_ALSA |
42 | &ALSA_bootstrap, |
43 | #endif |
44 | #if SDL_AUDIO_DRIVER_SNDIO |
45 | &SNDIO_bootstrap, |
46 | #endif |
47 | #if SDL_AUDIO_DRIVER_NETBSD |
48 | &NETBSDAUDIO_bootstrap, |
49 | #endif |
50 | #if SDL_AUDIO_DRIVER_OSS |
51 | &DSP_bootstrap, |
52 | #endif |
53 | #if SDL_AUDIO_DRIVER_QSA |
54 | &QSAAUDIO_bootstrap, |
55 | #endif |
56 | #if SDL_AUDIO_DRIVER_SUNAUDIO |
57 | &SUNAUDIO_bootstrap, |
58 | #endif |
59 | #if SDL_AUDIO_DRIVER_ARTS |
60 | &ARTS_bootstrap, |
61 | #endif |
62 | #if SDL_AUDIO_DRIVER_ESD |
63 | &ESD_bootstrap, |
64 | #endif |
65 | #if SDL_AUDIO_DRIVER_NACL |
66 | &NACLAUDIO_bootstrap, |
67 | #endif |
68 | #if SDL_AUDIO_DRIVER_NAS |
69 | &NAS_bootstrap, |
70 | #endif |
71 | #if SDL_AUDIO_DRIVER_WASAPI |
72 | &WASAPI_bootstrap, |
73 | #endif |
74 | #if SDL_AUDIO_DRIVER_DSOUND |
75 | &DSOUND_bootstrap, |
76 | #endif |
77 | #if SDL_AUDIO_DRIVER_WINMM |
78 | &WINMM_bootstrap, |
79 | #endif |
80 | #if SDL_AUDIO_DRIVER_PAUDIO |
81 | &PAUDIO_bootstrap, |
82 | #endif |
83 | #if SDL_AUDIO_DRIVER_HAIKU |
84 | &HAIKUAUDIO_bootstrap, |
85 | #endif |
86 | #if SDL_AUDIO_DRIVER_COREAUDIO |
87 | &COREAUDIO_bootstrap, |
88 | #endif |
89 | #if SDL_AUDIO_DRIVER_FUSIONSOUND |
90 | &FUSIONSOUND_bootstrap, |
91 | #endif |
92 | #if SDL_AUDIO_DRIVER_AAUDIO |
93 | &aaudio_bootstrap, |
94 | #endif |
95 | #if SDL_AUDIO_DRIVER_OPENSLES |
96 | &openslES_bootstrap, |
97 | #endif |
98 | #if SDL_AUDIO_DRIVER_ANDROID |
99 | &ANDROIDAUDIO_bootstrap, |
100 | #endif |
101 | #if SDL_AUDIO_DRIVER_PSP |
102 | &PSPAUDIO_bootstrap, |
103 | #endif |
104 | #if SDL_AUDIO_DRIVER_VITA |
105 | &VITAAUD_bootstrap, |
106 | #endif |
107 | #if SDL_AUDIO_DRIVER_EMSCRIPTEN |
108 | &EMSCRIPTENAUDIO_bootstrap, |
109 | #endif |
110 | #if SDL_AUDIO_DRIVER_JACK |
111 | &JACK_bootstrap, |
112 | #endif |
113 | #if SDL_AUDIO_DRIVER_PIPEWIRE |
114 | &PIPEWIRE_bootstrap, |
115 | #endif |
116 | #if SDL_AUDIO_DRIVER_OS2 |
117 | &OS2AUDIO_bootstrap, |
118 | #endif |
119 | #if SDL_AUDIO_DRIVER_DISK |
120 | &DISKAUDIO_bootstrap, |
121 | #endif |
122 | #if SDL_AUDIO_DRIVER_DUMMY |
123 | &DUMMYAUDIO_bootstrap, |
124 | #endif |
125 | NULL |
126 | }; |
127 | |
128 | |
129 | #ifdef HAVE_LIBSAMPLERATE_H |
130 | #ifdef SDL_LIBSAMPLERATE_DYNAMIC |
131 | static void *SRC_lib = NULL; |
132 | #endif |
133 | SDL_bool SRC_available = SDL_FALSE; |
134 | int SRC_converter = 0; |
135 | SRC_STATE* (*SRC_src_new)(int converter_type, int channels, int *error) = NULL; |
136 | int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data) = NULL; |
137 | int (*SRC_src_reset)(SRC_STATE *state) = NULL; |
138 | SRC_STATE* (*SRC_src_delete)(SRC_STATE *state) = NULL; |
139 | const char* (*SRC_src_strerror)(int error) = NULL; |
140 | |
141 | static SDL_bool |
142 | LoadLibSampleRate(void) |
143 | { |
144 | const char *hint = SDL_GetHint(SDL_HINT_AUDIO_RESAMPLING_MODE); |
145 | |
146 | SRC_available = SDL_FALSE; |
147 | SRC_converter = 0; |
148 | |
149 | if (!hint || *hint == '0' || SDL_strcasecmp(hint, "default" ) == 0) { |
150 | return SDL_FALSE; /* don't load anything. */ |
151 | } else if (*hint == '1' || SDL_strcasecmp(hint, "fast" ) == 0) { |
152 | SRC_converter = SRC_SINC_FASTEST; |
153 | } else if (*hint == '2' || SDL_strcasecmp(hint, "medium" ) == 0) { |
154 | SRC_converter = SRC_SINC_MEDIUM_QUALITY; |
155 | } else if (*hint == '3' || SDL_strcasecmp(hint, "best" ) == 0) { |
156 | SRC_converter = SRC_SINC_BEST_QUALITY; |
157 | } else { |
158 | return SDL_FALSE; /* treat it like "default", don't load anything. */ |
159 | } |
160 | |
161 | #ifdef SDL_LIBSAMPLERATE_DYNAMIC |
162 | SDL_assert(SRC_lib == NULL); |
163 | SRC_lib = SDL_LoadObject(SDL_LIBSAMPLERATE_DYNAMIC); |
164 | if (!SRC_lib) { |
165 | SDL_ClearError(); |
166 | return SDL_FALSE; |
167 | } |
168 | |
169 | SRC_src_new = (SRC_STATE* (*)(int converter_type, int channels, int *error))SDL_LoadFunction(SRC_lib, "src_new" ); |
170 | SRC_src_process = (int (*)(SRC_STATE *state, SRC_DATA *data))SDL_LoadFunction(SRC_lib, "src_process" ); |
171 | SRC_src_reset = (int(*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_reset" ); |
172 | SRC_src_delete = (SRC_STATE* (*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_delete" ); |
173 | SRC_src_strerror = (const char* (*)(int error))SDL_LoadFunction(SRC_lib, "src_strerror" ); |
174 | |
175 | if (!SRC_src_new || !SRC_src_process || !SRC_src_reset || !SRC_src_delete || !SRC_src_strerror) { |
176 | SDL_UnloadObject(SRC_lib); |
177 | SRC_lib = NULL; |
178 | return SDL_FALSE; |
179 | } |
180 | #else |
181 | SRC_src_new = src_new; |
182 | SRC_src_process = src_process; |
183 | SRC_src_reset = src_reset; |
184 | SRC_src_delete = src_delete; |
185 | SRC_src_strerror = src_strerror; |
186 | #endif |
187 | |
188 | SRC_available = SDL_TRUE; |
189 | return SDL_TRUE; |
190 | } |
191 | |
192 | static void |
193 | UnloadLibSampleRate(void) |
194 | { |
195 | #ifdef SDL_LIBSAMPLERATE_DYNAMIC |
196 | if (SRC_lib != NULL) { |
197 | SDL_UnloadObject(SRC_lib); |
198 | } |
199 | SRC_lib = NULL; |
200 | #endif |
201 | |
202 | SRC_available = SDL_FALSE; |
203 | SRC_src_new = NULL; |
204 | SRC_src_process = NULL; |
205 | SRC_src_reset = NULL; |
206 | SRC_src_delete = NULL; |
207 | SRC_src_strerror = NULL; |
208 | } |
209 | #endif |
210 | |
211 | static SDL_AudioDevice * |
212 | get_audio_device(SDL_AudioDeviceID id) |
213 | { |
214 | id--; |
215 | if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL)) { |
216 | SDL_SetError("Invalid audio device ID" ); |
217 | return NULL; |
218 | } |
219 | |
220 | return open_devices[id]; |
221 | } |
222 | |
223 | |
224 | /* stubs for audio drivers that don't need a specific entry point... */ |
225 | static void |
226 | SDL_AudioDetectDevices_Default(void) |
227 | { |
228 | /* you have to write your own implementation if these assertions fail. */ |
229 | SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice); |
230 | SDL_assert(current_audio.impl.OnlyHasDefaultCaptureDevice || !current_audio.impl.HasCaptureSupport); |
231 | |
232 | SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *) ((size_t) 0x1)); |
233 | if (current_audio.impl.HasCaptureSupport) { |
234 | SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *) ((size_t) 0x2)); |
235 | } |
236 | } |
237 | |
238 | static void |
239 | SDL_AudioThreadInit_Default(_THIS) |
240 | { /* no-op. */ |
241 | } |
242 | |
243 | static void |
244 | SDL_AudioThreadDeinit_Default(_THIS) |
245 | { /* no-op. */ |
246 | } |
247 | |
248 | static void |
249 | SDL_AudioBeginLoopIteration_Default(_THIS) |
250 | { /* no-op. */ |
251 | } |
252 | |
253 | static void |
254 | SDL_AudioWaitDevice_Default(_THIS) |
255 | { /* no-op. */ |
256 | } |
257 | |
258 | static void |
259 | SDL_AudioPlayDevice_Default(_THIS) |
260 | { /* no-op. */ |
261 | } |
262 | |
263 | static Uint8 * |
264 | SDL_AudioGetDeviceBuf_Default(_THIS) |
265 | { |
266 | return NULL; |
267 | } |
268 | |
269 | static int |
270 | SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen) |
271 | { |
272 | return -1; /* just fail immediately. */ |
273 | } |
274 | |
275 | static void |
276 | SDL_AudioFlushCapture_Default(_THIS) |
277 | { /* no-op. */ |
278 | } |
279 | |
280 | static void |
281 | SDL_AudioPrepareToClose_Default(_THIS) |
282 | { /* no-op. */ |
283 | } |
284 | |
285 | static void |
286 | SDL_AudioCloseDevice_Default(_THIS) |
287 | { /* no-op. */ |
288 | } |
289 | |
290 | static void |
291 | SDL_AudioDeinitialize_Default(void) |
292 | { /* no-op. */ |
293 | } |
294 | |
295 | static void |
296 | SDL_AudioFreeDeviceHandle_Default(void *handle) |
297 | { /* no-op. */ |
298 | } |
299 | |
300 | |
301 | static int |
302 | SDL_AudioOpenDevice_Default(_THIS, void *handle, const char *devname, int iscapture) |
303 | { |
304 | return SDL_Unsupported(); |
305 | } |
306 | |
307 | static SDL_INLINE SDL_bool |
308 | is_in_audio_device_thread(SDL_AudioDevice * device) |
309 | { |
310 | /* The device thread locks the same mutex, but not through the public API. |
311 | This check is in case the application, in the audio callback, |
312 | tries to lock the thread that we've already locked from the |
313 | device thread...just in case we only have non-recursive mutexes. */ |
314 | if (device->thread && (SDL_ThreadID() == device->threadid)) { |
315 | return SDL_TRUE; |
316 | } |
317 | |
318 | return SDL_FALSE; |
319 | } |
320 | |
321 | static void |
322 | SDL_AudioLockDevice_Default(SDL_AudioDevice * device) |
323 | { |
324 | if (!is_in_audio_device_thread(device)) { |
325 | SDL_LockMutex(device->mixer_lock); |
326 | } |
327 | } |
328 | |
329 | static void |
330 | SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device) |
331 | { |
332 | if (!is_in_audio_device_thread(device)) { |
333 | SDL_UnlockMutex(device->mixer_lock); |
334 | } |
335 | } |
336 | |
337 | static void |
338 | SDL_AudioLockOrUnlockDeviceWithNoMixerLock(SDL_AudioDevice * device) |
339 | { |
340 | } |
341 | |
342 | static void |
343 | finish_audio_entry_points_init(void) |
344 | { |
345 | /* |
346 | * Fill in stub functions for unused driver entry points. This lets us |
347 | * blindly call them without having to check for validity first. |
348 | */ |
349 | |
350 | if (current_audio.impl.SkipMixerLock) { |
351 | if (current_audio.impl.LockDevice == NULL) { |
352 | current_audio.impl.LockDevice = SDL_AudioLockOrUnlockDeviceWithNoMixerLock; |
353 | } |
354 | if (current_audio.impl.UnlockDevice == NULL) { |
355 | current_audio.impl.UnlockDevice = SDL_AudioLockOrUnlockDeviceWithNoMixerLock; |
356 | } |
357 | } |
358 | |
359 | #define FILL_STUB(x) \ |
360 | if (current_audio.impl.x == NULL) { \ |
361 | current_audio.impl.x = SDL_Audio##x##_Default; \ |
362 | } |
363 | FILL_STUB(DetectDevices); |
364 | FILL_STUB(OpenDevice); |
365 | FILL_STUB(ThreadInit); |
366 | FILL_STUB(ThreadDeinit); |
367 | FILL_STUB(BeginLoopIteration); |
368 | FILL_STUB(WaitDevice); |
369 | FILL_STUB(PlayDevice); |
370 | FILL_STUB(GetDeviceBuf); |
371 | FILL_STUB(CaptureFromDevice); |
372 | FILL_STUB(FlushCapture); |
373 | FILL_STUB(PrepareToClose); |
374 | FILL_STUB(CloseDevice); |
375 | FILL_STUB(LockDevice); |
376 | FILL_STUB(UnlockDevice); |
377 | FILL_STUB(FreeDeviceHandle); |
378 | FILL_STUB(Deinitialize); |
379 | #undef FILL_STUB |
380 | } |
381 | |
382 | |
383 | /* device hotplug support... */ |
384 | |
385 | static int |
386 | add_audio_device(const char *name, SDL_AudioSpec *spec, void *handle, SDL_AudioDeviceItem **devices, int *devCount) |
387 | { |
388 | int retval = -1; |
389 | SDL_AudioDeviceItem *item; |
390 | const SDL_AudioDeviceItem *i; |
391 | int dupenum = 0; |
392 | |
393 | SDL_assert(handle != NULL); /* we reserve NULL, audio backends can't use it. */ |
394 | SDL_assert(name != NULL); |
395 | |
396 | item = (SDL_AudioDeviceItem *) SDL_malloc(sizeof (SDL_AudioDeviceItem)); |
397 | if (!item) { |
398 | return SDL_OutOfMemory(); |
399 | } |
400 | |
401 | item->original_name = SDL_strdup(name); |
402 | if (!item->original_name) { |
403 | SDL_free(item); |
404 | return SDL_OutOfMemory(); |
405 | } |
406 | |
407 | item->dupenum = 0; |
408 | item->name = item->original_name; |
409 | if (spec != NULL) { |
410 | SDL_memcpy(&item->spec, spec, sizeof(SDL_AudioSpec)); |
411 | } else { |
412 | SDL_zero(item->spec); |
413 | } |
414 | item->handle = handle; |
415 | |
416 | SDL_LockMutex(current_audio.detectionLock); |
417 | |
418 | for (i = *devices; i != NULL; i = i->next) { |
419 | if (SDL_strcmp(name, i->original_name) == 0) { |
420 | dupenum = i->dupenum + 1; |
421 | break; /* stop at the highest-numbered dupe. */ |
422 | } |
423 | } |
424 | |
425 | if (dupenum) { |
426 | const size_t len = SDL_strlen(name) + 16; |
427 | char *replacement = (char *) SDL_malloc(len); |
428 | if (!replacement) { |
429 | SDL_UnlockMutex(current_audio.detectionLock); |
430 | SDL_free(item->original_name); |
431 | SDL_free(item); |
432 | SDL_OutOfMemory(); |
433 | return -1; |
434 | } |
435 | |
436 | SDL_snprintf(replacement, len, "%s (%d)" , name, dupenum + 1); |
437 | item->dupenum = dupenum; |
438 | item->name = replacement; |
439 | } |
440 | |
441 | item->next = *devices; |
442 | *devices = item; |
443 | retval = (*devCount)++; /* !!! FIXME: this should be an atomic increment */ |
444 | |
445 | SDL_UnlockMutex(current_audio.detectionLock); |
446 | |
447 | return retval; |
448 | } |
449 | |
450 | static SDL_INLINE int |
451 | add_capture_device(const char *name, SDL_AudioSpec *spec, void *handle) |
452 | { |
453 | SDL_assert(current_audio.impl.HasCaptureSupport); |
454 | return add_audio_device(name, spec, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount); |
455 | } |
456 | |
457 | static SDL_INLINE int |
458 | add_output_device(const char *name, SDL_AudioSpec *spec, void *handle) |
459 | { |
460 | return add_audio_device(name, spec, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount); |
461 | } |
462 | |
463 | static void |
464 | free_device_list(SDL_AudioDeviceItem **devices, int *devCount) |
465 | { |
466 | SDL_AudioDeviceItem *item, *next; |
467 | for (item = *devices; item != NULL; item = next) { |
468 | next = item->next; |
469 | if (item->handle != NULL) { |
470 | current_audio.impl.FreeDeviceHandle(item->handle); |
471 | } |
472 | /* these two pointers are the same if not a duplicate devname */ |
473 | if (item->name != item->original_name) { |
474 | SDL_free(item->name); |
475 | } |
476 | SDL_free(item->original_name); |
477 | SDL_free(item); |
478 | } |
479 | *devices = NULL; |
480 | *devCount = 0; |
481 | } |
482 | |
483 | |
484 | /* The audio backends call this when a new device is plugged in. */ |
485 | void |
486 | SDL_AddAudioDevice(const int iscapture, const char *name, SDL_AudioSpec *spec, void *handle) |
487 | { |
488 | const int device_index = iscapture ? add_capture_device(name, spec, handle) : add_output_device(name, spec, handle); |
489 | if (device_index != -1) { |
490 | /* Post the event, if desired */ |
491 | if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) { |
492 | SDL_Event event; |
493 | SDL_zero(event); |
494 | event.adevice.type = SDL_AUDIODEVICEADDED; |
495 | event.adevice.which = device_index; |
496 | event.adevice.iscapture = iscapture; |
497 | SDL_PushEvent(&event); |
498 | } |
499 | } |
500 | } |
501 | |
502 | /* The audio backends call this when a currently-opened device is lost. */ |
503 | void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device) |
504 | { |
505 | SDL_assert(get_audio_device(device->id) == device); |
506 | |
507 | if (!SDL_AtomicGet(&device->enabled)) { |
508 | return; /* don't report disconnects more than once. */ |
509 | } |
510 | |
511 | if (SDL_AtomicGet(&device->shutdown)) { |
512 | return; /* don't report disconnect if we're trying to close device. */ |
513 | } |
514 | |
515 | /* Ends the audio callback and mark the device as STOPPED, but the |
516 | app still needs to close the device to free resources. */ |
517 | current_audio.impl.LockDevice(device); |
518 | SDL_AtomicSet(&device->enabled, 0); |
519 | current_audio.impl.UnlockDevice(device); |
520 | |
521 | /* Post the event, if desired */ |
522 | if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) { |
523 | SDL_Event event; |
524 | SDL_zero(event); |
525 | event.adevice.type = SDL_AUDIODEVICEREMOVED; |
526 | event.adevice.which = device->id; |
527 | event.adevice.iscapture = device->iscapture ? 1 : 0; |
528 | SDL_PushEvent(&event); |
529 | } |
530 | } |
531 | |
532 | static void |
533 | mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag) |
534 | { |
535 | SDL_AudioDeviceItem *item; |
536 | SDL_assert(handle != NULL); |
537 | for (item = devices; item != NULL; item = item->next) { |
538 | if (item->handle == handle) { |
539 | item->handle = NULL; |
540 | *removedFlag = SDL_TRUE; |
541 | return; |
542 | } |
543 | } |
544 | } |
545 | |
546 | /* The audio backends call this when a device is removed from the system. */ |
547 | void |
548 | SDL_RemoveAudioDevice(const int iscapture, void *handle) |
549 | { |
550 | int device_index; |
551 | SDL_AudioDevice *device = NULL; |
552 | |
553 | SDL_LockMutex(current_audio.detectionLock); |
554 | if (iscapture) { |
555 | mark_device_removed(handle, current_audio.inputDevices, ¤t_audio.captureDevicesRemoved); |
556 | } else { |
557 | mark_device_removed(handle, current_audio.outputDevices, ¤t_audio.outputDevicesRemoved); |
558 | } |
559 | for (device_index = 0; device_index < SDL_arraysize(open_devices); device_index++) |
560 | { |
561 | device = open_devices[device_index]; |
562 | if (device != NULL && device->handle == handle) |
563 | { |
564 | SDL_OpenedAudioDeviceDisconnected(device); |
565 | break; |
566 | } |
567 | } |
568 | SDL_UnlockMutex(current_audio.detectionLock); |
569 | |
570 | current_audio.impl.FreeDeviceHandle(handle); |
571 | } |
572 | |
573 | |
574 | |
575 | /* buffer queueing support... */ |
576 | |
577 | static void SDLCALL |
578 | SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len) |
579 | { |
580 | /* this function always holds the mixer lock before being called. */ |
581 | SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; |
582 | size_t dequeued; |
583 | |
584 | SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ |
585 | SDL_assert(!device->iscapture); /* this shouldn't ever happen, right?! */ |
586 | SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ |
587 | |
588 | dequeued = SDL_ReadFromDataQueue(device->buffer_queue, stream, len); |
589 | stream += dequeued; |
590 | len -= (int) dequeued; |
591 | |
592 | if (len > 0) { /* fill any remaining space in the stream with silence. */ |
593 | SDL_assert(SDL_CountDataQueue(device->buffer_queue) == 0); |
594 | SDL_memset(stream, device->callbackspec.silence, len); |
595 | } |
596 | } |
597 | |
598 | static void SDLCALL |
599 | SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len) |
600 | { |
601 | /* this function always holds the mixer lock before being called. */ |
602 | SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; |
603 | |
604 | SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ |
605 | SDL_assert(device->iscapture); /* this shouldn't ever happen, right?! */ |
606 | SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ |
607 | |
608 | /* note that if this needs to allocate more space and run out of memory, |
609 | we have no choice but to quietly drop the data and hope it works out |
610 | later, but you probably have bigger problems in this case anyhow. */ |
611 | SDL_WriteToDataQueue(device->buffer_queue, stream, len); |
612 | } |
613 | |
614 | int |
615 | SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len) |
616 | { |
617 | SDL_AudioDevice *device = get_audio_device(devid); |
618 | int rc = 0; |
619 | |
620 | if (!device) { |
621 | return -1; /* get_audio_device() will have set the error state */ |
622 | } else if (device->iscapture) { |
623 | return SDL_SetError("This is a capture device, queueing not allowed" ); |
624 | } else if (device->callbackspec.callback != SDL_BufferQueueDrainCallback) { |
625 | return SDL_SetError("Audio device has a callback, queueing not allowed" ); |
626 | } |
627 | |
628 | if (len > 0) { |
629 | current_audio.impl.LockDevice(device); |
630 | rc = SDL_WriteToDataQueue(device->buffer_queue, data, len); |
631 | current_audio.impl.UnlockDevice(device); |
632 | } |
633 | |
634 | return rc; |
635 | } |
636 | |
637 | Uint32 |
638 | SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len) |
639 | { |
640 | SDL_AudioDevice *device = get_audio_device(devid); |
641 | Uint32 rc; |
642 | |
643 | if ( (len == 0) || /* nothing to do? */ |
644 | (!device) || /* called with bogus device id */ |
645 | (!device->iscapture) || /* playback devices can't dequeue */ |
646 | (device->callbackspec.callback != SDL_BufferQueueFillCallback) ) { /* not set for queueing */ |
647 | return 0; /* just report zero bytes dequeued. */ |
648 | } |
649 | |
650 | current_audio.impl.LockDevice(device); |
651 | rc = (Uint32) SDL_ReadFromDataQueue(device->buffer_queue, data, len); |
652 | current_audio.impl.UnlockDevice(device); |
653 | return rc; |
654 | } |
655 | |
656 | Uint32 |
657 | SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid) |
658 | { |
659 | Uint32 retval = 0; |
660 | SDL_AudioDevice *device = get_audio_device(devid); |
661 | |
662 | if (!device) { |
663 | return 0; |
664 | } |
665 | |
666 | /* Nothing to do unless we're set up for queueing. */ |
667 | if (device->callbackspec.callback == SDL_BufferQueueDrainCallback || |
668 | device->callbackspec.callback == SDL_BufferQueueFillCallback) |
669 | { |
670 | current_audio.impl.LockDevice(device); |
671 | retval = (Uint32) SDL_CountDataQueue(device->buffer_queue); |
672 | current_audio.impl.UnlockDevice(device); |
673 | } |
674 | |
675 | return retval; |
676 | } |
677 | |
678 | void |
679 | SDL_ClearQueuedAudio(SDL_AudioDeviceID devid) |
680 | { |
681 | SDL_AudioDevice *device = get_audio_device(devid); |
682 | |
683 | if (!device) { |
684 | return; /* nothing to do. */ |
685 | } |
686 | |
687 | /* Blank out the device and release the mutex. Free it afterwards. */ |
688 | current_audio.impl.LockDevice(device); |
689 | |
690 | /* Keep up to two packets in the pool to reduce future malloc pressure. */ |
691 | SDL_ClearDataQueue(device->buffer_queue, SDL_AUDIOBUFFERQUEUE_PACKETLEN * 2); |
692 | |
693 | current_audio.impl.UnlockDevice(device); |
694 | } |
695 | |
696 | |
697 | /* The general mixing thread function */ |
698 | static int SDLCALL |
699 | SDL_RunAudio(void *devicep) |
700 | { |
701 | SDL_AudioDevice *device = (SDL_AudioDevice *) devicep; |
702 | void *udata = device->callbackspec.userdata; |
703 | SDL_AudioCallback callback = device->callbackspec.callback; |
704 | int data_len = 0; |
705 | Uint8 *data; |
706 | |
707 | SDL_assert(!device->iscapture); |
708 | |
709 | #if SDL_AUDIO_DRIVER_ANDROID |
710 | { |
711 | /* Set thread priority to THREAD_PRIORITY_AUDIO */ |
712 | extern void Android_JNI_AudioSetThreadPriority(int, int); |
713 | Android_JNI_AudioSetThreadPriority(device->iscapture, device->id); |
714 | } |
715 | #else |
716 | /* The audio mixing is always a high priority thread */ |
717 | SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL); |
718 | #endif |
719 | |
720 | /* Perform any thread setup */ |
721 | device->threadid = SDL_ThreadID(); |
722 | current_audio.impl.ThreadInit(device); |
723 | |
724 | /* Loop, filling the audio buffers */ |
725 | while (!SDL_AtomicGet(&device->shutdown)) { |
726 | current_audio.impl.BeginLoopIteration(device); |
727 | data_len = device->callbackspec.size; |
728 | |
729 | /* Fill the current buffer with sound */ |
730 | if (!device->stream && SDL_AtomicGet(&device->enabled)) { |
731 | SDL_assert(data_len == device->spec.size); |
732 | data = current_audio.impl.GetDeviceBuf(device); |
733 | } else { |
734 | /* if the device isn't enabled, we still write to the |
735 | work_buffer, so the app's callback will fire with |
736 | a regular frequency, in case they depend on that |
737 | for timing or progress. They can use hotplug |
738 | now to know if the device failed. |
739 | Streaming playback uses work_buffer, too. */ |
740 | data = NULL; |
741 | } |
742 | |
743 | if (data == NULL) { |
744 | data = device->work_buffer; |
745 | } |
746 | |
747 | /* !!! FIXME: this should be LockDevice. */ |
748 | SDL_LockMutex(device->mixer_lock); |
749 | if (SDL_AtomicGet(&device->paused)) { |
750 | SDL_memset(data, device->callbackspec.silence, data_len); |
751 | } else { |
752 | callback(udata, data, data_len); |
753 | } |
754 | SDL_UnlockMutex(device->mixer_lock); |
755 | |
756 | if (device->stream) { |
757 | /* Stream available audio to device, converting/resampling. */ |
758 | /* if this fails...oh well. We'll play silence here. */ |
759 | SDL_AudioStreamPut(device->stream, data, data_len); |
760 | |
761 | while (SDL_AudioStreamAvailable(device->stream) >= ((int) device->spec.size)) { |
762 | int got; |
763 | data = SDL_AtomicGet(&device->enabled) ? current_audio.impl.GetDeviceBuf(device) : NULL; |
764 | got = SDL_AudioStreamGet(device->stream, data ? data : device->work_buffer, device->spec.size); |
765 | SDL_assert((got <= 0) || (got == device->spec.size)); |
766 | |
767 | if (data == NULL) { /* device is having issues... */ |
768 | const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); |
769 | SDL_Delay(delay); /* wait for as long as this buffer would have played. Maybe device recovers later? */ |
770 | } else { |
771 | if (got != device->spec.size) { |
772 | SDL_memset(data, device->spec.silence, device->spec.size); |
773 | } |
774 | current_audio.impl.PlayDevice(device); |
775 | current_audio.impl.WaitDevice(device); |
776 | } |
777 | } |
778 | } else if (data == device->work_buffer) { |
779 | /* nothing to do; pause like we queued a buffer to play. */ |
780 | const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); |
781 | SDL_Delay(delay); |
782 | } else { /* writing directly to the device. */ |
783 | /* queue this buffer and wait for it to finish playing. */ |
784 | current_audio.impl.PlayDevice(device); |
785 | current_audio.impl.WaitDevice(device); |
786 | } |
787 | } |
788 | |
789 | current_audio.impl.PrepareToClose(device); |
790 | |
791 | /* Wait for the audio to drain. */ |
792 | SDL_Delay(((device->spec.samples * 1000) / device->spec.freq) * 2); |
793 | |
794 | current_audio.impl.ThreadDeinit(device); |
795 | |
796 | return 0; |
797 | } |
798 | |
799 | /* !!! FIXME: this needs to deal with device spec changes. */ |
800 | /* The general capture thread function */ |
801 | static int SDLCALL |
802 | SDL_CaptureAudio(void *devicep) |
803 | { |
804 | SDL_AudioDevice *device = (SDL_AudioDevice *) devicep; |
805 | const int silence = (int) device->spec.silence; |
806 | const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); |
807 | const int data_len = device->spec.size; |
808 | Uint8 *data; |
809 | void *udata = device->callbackspec.userdata; |
810 | SDL_AudioCallback callback = device->callbackspec.callback; |
811 | |
812 | SDL_assert(device->iscapture); |
813 | |
814 | #if SDL_AUDIO_DRIVER_ANDROID |
815 | { |
816 | /* Set thread priority to THREAD_PRIORITY_AUDIO */ |
817 | extern void Android_JNI_AudioSetThreadPriority(int, int); |
818 | Android_JNI_AudioSetThreadPriority(device->iscapture, device->id); |
819 | } |
820 | #else |
821 | /* The audio mixing is always a high priority thread */ |
822 | SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); |
823 | #endif |
824 | |
825 | /* Perform any thread setup */ |
826 | device->threadid = SDL_ThreadID(); |
827 | current_audio.impl.ThreadInit(device); |
828 | |
829 | /* Loop, filling the audio buffers */ |
830 | while (!SDL_AtomicGet(&device->shutdown)) { |
831 | int still_need; |
832 | Uint8 *ptr; |
833 | |
834 | current_audio.impl.BeginLoopIteration(device); |
835 | |
836 | if (SDL_AtomicGet(&device->paused)) { |
837 | SDL_Delay(delay); /* just so we don't cook the CPU. */ |
838 | if (device->stream) { |
839 | SDL_AudioStreamClear(device->stream); |
840 | } |
841 | current_audio.impl.FlushCapture(device); /* dump anything pending. */ |
842 | continue; |
843 | } |
844 | |
845 | /* Fill the current buffer with sound */ |
846 | still_need = data_len; |
847 | |
848 | /* Use the work_buffer to hold data read from the device. */ |
849 | data = device->work_buffer; |
850 | SDL_assert(data != NULL); |
851 | |
852 | ptr = data; |
853 | |
854 | /* We still read from the device when "paused" to keep the state sane, |
855 | and block when there isn't data so this thread isn't eating CPU. |
856 | But we don't process it further or call the app's callback. */ |
857 | |
858 | if (!SDL_AtomicGet(&device->enabled)) { |
859 | SDL_Delay(delay); /* try to keep callback firing at normal pace. */ |
860 | } else { |
861 | while (still_need > 0) { |
862 | const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need); |
863 | SDL_assert(rc <= still_need); /* device should not overflow buffer. :) */ |
864 | if (rc > 0) { |
865 | still_need -= rc; |
866 | ptr += rc; |
867 | } else { /* uhoh, device failed for some reason! */ |
868 | SDL_OpenedAudioDeviceDisconnected(device); |
869 | break; |
870 | } |
871 | } |
872 | } |
873 | |
874 | if (still_need > 0) { |
875 | /* Keep any data we already read, silence the rest. */ |
876 | SDL_memset(ptr, silence, still_need); |
877 | } |
878 | |
879 | if (device->stream) { |
880 | /* if this fails...oh well. */ |
881 | SDL_AudioStreamPut(device->stream, data, data_len); |
882 | |
883 | while (SDL_AudioStreamAvailable(device->stream) >= ((int) device->callbackspec.size)) { |
884 | const int got = SDL_AudioStreamGet(device->stream, device->work_buffer, device->callbackspec.size); |
885 | SDL_assert((got < 0) || (got == device->callbackspec.size)); |
886 | if (got != device->callbackspec.size) { |
887 | SDL_memset(device->work_buffer, device->spec.silence, device->callbackspec.size); |
888 | } |
889 | |
890 | /* !!! FIXME: this should be LockDevice. */ |
891 | SDL_LockMutex(device->mixer_lock); |
892 | if (!SDL_AtomicGet(&device->paused)) { |
893 | callback(udata, device->work_buffer, device->callbackspec.size); |
894 | } |
895 | SDL_UnlockMutex(device->mixer_lock); |
896 | } |
897 | } else { /* feeding user callback directly without streaming. */ |
898 | /* !!! FIXME: this should be LockDevice. */ |
899 | SDL_LockMutex(device->mixer_lock); |
900 | if (!SDL_AtomicGet(&device->paused)) { |
901 | callback(udata, data, device->callbackspec.size); |
902 | } |
903 | SDL_UnlockMutex(device->mixer_lock); |
904 | } |
905 | } |
906 | |
907 | current_audio.impl.FlushCapture(device); |
908 | |
909 | current_audio.impl.ThreadDeinit(device); |
910 | |
911 | return 0; |
912 | } |
913 | |
914 | |
915 | static SDL_AudioFormat |
916 | SDL_ParseAudioFormat(const char *string) |
917 | { |
918 | #define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) return AUDIO_##x |
919 | CHECK_FMT_STRING(U8); |
920 | CHECK_FMT_STRING(S8); |
921 | CHECK_FMT_STRING(U16LSB); |
922 | CHECK_FMT_STRING(S16LSB); |
923 | CHECK_FMT_STRING(U16MSB); |
924 | CHECK_FMT_STRING(S16MSB); |
925 | CHECK_FMT_STRING(U16SYS); |
926 | CHECK_FMT_STRING(S16SYS); |
927 | CHECK_FMT_STRING(U16); |
928 | CHECK_FMT_STRING(S16); |
929 | CHECK_FMT_STRING(S32LSB); |
930 | CHECK_FMT_STRING(S32MSB); |
931 | CHECK_FMT_STRING(S32SYS); |
932 | CHECK_FMT_STRING(S32); |
933 | CHECK_FMT_STRING(F32LSB); |
934 | CHECK_FMT_STRING(F32MSB); |
935 | CHECK_FMT_STRING(F32SYS); |
936 | CHECK_FMT_STRING(F32); |
937 | #undef CHECK_FMT_STRING |
938 | return 0; |
939 | } |
940 | |
941 | int |
942 | SDL_GetNumAudioDrivers(void) |
943 | { |
944 | return SDL_arraysize(bootstrap) - 1; |
945 | } |
946 | |
947 | const char * |
948 | SDL_GetAudioDriver(int index) |
949 | { |
950 | if (index >= 0 && index < SDL_GetNumAudioDrivers()) { |
951 | return bootstrap[index]->name; |
952 | } |
953 | return NULL; |
954 | } |
955 | |
956 | int |
957 | SDL_AudioInit(const char *driver_name) |
958 | { |
959 | int i = 0; |
960 | int initialized = 0; |
961 | int tried_to_init = 0; |
962 | |
963 | if (SDL_WasInit(SDL_INIT_AUDIO)) { |
964 | SDL_AudioQuit(); /* shutdown driver if already running. */ |
965 | } |
966 | |
967 | SDL_zero(current_audio); |
968 | SDL_zeroa(open_devices); |
969 | |
970 | /* Select the proper audio driver */ |
971 | if (driver_name == NULL) { |
972 | driver_name = SDL_getenv("SDL_AUDIODRIVER" ); |
973 | } |
974 | |
975 | for (i = 0; (!initialized) && (bootstrap[i]); ++i) { |
976 | /* make sure we should even try this driver before doing so... */ |
977 | const AudioBootStrap *backend = bootstrap[i]; |
978 | if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) || |
979 | (!driver_name && backend->demand_only)) { |
980 | continue; |
981 | } |
982 | |
983 | tried_to_init = 1; |
984 | SDL_zero(current_audio); |
985 | current_audio.name = backend->name; |
986 | current_audio.desc = backend->desc; |
987 | initialized = backend->init(¤t_audio.impl); |
988 | } |
989 | |
990 | if (!initialized) { |
991 | /* specific drivers will set the error message if they fail... */ |
992 | if (!tried_to_init) { |
993 | if (driver_name) { |
994 | SDL_SetError("Audio target '%s' not available" , driver_name); |
995 | } else { |
996 | SDL_SetError("No available audio device" ); |
997 | } |
998 | } |
999 | |
1000 | SDL_zero(current_audio); |
1001 | return -1; /* No driver was available, so fail. */ |
1002 | } |
1003 | |
1004 | current_audio.detectionLock = SDL_CreateMutex(); |
1005 | |
1006 | finish_audio_entry_points_init(); |
1007 | |
1008 | /* Make sure we have a list of devices available at startup. */ |
1009 | current_audio.impl.DetectDevices(); |
1010 | |
1011 | #ifdef HAVE_LIBSAMPLERATE_H |
1012 | LoadLibSampleRate(); |
1013 | #endif |
1014 | |
1015 | return 0; |
1016 | } |
1017 | |
1018 | /* |
1019 | * Get the current audio driver name |
1020 | */ |
1021 | const char * |
1022 | SDL_GetCurrentAudioDriver() |
1023 | { |
1024 | return current_audio.name; |
1025 | } |
1026 | |
1027 | /* Clean out devices that we've removed but had to keep around for stability. */ |
1028 | static void |
1029 | clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag) |
1030 | { |
1031 | SDL_AudioDeviceItem *item = *devices; |
1032 | SDL_AudioDeviceItem *prev = NULL; |
1033 | int total = 0; |
1034 | |
1035 | while (item) { |
1036 | SDL_AudioDeviceItem *next = item->next; |
1037 | if (item->handle != NULL) { |
1038 | total++; |
1039 | prev = item; |
1040 | } else { |
1041 | if (prev) { |
1042 | prev->next = next; |
1043 | } else { |
1044 | *devices = next; |
1045 | } |
1046 | /* these two pointers are the same if not a duplicate devname */ |
1047 | if (item->name != item->original_name) { |
1048 | SDL_free(item->name); |
1049 | } |
1050 | SDL_free(item->original_name); |
1051 | SDL_free(item); |
1052 | } |
1053 | item = next; |
1054 | } |
1055 | |
1056 | *devCount = total; |
1057 | *removedFlag = SDL_FALSE; |
1058 | } |
1059 | |
1060 | |
1061 | int |
1062 | SDL_GetNumAudioDevices(int iscapture) |
1063 | { |
1064 | int retval = 0; |
1065 | |
1066 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
1067 | return -1; |
1068 | } |
1069 | |
1070 | SDL_LockMutex(current_audio.detectionLock); |
1071 | if (iscapture && current_audio.captureDevicesRemoved) { |
1072 | clean_out_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount, ¤t_audio.captureDevicesRemoved); |
1073 | } |
1074 | |
1075 | if (!iscapture && current_audio.outputDevicesRemoved) { |
1076 | clean_out_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount, ¤t_audio.outputDevicesRemoved); |
1077 | } |
1078 | |
1079 | retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; |
1080 | SDL_UnlockMutex(current_audio.detectionLock); |
1081 | |
1082 | return retval; |
1083 | } |
1084 | |
1085 | |
1086 | const char * |
1087 | SDL_GetAudioDeviceName(int index, int iscapture) |
1088 | { |
1089 | const char *retval = NULL; |
1090 | |
1091 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
1092 | SDL_SetError("Audio subsystem is not initialized" ); |
1093 | return NULL; |
1094 | } |
1095 | |
1096 | if (iscapture && !current_audio.impl.HasCaptureSupport) { |
1097 | SDL_SetError("No capture support" ); |
1098 | return NULL; |
1099 | } |
1100 | |
1101 | if (index >= 0) { |
1102 | SDL_AudioDeviceItem *item; |
1103 | int i; |
1104 | |
1105 | SDL_LockMutex(current_audio.detectionLock); |
1106 | item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; |
1107 | i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; |
1108 | if (index < i) { |
1109 | for (i--; i > index; i--, item = item->next) { |
1110 | SDL_assert(item != NULL); |
1111 | } |
1112 | SDL_assert(item != NULL); |
1113 | retval = item->name; |
1114 | } |
1115 | SDL_UnlockMutex(current_audio.detectionLock); |
1116 | } |
1117 | |
1118 | if (retval == NULL) { |
1119 | SDL_SetError("No such device" ); |
1120 | } |
1121 | |
1122 | return retval; |
1123 | } |
1124 | |
1125 | |
1126 | int |
1127 | SDL_GetAudioDeviceSpec(int index, int iscapture, SDL_AudioSpec *spec) |
1128 | { |
1129 | if (spec == NULL) { |
1130 | return SDL_InvalidParamError("spec" ); |
1131 | } |
1132 | |
1133 | SDL_zerop(spec); |
1134 | |
1135 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
1136 | return SDL_SetError("Audio subsystem is not initialized" ); |
1137 | } |
1138 | |
1139 | if (iscapture && !current_audio.impl.HasCaptureSupport) { |
1140 | return SDL_SetError("No capture support" ); |
1141 | } |
1142 | |
1143 | if (index >= 0) { |
1144 | SDL_AudioDeviceItem *item; |
1145 | int i; |
1146 | |
1147 | SDL_LockMutex(current_audio.detectionLock); |
1148 | item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; |
1149 | i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; |
1150 | if (index < i) { |
1151 | for (i--; i > index; i--, item = item->next) { |
1152 | SDL_assert(item != NULL); |
1153 | } |
1154 | SDL_assert(item != NULL); |
1155 | SDL_memcpy(spec, &item->spec, sizeof(SDL_AudioSpec)); |
1156 | } |
1157 | SDL_UnlockMutex(current_audio.detectionLock); |
1158 | } |
1159 | |
1160 | return 0; |
1161 | } |
1162 | |
1163 | |
1164 | static void |
1165 | close_audio_device(SDL_AudioDevice * device) |
1166 | { |
1167 | if (!device) { |
1168 | return; |
1169 | } |
1170 | |
1171 | /* make sure the device is paused before we do anything else, so the |
1172 | audio callback definitely won't fire again. */ |
1173 | current_audio.impl.LockDevice(device); |
1174 | SDL_AtomicSet(&device->paused, 1); |
1175 | SDL_AtomicSet(&device->shutdown, 1); |
1176 | SDL_AtomicSet(&device->enabled, 0); |
1177 | current_audio.impl.UnlockDevice(device); |
1178 | |
1179 | if (device->thread != NULL) { |
1180 | SDL_WaitThread(device->thread, NULL); |
1181 | } |
1182 | if (device->mixer_lock != NULL) { |
1183 | SDL_DestroyMutex(device->mixer_lock); |
1184 | } |
1185 | |
1186 | SDL_free(device->work_buffer); |
1187 | SDL_FreeAudioStream(device->stream); |
1188 | |
1189 | if (device->id > 0) { |
1190 | SDL_AudioDevice *opendev = open_devices[device->id - 1]; |
1191 | SDL_assert((opendev == device) || (opendev == NULL)); |
1192 | if (opendev == device) { |
1193 | open_devices[device->id - 1] = NULL; |
1194 | } |
1195 | } |
1196 | |
1197 | if (device->hidden != NULL) { |
1198 | current_audio.impl.CloseDevice(device); |
1199 | } |
1200 | |
1201 | SDL_FreeDataQueue(device->buffer_queue); |
1202 | |
1203 | SDL_free(device); |
1204 | } |
1205 | |
1206 | |
1207 | /* |
1208 | * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig). |
1209 | * Fills in a sanitized copy in (prepared). |
1210 | * Returns non-zero if okay, zero on fatal parameters in (orig). |
1211 | */ |
1212 | static int |
1213 | prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared) |
1214 | { |
1215 | SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec)); |
1216 | |
1217 | if (orig->freq == 0) { |
1218 | const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY" ); |
1219 | if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) { |
1220 | prepared->freq = 22050; /* a reasonable default */ |
1221 | } |
1222 | } |
1223 | |
1224 | if (orig->format == 0) { |
1225 | const char *env = SDL_getenv("SDL_AUDIO_FORMAT" ); |
1226 | if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) { |
1227 | prepared->format = AUDIO_S16; /* a reasonable default */ |
1228 | } |
1229 | } |
1230 | |
1231 | switch (orig->channels) { |
1232 | case 0:{ |
1233 | const char *env = SDL_getenv("SDL_AUDIO_CHANNELS" ); |
1234 | if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) { |
1235 | prepared->channels = 2; /* a reasonable default */ |
1236 | break; |
1237 | } |
1238 | } |
1239 | case 1: /* Mono */ |
1240 | case 2: /* Stereo */ |
1241 | case 4: /* Quadrophonic */ |
1242 | case 6: /* 5.1 surround */ |
1243 | case 8: /* 7.1 surround */ |
1244 | break; |
1245 | default: |
1246 | SDL_SetError("Unsupported number of audio channels." ); |
1247 | return 0; |
1248 | } |
1249 | |
1250 | if (orig->samples == 0) { |
1251 | const char *env = SDL_getenv("SDL_AUDIO_SAMPLES" ); |
1252 | if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) { |
1253 | /* Pick a default of ~46 ms at desired frequency */ |
1254 | /* !!! FIXME: remove this when the non-Po2 resampling is in. */ |
1255 | const int samples = (prepared->freq / 1000) * 46; |
1256 | int power2 = 1; |
1257 | while (power2 < samples) { |
1258 | power2 *= 2; |
1259 | } |
1260 | prepared->samples = power2; |
1261 | } |
1262 | } |
1263 | |
1264 | /* Calculate the silence and size of the audio specification */ |
1265 | SDL_CalculateAudioSpec(prepared); |
1266 | |
1267 | return 1; |
1268 | } |
1269 | |
1270 | static SDL_AudioDeviceID |
1271 | open_audio_device(const char *devname, int iscapture, |
1272 | const SDL_AudioSpec * desired, SDL_AudioSpec * obtained, |
1273 | int allowed_changes, int min_id) |
1274 | { |
1275 | const SDL_bool is_internal_thread = (desired->callback == NULL); |
1276 | SDL_AudioDeviceID id = 0; |
1277 | SDL_AudioSpec _obtained; |
1278 | SDL_AudioDevice *device; |
1279 | SDL_bool build_stream; |
1280 | void *handle = NULL; |
1281 | int i = 0; |
1282 | |
1283 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
1284 | SDL_SetError("Audio subsystem is not initialized" ); |
1285 | return 0; |
1286 | } |
1287 | |
1288 | if (iscapture && !current_audio.impl.HasCaptureSupport) { |
1289 | SDL_SetError("No capture support" ); |
1290 | return 0; |
1291 | } |
1292 | |
1293 | /* !!! FIXME: there is a race condition here if two devices open from two threads at once. */ |
1294 | /* Find an available device ID... */ |
1295 | for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) { |
1296 | if (open_devices[id] == NULL) { |
1297 | break; |
1298 | } |
1299 | } |
1300 | |
1301 | if (id == SDL_arraysize(open_devices)) { |
1302 | SDL_SetError("Too many open audio devices" ); |
1303 | return 0; |
1304 | } |
1305 | |
1306 | if (!obtained) { |
1307 | obtained = &_obtained; |
1308 | } |
1309 | if (!prepare_audiospec(desired, obtained)) { |
1310 | return 0; |
1311 | } |
1312 | |
1313 | /* If app doesn't care about a specific device, let the user override. */ |
1314 | if (devname == NULL) { |
1315 | devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME" ); |
1316 | } |
1317 | |
1318 | /* |
1319 | * Catch device names at the high level for the simple case... |
1320 | * This lets us have a basic "device enumeration" for systems that |
1321 | * don't have multiple devices, but makes sure the device name is |
1322 | * always NULL when it hits the low level. |
1323 | * |
1324 | * Also make sure that the simple case prevents multiple simultaneous |
1325 | * opens of the default system device. |
1326 | */ |
1327 | |
1328 | if ((iscapture) && (current_audio.impl.OnlyHasDefaultCaptureDevice)) { |
1329 | if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) { |
1330 | SDL_SetError("No such device" ); |
1331 | return 0; |
1332 | } |
1333 | devname = NULL; |
1334 | |
1335 | for (i = 0; i < SDL_arraysize(open_devices); i++) { |
1336 | if ((open_devices[i]) && (open_devices[i]->iscapture)) { |
1337 | SDL_SetError("Audio device already open" ); |
1338 | return 0; |
1339 | } |
1340 | } |
1341 | } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) { |
1342 | if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) { |
1343 | SDL_SetError("No such device" ); |
1344 | return 0; |
1345 | } |
1346 | devname = NULL; |
1347 | |
1348 | for (i = 0; i < SDL_arraysize(open_devices); i++) { |
1349 | if ((open_devices[i]) && (!open_devices[i]->iscapture)) { |
1350 | SDL_SetError("Audio device already open" ); |
1351 | return 0; |
1352 | } |
1353 | } |
1354 | } else if (devname != NULL) { |
1355 | /* if the app specifies an exact string, we can pass the backend |
1356 | an actual device handle thingey, which saves them the effort of |
1357 | figuring out what device this was (such as, reenumerating |
1358 | everything again to find the matching human-readable name). |
1359 | It might still need to open a device based on the string for, |
1360 | say, a network audio server, but this optimizes some cases. */ |
1361 | SDL_AudioDeviceItem *item; |
1362 | SDL_LockMutex(current_audio.detectionLock); |
1363 | for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) { |
1364 | if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) { |
1365 | handle = item->handle; |
1366 | break; |
1367 | } |
1368 | } |
1369 | SDL_UnlockMutex(current_audio.detectionLock); |
1370 | } |
1371 | |
1372 | if (!current_audio.impl.AllowsArbitraryDeviceNames) { |
1373 | /* has to be in our device list, or the default device. */ |
1374 | if ((handle == NULL) && (devname != NULL)) { |
1375 | SDL_SetError("No such device." ); |
1376 | return 0; |
1377 | } |
1378 | } |
1379 | |
1380 | device = (SDL_AudioDevice *) SDL_calloc(1, sizeof (SDL_AudioDevice)); |
1381 | if (device == NULL) { |
1382 | SDL_OutOfMemory(); |
1383 | return 0; |
1384 | } |
1385 | device->id = id + 1; |
1386 | device->spec = *obtained; |
1387 | device->iscapture = iscapture ? SDL_TRUE : SDL_FALSE; |
1388 | device->handle = handle; |
1389 | |
1390 | SDL_AtomicSet(&device->shutdown, 0); /* just in case. */ |
1391 | SDL_AtomicSet(&device->paused, 1); |
1392 | SDL_AtomicSet(&device->enabled, 1); |
1393 | |
1394 | /* Create a mutex for locking the sound buffers */ |
1395 | if (!current_audio.impl.SkipMixerLock) { |
1396 | device->mixer_lock = SDL_CreateMutex(); |
1397 | if (device->mixer_lock == NULL) { |
1398 | close_audio_device(device); |
1399 | SDL_SetError("Couldn't create mixer lock" ); |
1400 | return 0; |
1401 | } |
1402 | } |
1403 | |
1404 | if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) { |
1405 | close_audio_device(device); |
1406 | return 0; |
1407 | } |
1408 | |
1409 | /* if your target really doesn't need it, set it to 0x1 or something. */ |
1410 | /* otherwise, close_audio_device() won't call impl.CloseDevice(). */ |
1411 | SDL_assert(device->hidden != NULL); |
1412 | |
1413 | /* See if we need to do any conversion */ |
1414 | build_stream = SDL_FALSE; |
1415 | if (obtained->freq != device->spec.freq) { |
1416 | if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) { |
1417 | obtained->freq = device->spec.freq; |
1418 | } else { |
1419 | build_stream = SDL_TRUE; |
1420 | } |
1421 | } |
1422 | if (obtained->format != device->spec.format) { |
1423 | if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) { |
1424 | obtained->format = device->spec.format; |
1425 | } else { |
1426 | build_stream = SDL_TRUE; |
1427 | } |
1428 | } |
1429 | if (obtained->channels != device->spec.channels) { |
1430 | if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) { |
1431 | obtained->channels = device->spec.channels; |
1432 | } else { |
1433 | build_stream = SDL_TRUE; |
1434 | } |
1435 | } |
1436 | if (device->spec.samples != obtained->samples) { |
1437 | if (allowed_changes & SDL_AUDIO_ALLOW_SAMPLES_CHANGE) { |
1438 | obtained->samples = device->spec.samples; |
1439 | } else { |
1440 | build_stream = SDL_TRUE; |
1441 | } |
1442 | } |
1443 | |
1444 | SDL_CalculateAudioSpec(obtained); /* recalc after possible changes. */ |
1445 | |
1446 | device->callbackspec = *obtained; |
1447 | |
1448 | if (build_stream) { |
1449 | if (iscapture) { |
1450 | device->stream = SDL_NewAudioStream(device->spec.format, |
1451 | device->spec.channels, device->spec.freq, |
1452 | obtained->format, obtained->channels, obtained->freq); |
1453 | } else { |
1454 | device->stream = SDL_NewAudioStream(obtained->format, obtained->channels, |
1455 | obtained->freq, device->spec.format, |
1456 | device->spec.channels, device->spec.freq); |
1457 | } |
1458 | |
1459 | if (!device->stream) { |
1460 | close_audio_device(device); |
1461 | return 0; |
1462 | } |
1463 | } |
1464 | |
1465 | if (device->spec.callback == NULL) { /* use buffer queueing? */ |
1466 | /* pool a few packets to start. Enough for two callbacks. */ |
1467 | device->buffer_queue = SDL_NewDataQueue(SDL_AUDIOBUFFERQUEUE_PACKETLEN, obtained->size * 2); |
1468 | if (!device->buffer_queue) { |
1469 | close_audio_device(device); |
1470 | SDL_SetError("Couldn't create audio buffer queue" ); |
1471 | return 0; |
1472 | } |
1473 | device->callbackspec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback; |
1474 | device->callbackspec.userdata = device; |
1475 | } |
1476 | |
1477 | /* Allocate a scratch audio buffer */ |
1478 | device->work_buffer_len = build_stream ? device->callbackspec.size : 0; |
1479 | if (device->spec.size > device->work_buffer_len) { |
1480 | device->work_buffer_len = device->spec.size; |
1481 | } |
1482 | SDL_assert(device->work_buffer_len > 0); |
1483 | |
1484 | device->work_buffer = (Uint8 *) SDL_malloc(device->work_buffer_len); |
1485 | if (device->work_buffer == NULL) { |
1486 | close_audio_device(device); |
1487 | SDL_OutOfMemory(); |
1488 | return 0; |
1489 | } |
1490 | |
1491 | open_devices[id] = device; /* add it to our list of open devices. */ |
1492 | |
1493 | /* Start the audio thread if necessary */ |
1494 | if (!current_audio.impl.ProvidesOwnCallbackThread) { |
1495 | /* Start the audio thread */ |
1496 | /* !!! FIXME: we don't force the audio thread stack size here if it calls into user code, but maybe we should? */ |
1497 | /* buffer queueing callback only needs a few bytes, so make the stack tiny. */ |
1498 | const size_t stacksize = is_internal_thread ? 64 * 1024 : 0; |
1499 | char threadname[64]; |
1500 | |
1501 | SDL_snprintf(threadname, sizeof (threadname), "SDLAudio%c%d" , (iscapture) ? 'C' : 'P', (int) device->id); |
1502 | device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device); |
1503 | |
1504 | if (device->thread == NULL) { |
1505 | close_audio_device(device); |
1506 | SDL_SetError("Couldn't create audio thread" ); |
1507 | return 0; |
1508 | } |
1509 | } |
1510 | |
1511 | return device->id; |
1512 | } |
1513 | |
1514 | |
1515 | int |
1516 | SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained) |
1517 | { |
1518 | SDL_AudioDeviceID id = 0; |
1519 | |
1520 | /* Start up the audio driver, if necessary. This is legacy behaviour! */ |
1521 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
1522 | if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { |
1523 | return -1; |
1524 | } |
1525 | } |
1526 | |
1527 | /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */ |
1528 | if (open_devices[0] != NULL) { |
1529 | SDL_SetError("Audio device is already opened" ); |
1530 | return -1; |
1531 | } |
1532 | |
1533 | if (obtained) { |
1534 | id = open_audio_device(NULL, 0, desired, obtained, |
1535 | SDL_AUDIO_ALLOW_ANY_CHANGE, 1); |
1536 | } else { |
1537 | SDL_AudioSpec _obtained; |
1538 | SDL_zero(_obtained); |
1539 | id = open_audio_device(NULL, 0, desired, &_obtained, 0, 1); |
1540 | /* On successful open, copy calculated values into 'desired'. */ |
1541 | if (id > 0) { |
1542 | desired->size = _obtained.size; |
1543 | desired->silence = _obtained.silence; |
1544 | } |
1545 | } |
1546 | |
1547 | SDL_assert((id == 0) || (id == 1)); |
1548 | return (id == 0) ? -1 : 0; |
1549 | } |
1550 | |
1551 | SDL_AudioDeviceID |
1552 | SDL_OpenAudioDevice(const char *device, int iscapture, |
1553 | const SDL_AudioSpec * desired, SDL_AudioSpec * obtained, |
1554 | int allowed_changes) |
1555 | { |
1556 | return open_audio_device(device, iscapture, desired, obtained, |
1557 | allowed_changes, 2); |
1558 | } |
1559 | |
1560 | SDL_AudioStatus |
1561 | SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid) |
1562 | { |
1563 | SDL_AudioDevice *device = get_audio_device(devid); |
1564 | SDL_AudioStatus status = SDL_AUDIO_STOPPED; |
1565 | if (device && SDL_AtomicGet(&device->enabled)) { |
1566 | if (SDL_AtomicGet(&device->paused)) { |
1567 | status = SDL_AUDIO_PAUSED; |
1568 | } else { |
1569 | status = SDL_AUDIO_PLAYING; |
1570 | } |
1571 | } |
1572 | return status; |
1573 | } |
1574 | |
1575 | |
1576 | SDL_AudioStatus |
1577 | SDL_GetAudioStatus(void) |
1578 | { |
1579 | return SDL_GetAudioDeviceStatus(1); |
1580 | } |
1581 | |
1582 | void |
1583 | SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on) |
1584 | { |
1585 | SDL_AudioDevice *device = get_audio_device(devid); |
1586 | if (device) { |
1587 | current_audio.impl.LockDevice(device); |
1588 | SDL_AtomicSet(&device->paused, pause_on ? 1 : 0); |
1589 | current_audio.impl.UnlockDevice(device); |
1590 | } |
1591 | } |
1592 | |
1593 | void |
1594 | SDL_PauseAudio(int pause_on) |
1595 | { |
1596 | SDL_PauseAudioDevice(1, pause_on); |
1597 | } |
1598 | |
1599 | |
1600 | void |
1601 | SDL_LockAudioDevice(SDL_AudioDeviceID devid) |
1602 | { |
1603 | /* Obtain a lock on the mixing buffers */ |
1604 | SDL_AudioDevice *device = get_audio_device(devid); |
1605 | if (device) { |
1606 | current_audio.impl.LockDevice(device); |
1607 | } |
1608 | } |
1609 | |
1610 | void |
1611 | SDL_LockAudio(void) |
1612 | { |
1613 | SDL_LockAudioDevice(1); |
1614 | } |
1615 | |
1616 | void |
1617 | SDL_UnlockAudioDevice(SDL_AudioDeviceID devid) |
1618 | { |
1619 | /* Obtain a lock on the mixing buffers */ |
1620 | SDL_AudioDevice *device = get_audio_device(devid); |
1621 | if (device) { |
1622 | current_audio.impl.UnlockDevice(device); |
1623 | } |
1624 | } |
1625 | |
1626 | void |
1627 | SDL_UnlockAudio(void) |
1628 | { |
1629 | SDL_UnlockAudioDevice(1); |
1630 | } |
1631 | |
1632 | void |
1633 | SDL_CloseAudioDevice(SDL_AudioDeviceID devid) |
1634 | { |
1635 | close_audio_device(get_audio_device(devid)); |
1636 | } |
1637 | |
1638 | void |
1639 | SDL_CloseAudio(void) |
1640 | { |
1641 | SDL_CloseAudioDevice(1); |
1642 | } |
1643 | |
1644 | void |
1645 | SDL_AudioQuit(void) |
1646 | { |
1647 | SDL_AudioDeviceID i; |
1648 | |
1649 | if (!current_audio.name) { /* not initialized?! */ |
1650 | return; |
1651 | } |
1652 | |
1653 | for (i = 0; i < SDL_arraysize(open_devices); i++) { |
1654 | close_audio_device(open_devices[i]); |
1655 | } |
1656 | |
1657 | free_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount); |
1658 | free_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount); |
1659 | |
1660 | /* Free the driver data */ |
1661 | current_audio.impl.Deinitialize(); |
1662 | |
1663 | SDL_DestroyMutex(current_audio.detectionLock); |
1664 | |
1665 | SDL_zero(current_audio); |
1666 | SDL_zeroa(open_devices); |
1667 | |
1668 | #ifdef HAVE_LIBSAMPLERATE_H |
1669 | UnloadLibSampleRate(); |
1670 | #endif |
1671 | |
1672 | SDL_FreeResampleFilter(); |
1673 | } |
1674 | |
1675 | #define NUM_FORMATS 10 |
1676 | static int format_idx; |
1677 | static int format_idx_sub; |
1678 | static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = { |
1679 | {AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, |
1680 | AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB}, |
1681 | {AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, |
1682 | AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB}, |
1683 | {AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB, |
1684 | AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8}, |
1685 | {AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB, |
1686 | AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8}, |
1687 | {AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB, |
1688 | AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8}, |
1689 | {AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB, |
1690 | AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8}, |
1691 | {AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB, |
1692 | AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8}, |
1693 | {AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB, |
1694 | AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8}, |
1695 | {AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB, |
1696 | AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8}, |
1697 | {AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB, |
1698 | AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8}, |
1699 | }; |
1700 | |
1701 | SDL_AudioFormat |
1702 | SDL_FirstAudioFormat(SDL_AudioFormat format) |
1703 | { |
1704 | for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) { |
1705 | if (format_list[format_idx][0] == format) { |
1706 | break; |
1707 | } |
1708 | } |
1709 | format_idx_sub = 0; |
1710 | return SDL_NextAudioFormat(); |
1711 | } |
1712 | |
1713 | SDL_AudioFormat |
1714 | SDL_NextAudioFormat(void) |
1715 | { |
1716 | if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) { |
1717 | return 0; |
1718 | } |
1719 | return format_list[format_idx][format_idx_sub++]; |
1720 | } |
1721 | |
1722 | Uint8 |
1723 | SDL_SilenceValueForFormat(const SDL_AudioFormat format) |
1724 | { |
1725 | switch (format) { |
1726 | /* !!! FIXME: 0x80 isn't perfect for U16, but we can't fit 0x8000 in a |
1727 | !!! FIXME: byte for memset() use. This is actually 0.1953 percent |
1728 | !!! FIXME: off from silence. Maybe just don't use U16. */ |
1729 | case AUDIO_U16LSB: |
1730 | case AUDIO_U16MSB: |
1731 | case AUDIO_U8: |
1732 | return 0x80; |
1733 | |
1734 | default: break; |
1735 | } |
1736 | |
1737 | return 0x00; |
1738 | } |
1739 | |
1740 | void |
1741 | SDL_CalculateAudioSpec(SDL_AudioSpec * spec) |
1742 | { |
1743 | spec->silence = SDL_SilenceValueForFormat(spec->format); |
1744 | spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8; |
1745 | spec->size *= spec->channels; |
1746 | spec->size *= spec->samples; |
1747 | } |
1748 | |
1749 | |
1750 | /* |
1751 | * Moved here from SDL_mixer.c, since it relies on internals of an opened |
1752 | * audio device (and is deprecated, by the way!). |
1753 | */ |
1754 | void |
1755 | SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume) |
1756 | { |
1757 | /* Mix the user-level audio format */ |
1758 | SDL_AudioDevice *device = get_audio_device(1); |
1759 | if (device != NULL) { |
1760 | SDL_MixAudioFormat(dst, src, device->callbackspec.format, len, volume); |
1761 | } |
1762 | } |
1763 | |
1764 | /* vi: set ts=4 sw=4 expandtab: */ |
1765 | |