1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* Allow access to a raw mixing buffer */
25
26 #include "SDL.h"
27 #include "SDL_audio_c.h"
28 #include "SDL_audiomem.h"
29 #include "SDL_sysaudio.h"
30
31 #ifdef __OS2__
32 /* We'll need the DosSetPriority() API! */
33 #define INCL_DOSPROCESS
34 #include <os2.h>
35 #endif
36
37 /* Available audio drivers */
38 static AudioBootStrap *bootstrap[] = {
39 #if SDL_AUDIO_DRIVER_PULSE
40 &PULSE_bootstrap,
41 #endif
42 #if SDL_AUDIO_DRIVER_ALSA
43 &ALSA_bootstrap,
44 #endif
45 #if SDL_AUDIO_DRIVER_BSD
46 &BSD_AUDIO_bootstrap,
47 #endif
48 #if SDL_AUDIO_DRIVER_OSS
49 &DSP_bootstrap,
50 &DMA_bootstrap,
51 #endif
52 #if SDL_AUDIO_DRIVER_QNXNTO
53 &QNXNTOAUDIO_bootstrap,
54 #endif
55 #if SDL_AUDIO_DRIVER_SUNAUDIO
56 &SUNAUDIO_bootstrap,
57 #endif
58 #if SDL_AUDIO_DRIVER_DMEDIA
59 &DMEDIA_bootstrap,
60 #endif
61 #if SDL_AUDIO_DRIVER_ARTS
62 &ARTS_bootstrap,
63 #endif
64 #if SDL_AUDIO_DRIVER_ESD
65 &ESD_bootstrap,
66 #endif
67 #if SDL_AUDIO_DRIVER_NAS
68 &NAS_bootstrap,
69 #endif
70 #if SDL_AUDIO_DRIVER_DSOUND
71 &DSOUND_bootstrap,
72 #endif
73 #if SDL_AUDIO_DRIVER_WAVEOUT
74 &WAVEOUT_bootstrap,
75 #endif
76 #if SDL_AUDIO_DRIVER_PAUD
77 &Paud_bootstrap,
78 #endif
79 #if SDL_AUDIO_DRIVER_BAUDIO
80 &BAUDIO_bootstrap,
81 #endif
82 #if SDL_AUDIO_DRIVER_COREAUDIO
83 &COREAUDIO_bootstrap,
84 #endif
85 #if SDL_AUDIO_DRIVER_SNDMGR
86 &SNDMGR_bootstrap,
87 #endif
88 #if SDL_AUDIO_DRIVER_MINT
89 &MINTAUDIO_GSXB_bootstrap,
90 &MINTAUDIO_MCSN_bootstrap,
91 &MINTAUDIO_STFA_bootstrap,
92 &MINTAUDIO_XBIOS_bootstrap,
93 &MINTAUDIO_DMA8_bootstrap,
94 #endif
95 #if SDL_AUDIO_DRIVER_DISK
96 &DISKAUD_bootstrap,
97 #endif
98 #if SDL_AUDIO_DRIVER_DUMMY
99 &DUMMYAUD_bootstrap,
100 #endif
101 #if SDL_AUDIO_DRIVER_DC
102 &DCAUD_bootstrap,
103 #endif
104 #if SDL_AUDIO_DRIVER_NDS
105 &NDSAUD_bootstrap,
106 #endif
107 #if SDL_AUDIO_DRIVER_MMEAUDIO
108 &MMEAUDIO_bootstrap,
109 #endif
110 #if SDL_AUDIO_DRIVER_DART
111 &DART_bootstrap,
112 #endif
113 #if SDL_AUDIO_DRIVER_EPOCAUDIO
114 &EPOCAudio_bootstrap,
115 #endif
116 NULL
117 };
118 SDL_AudioDevice *current_audio = NULL;
119
120 /* Various local functions */
121 int SDL_AudioInit(const char *driver_name);
122 void SDL_AudioQuit(void);
123
124 /* The general mixing thread function */
SDL_RunAudio(void * audiop)125 int SDLCALL SDL_RunAudio(void *audiop)
126 {
127 SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
128 Uint8 *stream;
129 int stream_len;
130 void *udata;
131 void (SDLCALL *fill)(void *userdata,Uint8 *stream, int len);
132 int silence;
133
134 /* Perform any thread setup */
135 if ( audio->ThreadInit ) {
136 audio->ThreadInit(audio);
137 }
138 audio->threadid = SDL_ThreadID();
139
140 /* Set up the mixing function */
141 fill = audio->spec.callback;
142 udata = audio->spec.userdata;
143
144 if ( audio->convert.needed ) {
145 if ( audio->convert.src_format == AUDIO_U8 ) {
146 silence = 0x80;
147 } else {
148 silence = 0;
149 }
150 stream_len = audio->convert.len;
151 } else {
152 silence = audio->spec.silence;
153 stream_len = audio->spec.size;
154 }
155
156 #ifdef __OS2__
157 /* Increase the priority of this thread to make sure that
158 the audio will be continuous all the time! */
159 #ifdef USE_DOSSETPRIORITY
160 if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO"))
161 {
162 #ifdef DEBUG_BUILD
163 printf("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n", SDL_ThreadID());
164 #endif
165 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
166 }
167 else
168 {
169 #ifdef DEBUG_BUILD
170 printf("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n", SDL_ThreadID());
171 #endif
172 DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
173 }
174 #endif
175 #endif
176
177 /* Loop, filling the audio buffers */
178 while ( audio->enabled ) {
179
180 /* Fill the current buffer with sound */
181 if ( audio->convert.needed ) {
182 if ( audio->convert.buf ) {
183 stream = audio->convert.buf;
184 } else {
185 continue;
186 }
187 } else {
188 stream = audio->GetAudioBuf(audio);
189 if ( stream == NULL ) {
190 stream = audio->fake_stream;
191 }
192 }
193
194 SDL_memset(stream, silence, stream_len);
195
196 if ( ! audio->paused ) {
197 SDL_mutexP(audio->mixer_lock);
198 (*fill)(udata, stream, stream_len);
199 SDL_mutexV(audio->mixer_lock);
200 }
201
202 /* Convert the audio if necessary */
203 if ( audio->convert.needed ) {
204 SDL_ConvertAudio(&audio->convert);
205 stream = audio->GetAudioBuf(audio);
206 if ( stream == NULL ) {
207 stream = audio->fake_stream;
208 }
209 SDL_memcpy(stream, audio->convert.buf,
210 audio->convert.len_cvt);
211 }
212
213 /* Ready current buffer for play and change current buffer */
214 if ( stream != audio->fake_stream ) {
215 audio->PlayAudio(audio);
216 }
217
218 /* Wait for an audio buffer to become available */
219 if ( stream == audio->fake_stream ) {
220 SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
221 } else {
222 audio->WaitAudio(audio);
223 }
224 }
225
226 /* Wait for the audio to drain.. */
227 if ( audio->WaitDone ) {
228 audio->WaitDone(audio);
229 }
230
231 #ifdef __OS2__
232 #ifdef DEBUG_BUILD
233 printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID());
234 #endif
235 #endif
236 return(0);
237 }
238
SDL_LockAudio_Default(SDL_AudioDevice * audio)239 static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
240 {
241 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
242 return;
243 }
244 SDL_mutexP(audio->mixer_lock);
245 }
246
SDL_UnlockAudio_Default(SDL_AudioDevice * audio)247 static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
248 {
249 if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
250 return;
251 }
252 SDL_mutexV(audio->mixer_lock);
253 }
254
SDL_ParseAudioFormat(const char * string)255 static Uint16 SDL_ParseAudioFormat(const char *string)
256 {
257 Uint16 format = 0;
258
259 switch (*string) {
260 case 'U':
261 ++string;
262 format |= 0x0000;
263 break;
264 case 'S':
265 ++string;
266 format |= 0x8000;
267 break;
268 default:
269 return 0;
270 }
271 switch (SDL_atoi(string)) {
272 case 8:
273 string += 1;
274 format |= 8;
275 break;
276 case 16:
277 string += 2;
278 format |= 16;
279 if ( SDL_strcmp(string, "LSB") == 0
280 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
281 || SDL_strcmp(string, "SYS") == 0
282 #endif
283 ) {
284 format |= 0x0000;
285 }
286 if ( SDL_strcmp(string, "MSB") == 0
287 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
288 || SDL_strcmp(string, "SYS") == 0
289 #endif
290 ) {
291 format |= 0x1000;
292 }
293 break;
294 default:
295 return 0;
296 }
297 return format;
298 }
299
SDL_AudioInit(const char * driver_name)300 int SDL_AudioInit(const char *driver_name)
301 {
302 SDL_AudioDevice *audio;
303 int i = 0, idx;
304
305 /* Check to make sure we don't overwrite 'current_audio' */
306 if ( current_audio != NULL ) {
307 SDL_AudioQuit();
308 }
309
310 /* Select the proper audio driver */
311 audio = NULL;
312 idx = 0;
313 #if SDL_AUDIO_DRIVER_ESD
314 if ( (driver_name == NULL) && (SDL_getenv("ESPEAKER") != NULL) ) {
315 /* Ahem, we know that if ESPEAKER is set, user probably wants
316 to use ESD, but don't start it if it's not already running.
317 This probably isn't the place to do this, but... Shh! :)
318 */
319 for ( i=0; bootstrap[i]; ++i ) {
320 if ( SDL_strcasecmp(bootstrap[i]->name, "esd") == 0 ) {
321 #ifdef HAVE_PUTENV
322 const char *esd_no_spawn;
323
324 /* Don't start ESD if it's not running */
325 esd_no_spawn = getenv("ESD_NO_SPAWN");
326 if ( esd_no_spawn == NULL ) {
327 putenv("ESD_NO_SPAWN=1");
328 }
329 #endif
330 if ( bootstrap[i]->available() ) {
331 audio = bootstrap[i]->create(0);
332 break;
333 }
334 #ifdef HAVE_UNSETENV
335 if ( esd_no_spawn == NULL ) {
336 unsetenv("ESD_NO_SPAWN");
337 }
338 #endif
339 }
340 }
341 }
342 #endif /* SDL_AUDIO_DRIVER_ESD */
343 if ( audio == NULL ) {
344 if ( driver_name != NULL ) {
345 #if 0 /* This will be replaced with a better driver selection API */
346 if ( SDL_strrchr(driver_name, ':') != NULL ) {
347 idx = atoi(SDL_strrchr(driver_name, ':')+1);
348 }
349 #endif
350 for ( i=0; bootstrap[i]; ++i ) {
351 if (SDL_strcasecmp(bootstrap[i]->name, driver_name) == 0) {
352 if ( bootstrap[i]->available() ) {
353 audio=bootstrap[i]->create(idx);
354 break;
355 }
356 }
357 }
358 } else {
359 for ( i=0; bootstrap[i]; ++i ) {
360 if ( bootstrap[i]->available() ) {
361 audio = bootstrap[i]->create(idx);
362 if ( audio != NULL ) {
363 break;
364 }
365 }
366 }
367 }
368 if ( audio == NULL ) {
369 SDL_SetError("No available audio device");
370 #if 0 /* Don't fail SDL_Init() if audio isn't available.
371 SDL_OpenAudio() will handle it at that point. *sigh*
372 */
373 return(-1);
374 #endif
375 }
376 }
377 current_audio = audio;
378 if ( current_audio ) {
379 current_audio->name = bootstrap[i]->name;
380 if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
381 current_audio->LockAudio = SDL_LockAudio_Default;
382 current_audio->UnlockAudio = SDL_UnlockAudio_Default;
383 }
384 }
385 return(0);
386 }
387
SDL_AudioDriverName(char * namebuf,int maxlen)388 char *SDL_AudioDriverName(char *namebuf, int maxlen)
389 {
390 if ( current_audio != NULL ) {
391 SDL_strlcpy(namebuf, current_audio->name, maxlen);
392 return(namebuf);
393 }
394 return(NULL);
395 }
396
SDL_OpenAudio(SDL_AudioSpec * desired,SDL_AudioSpec * obtained)397 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
398 {
399 SDL_AudioDevice *audio;
400 const char *env;
401
402 /* Start up the audio driver, if necessary */
403 if ( ! current_audio ) {
404 if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
405 (current_audio == NULL) ) {
406 return(-1);
407 }
408 }
409 audio = current_audio;
410
411 if (audio->opened) {
412 SDL_SetError("Audio device is already opened");
413 return(-1);
414 }
415
416 /* Verify some parameters */
417 if ( desired->freq == 0 ) {
418 env = SDL_getenv("SDL_AUDIO_FREQUENCY");
419 if ( env ) {
420 desired->freq = SDL_atoi(env);
421 }
422 }
423 if ( desired->freq == 0 ) {
424 /* Pick some default audio frequency */
425 desired->freq = 22050;
426 }
427 if ( desired->format == 0 ) {
428 env = SDL_getenv("SDL_AUDIO_FORMAT");
429 if ( env ) {
430 desired->format = SDL_ParseAudioFormat(env);
431 }
432 }
433 if ( desired->format == 0 ) {
434 /* Pick some default audio format */
435 desired->format = AUDIO_S16;
436 }
437 if ( desired->channels == 0 ) {
438 env = SDL_getenv("SDL_AUDIO_CHANNELS");
439 if ( env ) {
440 desired->channels = (Uint8)SDL_atoi(env);
441 }
442 }
443 if ( desired->channels == 0 ) {
444 /* Pick a default number of channels */
445 desired->channels = 2;
446 }
447 switch ( desired->channels ) {
448 case 1: /* Mono */
449 case 2: /* Stereo */
450 case 4: /* surround */
451 case 6: /* surround with center and lfe */
452 break;
453 default:
454 SDL_SetError("1 (mono) and 2 (stereo) channels supported");
455 return(-1);
456 }
457 if ( desired->samples == 0 ) {
458 env = SDL_getenv("SDL_AUDIO_SAMPLES");
459 if ( env ) {
460 desired->samples = (Uint16)SDL_atoi(env);
461 }
462 }
463 if ( desired->samples == 0 ) {
464 /* Pick a default of ~46 ms at desired frequency */
465 int samples = (desired->freq / 1000) * 46;
466 int power2 = 1;
467 while ( power2 < samples ) {
468 power2 *= 2;
469 }
470 desired->samples = power2;
471 }
472 if ( desired->callback == NULL ) {
473 SDL_SetError("SDL_OpenAudio() passed a NULL callback");
474 return(-1);
475 }
476
477 #if SDL_THREADS_DISABLED
478 /* Uses interrupt driven audio, without thread */
479 #else
480 /* Create a semaphore for locking the sound buffers */
481 audio->mixer_lock = SDL_CreateMutex();
482 if ( audio->mixer_lock == NULL ) {
483 SDL_SetError("Couldn't create mixer lock");
484 SDL_CloseAudio();
485 return(-1);
486 }
487 #endif /* SDL_THREADS_DISABLED */
488
489 /* Calculate the silence and size of the audio specification */
490 SDL_CalculateAudioSpec(desired);
491
492 /* Open the audio subsystem */
493 SDL_memcpy(&audio->spec, desired, sizeof(audio->spec));
494 audio->convert.needed = 0;
495 audio->enabled = 1;
496 audio->paused = 1;
497
498 audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
499
500 if ( ! audio->opened ) {
501 SDL_CloseAudio();
502 return(-1);
503 }
504
505 /* If the audio driver changes the buffer size, accept it */
506 if ( audio->spec.samples != desired->samples ) {
507 desired->samples = audio->spec.samples;
508 SDL_CalculateAudioSpec(desired);
509 }
510
511 /* Allocate a fake audio memory buffer */
512 audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
513 if ( audio->fake_stream == NULL ) {
514 SDL_CloseAudio();
515 SDL_OutOfMemory();
516 return(-1);
517 }
518
519 /* See if we need to do any conversion */
520 if ( obtained != NULL ) {
521 SDL_memcpy(obtained, &audio->spec, sizeof(audio->spec));
522 } else if ( desired->freq != audio->spec.freq ||
523 desired->format != audio->spec.format ||
524 desired->channels != audio->spec.channels ) {
525 /* Build an audio conversion block */
526 if ( SDL_BuildAudioCVT(&audio->convert,
527 desired->format, desired->channels,
528 desired->freq,
529 audio->spec.format, audio->spec.channels,
530 audio->spec.freq) < 0 ) {
531 SDL_CloseAudio();
532 return(-1);
533 }
534 if ( audio->convert.needed ) {
535 audio->convert.len = (int) ( ((double) audio->spec.size) /
536 audio->convert.len_ratio );
537 audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
538 audio->convert.len*audio->convert.len_mult);
539 if ( audio->convert.buf == NULL ) {
540 SDL_CloseAudio();
541 SDL_OutOfMemory();
542 return(-1);
543 }
544 }
545 }
546
547 /* Start the audio thread if necessary */
548 switch (audio->opened) {
549 case 1:
550 /* Start the audio thread */
551 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) && !defined(__SYMBIAN32__)
552 #undef SDL_CreateThread
553 audio->thread = SDL_CreateThread(SDL_RunAudio, audio, NULL, NULL);
554 #else
555 audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
556 #endif
557 if ( audio->thread == NULL ) {
558 SDL_CloseAudio();
559 SDL_SetError("Couldn't create audio thread");
560 return(-1);
561 }
562 break;
563
564 default:
565 /* The audio is now playing */
566 break;
567 }
568
569 return(0);
570 }
571
SDL_GetAudioStatus(void)572 SDL_audiostatus SDL_GetAudioStatus(void)
573 {
574 SDL_AudioDevice *audio = current_audio;
575 SDL_audiostatus status;
576
577 status = SDL_AUDIO_STOPPED;
578 if ( audio && audio->enabled ) {
579 if ( audio->paused ) {
580 status = SDL_AUDIO_PAUSED;
581 } else {
582 status = SDL_AUDIO_PLAYING;
583 }
584 }
585 return(status);
586 }
587
SDL_PauseAudio(int pause_on)588 void SDL_PauseAudio (int pause_on)
589 {
590 SDL_AudioDevice *audio = current_audio;
591
592 if ( audio ) {
593 audio->paused = pause_on;
594 }
595 }
596
SDL_LockAudio(void)597 void SDL_LockAudio (void)
598 {
599 SDL_AudioDevice *audio = current_audio;
600
601 /* Obtain a lock on the mixing buffers */
602 if ( audio && audio->LockAudio ) {
603 audio->LockAudio(audio);
604 }
605 }
606
SDL_UnlockAudio(void)607 void SDL_UnlockAudio (void)
608 {
609 SDL_AudioDevice *audio = current_audio;
610
611 /* Release lock on the mixing buffers */
612 if ( audio && audio->UnlockAudio ) {
613 audio->UnlockAudio(audio);
614 }
615 }
616
SDL_CloseAudio(void)617 void SDL_CloseAudio (void)
618 {
619 SDL_QuitSubSystem(SDL_INIT_AUDIO);
620 }
621
SDL_AudioQuit(void)622 void SDL_AudioQuit(void)
623 {
624 SDL_AudioDevice *audio = current_audio;
625
626 if ( audio ) {
627 audio->enabled = 0;
628 if ( audio->thread != NULL ) {
629 SDL_WaitThread(audio->thread, NULL);
630 }
631 if ( audio->mixer_lock != NULL ) {
632 SDL_DestroyMutex(audio->mixer_lock);
633 }
634 if ( audio->fake_stream != NULL ) {
635 SDL_FreeAudioMem(audio->fake_stream);
636 }
637 if ( audio->convert.needed ) {
638 SDL_FreeAudioMem(audio->convert.buf);
639
640 }
641 if ( audio->opened ) {
642 audio->CloseAudio(audio);
643 audio->opened = 0;
644 }
645 /* Free the driver data */
646 audio->free(audio);
647 current_audio = NULL;
648 }
649 }
650
651 #define NUM_FORMATS 6
652 static int format_idx;
653 static int format_idx_sub;
654 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
655 { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
656 { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
657 { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
658 { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
659 { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
660 { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
661 };
662
SDL_FirstAudioFormat(Uint16 format)663 Uint16 SDL_FirstAudioFormat(Uint16 format)
664 {
665 for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
666 if ( format_list[format_idx][0] == format ) {
667 break;
668 }
669 }
670 format_idx_sub = 0;
671 return(SDL_NextAudioFormat());
672 }
673
SDL_NextAudioFormat(void)674 Uint16 SDL_NextAudioFormat(void)
675 {
676 if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
677 return(0);
678 }
679 return(format_list[format_idx][format_idx_sub++]);
680 }
681
SDL_CalculateAudioSpec(SDL_AudioSpec * spec)682 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
683 {
684 switch (spec->format) {
685 case AUDIO_U8:
686 spec->silence = 0x80;
687 break;
688 default:
689 spec->silence = 0x00;
690 break;
691 }
692 spec->size = (spec->format&0xFF)/8;
693 spec->size *= spec->channels;
694 spec->size *= spec->samples;
695 }
696
SDL_Audio_SetCaption(const char * caption)697 void SDL_Audio_SetCaption(const char *caption)
698 {
699 if ((current_audio) && (current_audio->SetCaption)) {
700 current_audio->SetCaption(current_audio, caption);
701 }
702 }
703
704