src/audio/SDL_audio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jun 2009 04:37:27 +0000
changeset 3162 dc1eb82ffdaa
parent 3139 7f684f249ec9
child 3242 af4a5af3cd2b
permissions -rw-r--r--
Von: Thomas Zimmermann
Betreff: [SDL] [PATCH] Make static variables const
Datum: Tue, 19 May 2009 19:45:37 +0200

Hi,

this is a set of simple changes which make some of SDL's internal static
arrays constant. The purpose is to shrink the number of write-able
static bytes and thus increase the number of memory pages shared between
SDL applications.

The patch set is against trunk@4513. Each of the attached patch files is
specific to a sub-system. The set is completed by a second mail, because
of the list's 40 KiB limit.

The files readelf-r4513.txt and readelf-const-patch.txt where made by
calling 'readelf -S libSDL.so'. They show the difference in ELF sections
without and with the patch. Some numbers measured on my x86-64:

Before

[13] .rodata PROGBITS 00000000000eaaa0 000eaaa0
0000000000008170 0000000000000000 A 0 0 32
[19] .data.rel.ro PROGBITS 00000000003045e0 001045e0
00000000000023d0 0000000000000000 WA 0 0 32
[23] .data PROGBITS 00000000003076e0 001076e0
0000000000004988 0000000000000000 WA 0 0 32

After

[13] .rodata PROGBITS 00000000000eaaa0 000eaaa0
0000000000009a50 0000000000000000 A 0 0 32
[19] .data.rel.ro PROGBITS 0000000000306040 00106040
0000000000002608 0000000000000000 WA 0 0 32
[23] .data PROGBITS 0000000000309360 00109360
0000000000002e88 0000000000000000 WA 0 0 32

The size of the write-able data section decreased considerably. Some
entries became const-after-relocation, while most of its content went
straight into the read-only data section.

Best regards, Thomas
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 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.h"
    28 #include "SDL_audio_c.h"
    29 #include "SDL_audiomem.h"
    30 #include "SDL_sysaudio.h"
    31 
    32 #define _THIS    SDL_AudioDevice *this
    33 
    34 static SDL_AudioDriver current_audio;
    35 static SDL_AudioDevice *open_devices[16];
    36 
    37 /* !!! FIXME: These are wordy and unlocalized... */
    38 #define DEFAULT_OUTPUT_DEVNAME "System audio output device"
    39 #define DEFAULT_INPUT_DEVNAME "System audio capture device"
    40 
    41 
    42 /*
    43  * Not all of these will be compiled and linked in, but it's convenient
    44  *  to have a complete list here and saves yet-another block of #ifdefs...
    45  *  Please see bootstrap[], below, for the actual #ifdef mess.
    46  */
    47 extern AudioBootStrap BSD_AUDIO_bootstrap;
    48 extern AudioBootStrap DSP_bootstrap;
    49 extern AudioBootStrap DMA_bootstrap;
    50 extern AudioBootStrap ALSA_bootstrap;
    51 extern AudioBootStrap PULSEAUDIO_bootstrap;
    52 extern AudioBootStrap QSAAUDIO_bootstrap;
    53 extern AudioBootStrap SUNAUDIO_bootstrap;
    54 extern AudioBootStrap DMEDIA_bootstrap;
    55 extern AudioBootStrap ARTS_bootstrap;
    56 extern AudioBootStrap ESD_bootstrap;
    57 extern AudioBootStrap NAS_bootstrap;
    58 extern AudioBootStrap DSOUND_bootstrap;
    59 extern AudioBootStrap WINWAVEOUT_bootstrap;
    60 extern AudioBootStrap PAUDIO_bootstrap;
    61 extern AudioBootStrap BEOSAUDIO_bootstrap;
    62 extern AudioBootStrap COREAUDIO_bootstrap;
    63 extern AudioBootStrap COREAUDIOIPHONE_bootstrap;
    64 extern AudioBootStrap SNDMGR_bootstrap;
    65 extern AudioBootStrap MINTAUDIO_GSXB_bootstrap;
    66 extern AudioBootStrap MINTAUDIO_MCSN_bootstrap;
    67 extern AudioBootStrap MINTAUDIO_STFA_bootstrap;
    68 extern AudioBootStrap MINTAUDIO_XBIOS_bootstrap;
    69 extern AudioBootStrap MINTAUDIO_DMA8_bootstrap;
    70 extern AudioBootStrap DISKAUD_bootstrap;
    71 extern AudioBootStrap DUMMYAUD_bootstrap;
    72 extern AudioBootStrap DCAUD_bootstrap;
    73 extern AudioBootStrap MMEAUDIO_bootstrap;
    74 extern AudioBootStrap DART_bootstrap;
    75 extern AudioBootStrap NDSAUD_bootstrap;
    76 extern AudioBootStrap FUSIONSOUND_bootstrap;
    77 
    78 
    79 /* Available audio drivers */
    80 static const AudioBootStrap *const bootstrap[] = {
    81 #if SDL_AUDIO_DRIVER_BSD
    82     &BSD_AUDIO_bootstrap,
    83 #endif
    84 #if SDL_AUDIO_DRIVER_PULSEAUDIO
    85     &PULSEAUDIO_bootstrap,
    86 #endif
    87 #if SDL_AUDIO_DRIVER_ALSA
    88     &ALSA_bootstrap,
    89 #endif
    90 #if SDL_AUDIO_DRIVER_OSS
    91     &DSP_bootstrap,
    92     &DMA_bootstrap,
    93 #endif
    94 #if SDL_AUDIO_DRIVER_QSA
    95     &QSAAUDIO_bootstrap,
    96 #endif
    97 #if SDL_AUDIO_DRIVER_SUNAUDIO
    98     &SUNAUDIO_bootstrap,
    99 #endif
   100 #if SDL_AUDIO_DRIVER_DMEDIA
   101     &DMEDIA_bootstrap,
   102 #endif
   103 #if SDL_AUDIO_DRIVER_ARTS
   104     &ARTS_bootstrap,
   105 #endif
   106 #if SDL_AUDIO_DRIVER_ESD
   107     &ESD_bootstrap,
   108 #endif
   109 #if SDL_AUDIO_DRIVER_NAS
   110     &NAS_bootstrap,
   111 #endif
   112 #if SDL_AUDIO_DRIVER_DSOUND
   113     &DSOUND_bootstrap,
   114 #endif
   115 #if SDL_AUDIO_DRIVER_WINWAVEOUT
   116     &WINWAVEOUT_bootstrap,
   117 #endif
   118 #if SDL_AUDIO_DRIVER_PAUDIO
   119     &PAUDIO_bootstrap,
   120 #endif
   121 #if SDL_AUDIO_DRIVER_BEOSAUDIO
   122     &BEOSAUDIO_bootstrap,
   123 #endif
   124 #if SDL_AUDIO_DRIVER_COREAUDIO
   125     &COREAUDIO_bootstrap,
   126 #endif
   127 #if SDL_AUDIO_DRIVER_COREAUDIOIPHONE
   128     &COREAUDIOIPHONE_bootstrap,
   129 #endif
   130 #if SDL_AUDIO_DRIVER_SNDMGR
   131     &SNDMGR_bootstrap,
   132 #endif
   133 #if SDL_AUDIO_DRIVER_MINT
   134     &MINTAUDIO_GSXB_bootstrap,
   135     &MINTAUDIO_MCSN_bootstrap,
   136     &MINTAUDIO_STFA_bootstrap,
   137     &MINTAUDIO_XBIOS_bootstrap,
   138     &MINTAUDIO_DMA8_bootstrap,
   139 #endif
   140 #if SDL_AUDIO_DRIVER_DISK
   141     &DISKAUD_bootstrap,
   142 #endif
   143 #if SDL_AUDIO_DRIVER_DUMMY
   144     &DUMMYAUD_bootstrap,
   145 #endif
   146 #if SDL_AUDIO_DRIVER_DC
   147     &DCAUD_bootstrap,
   148 #endif
   149 #if SDL_AUDIO_DRIVER_MMEAUDIO
   150     &MMEAUDIO_bootstrap,
   151 #endif
   152 #if SDL_AUDIO_DRIVER_DART
   153     &DART_bootstrap,
   154 #endif
   155 #if SDL_AUDIO_DRIVER_NDS
   156     &NDSAUD_bootstrap,
   157 #endif
   158 #if SDL_AUDIO_DRIVER_FUSIONSOUND
   159     &FUSIONSOUND_bootstrap,
   160 #endif
   161     NULL
   162 };
   163 
   164 static SDL_AudioDevice *
   165 get_audio_device(SDL_AudioDeviceID id)
   166 {
   167     id--;
   168     if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL)) {
   169         SDL_SetError("Invalid audio device ID");
   170         return NULL;
   171     }
   172 
   173     return open_devices[id];
   174 }
   175 
   176 
   177 /* stubs for audio drivers that don't need a specific entry point... */
   178 static int
   179 SDL_AudioDetectDevices_Default(int iscapture)
   180 {
   181     return -1;
   182 }
   183 
   184 static void
   185 SDL_AudioThreadInit_Default(_THIS)
   186 {                               /* no-op. */
   187 }
   188 
   189 static void
   190 SDL_AudioWaitDevice_Default(_THIS)
   191 {                               /* no-op. */
   192 }
   193 
   194 static void
   195 SDL_AudioPlayDevice_Default(_THIS)
   196 {                               /* no-op. */
   197 }
   198 
   199 static Uint8 *
   200 SDL_AudioGetDeviceBuf_Default(_THIS)
   201 {
   202     return NULL;
   203 }
   204 
   205 static void
   206 SDL_AudioWaitDone_Default(_THIS)
   207 {                               /* no-op. */
   208 }
   209 
   210 static void
   211 SDL_AudioCloseDevice_Default(_THIS)
   212 {                               /* no-op. */
   213 }
   214 
   215 static void
   216 SDL_AudioDeinitialize_Default(void)
   217 {                               /* no-op. */
   218 }
   219 
   220 static int
   221 SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
   222 {
   223     return 0;
   224 }
   225 
   226 static const char *
   227 SDL_AudioGetDeviceName_Default(int index, int iscapture)
   228 {
   229     SDL_SetError("No such device");
   230     return NULL;
   231 }
   232 
   233 static void
   234 SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
   235 {
   236     if (device->thread && (SDL_ThreadID() == device->threadid)) {
   237         return;
   238     }
   239     SDL_mutexP(device->mixer_lock);
   240 }
   241 
   242 static void
   243 SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
   244 {
   245     if (device->thread && (SDL_ThreadID() == device->threadid)) {
   246         return;
   247     }
   248     SDL_mutexV(device->mixer_lock);
   249 }
   250 
   251 
   252 static void
   253 finalize_audio_entry_points(void)
   254 {
   255     /*
   256      * Fill in stub functions for unused driver entry points. This lets us
   257      *  blindly call them without having to check for validity first.
   258      */
   259 
   260 #define FILL_STUB(x) \
   261         if (current_audio.impl.x == NULL) { \
   262             current_audio.impl.x = SDL_Audio##x##_Default; \
   263         }
   264     FILL_STUB(DetectDevices);
   265     FILL_STUB(GetDeviceName);
   266     FILL_STUB(OpenDevice);
   267     FILL_STUB(ThreadInit);
   268     FILL_STUB(WaitDevice);
   269     FILL_STUB(PlayDevice);
   270     FILL_STUB(GetDeviceBuf);
   271     FILL_STUB(WaitDone);
   272     FILL_STUB(CloseDevice);
   273     FILL_STUB(LockDevice);
   274     FILL_STUB(UnlockDevice);
   275     FILL_STUB(Deinitialize);
   276 #undef FILL_STUB
   277 }
   278 
   279 /* Streaming functions (for when the input and output buffer sizes are different) */
   280 /* Write [length] bytes from buf into the streamer */
   281 void
   282 SDL_StreamWrite(SDL_AudioStreamer * stream, Uint8 * buf, int length)
   283 {
   284     int i;
   285 
   286     for (i = 0; i < length; ++i) {
   287         stream->buffer[stream->write_pos] = buf[i];
   288         ++stream->write_pos;
   289     }
   290 }
   291 
   292 /* Read [length] bytes out of the streamer into buf */
   293 void
   294 SDL_StreamRead(SDL_AudioStreamer * stream, Uint8 * buf, int length)
   295 {
   296     int i;
   297 
   298     for (i = 0; i < length; ++i) {
   299         buf[i] = stream->buffer[stream->read_pos];
   300         ++stream->read_pos;
   301     }
   302 }
   303 
   304 int
   305 SDL_StreamLength(SDL_AudioStreamer * stream)
   306 {
   307     return (stream->write_pos - stream->read_pos) % stream->max_len;
   308 }
   309 
   310 /* Initialize the stream by allocating the buffer and setting the read/write heads to the beginning */
   311 int
   312 SDL_StreamInit(SDL_AudioStreamer * stream, int max_len, Uint8 silence)
   313 {
   314     /* First try to allocate the buffer */
   315     stream->buffer = (Uint8 *) SDL_malloc(max_len);
   316     if (stream->buffer == NULL) {
   317         return -1;
   318     }
   319 
   320     stream->max_len = max_len;
   321     stream->read_pos = 0;
   322     stream->write_pos = 0;
   323 
   324     /* Zero out the buffer */
   325     SDL_memset(stream->buffer, silence, max_len);
   326 
   327     return 0;
   328 }
   329 
   330 /* Deinitialize the stream simply by freeing the buffer */
   331 void
   332 SDL_StreamDeinit(SDL_AudioStreamer * stream)
   333 {
   334     if (stream->buffer != NULL) {
   335         SDL_free(stream->buffer);
   336     }
   337 }
   338 
   339 
   340 /* The general mixing thread function */
   341 int SDLCALL
   342 SDL_RunAudio(void *devicep)
   343 {
   344     SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
   345     Uint8 *stream;
   346     int stream_len;
   347     void *udata;
   348     void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
   349     int silence;
   350 
   351     /* For streaming when the buffer sizes don't match up */
   352     Uint8 *istream;
   353     int istream_len;
   354 
   355     /* Perform any thread setup */
   356     device->threadid = SDL_ThreadID();
   357     current_audio.impl.ThreadInit(device);
   358 
   359     /* Set up the mixing function */
   360     fill = device->spec.callback;
   361     udata = device->spec.userdata;
   362 
   363     /* By default do not stream */
   364     device->use_streamer = 0;
   365 
   366     if (device->convert.needed) {
   367         if (device->convert.src_format == AUDIO_U8) {
   368             silence = 0x80;
   369         } else {
   370             silence = 0;
   371         }
   372 
   373 #if 0                           /* !!! FIXME: I took len_div out of the structure. Use rate_incr instead? */
   374         /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */
   375         if (device->convert.len_mult != 1 || device->convert.len_div != 1) {
   376             /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */
   377             stream_max_len = 2 * device->spec.size;
   378             if (device->convert.len_mult > device->convert.len_div) {
   379                 stream_max_len *= device->convert.len_mult;
   380                 stream_max_len /= device->convert.len_div;
   381             }
   382             if (SDL_StreamInit(&device->streamer, stream_max_len, silence) <
   383                 0)
   384                 return -1;
   385             device->use_streamer = 1;
   386 
   387             /* istream_len should be the length of what we grab from the callback and feed to conversion,
   388                so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d
   389              */
   390             istream_len =
   391                 device->spec.size * device->convert.len_div /
   392                 device->convert.len_mult;
   393         }
   394 #endif
   395 
   396         /* stream_len = device->convert.len; */
   397         stream_len = device->spec.size;
   398     } else {
   399         silence = device->spec.silence;
   400         stream_len = device->spec.size;
   401     }
   402 
   403     /* Determine if the streamer is necessary here */
   404     if (device->use_streamer == 1) {
   405         /* This code is almost the same as the old code. The difference is, instead of reding
   406            directly from the callback into "stream", then converting and sending the audio off,
   407            we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device.
   408            However, reading and writing with streamer are done separately:
   409            - We only call the callback and write to the streamer when the streamer does not
   410            contain enough samples to output to the device.
   411            - We only read from the streamer and tell the device to play when the streamer
   412            does have enough samples to output.
   413            This allows us to perform resampling in the conversion step, where the output of the
   414            resampling process can be any number. We will have to see what a good size for the
   415            stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure.
   416          */
   417         while (device->enabled) {
   418             /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */
   419             if (SDL_StreamLength(&device->streamer) < stream_len) {
   420                 /* Set up istream */
   421                 if (device->convert.needed) {
   422                     if (device->convert.buf) {
   423                         istream = device->convert.buf;
   424                     } else {
   425                         continue;
   426                     }
   427                 } else {
   428                     istream = current_audio.impl.GetDeviceBuf(device);
   429                     if (istream == NULL) {
   430                         istream = device->fake_stream;
   431                     }
   432                 }
   433 
   434                 /* Read from the callback into the _input_ stream */
   435                 if (!device->paused) {
   436                     SDL_mutexP(device->mixer_lock);
   437                     (*fill) (udata, istream, istream_len);
   438                     SDL_mutexV(device->mixer_lock);
   439                 }
   440 
   441                 /* Convert the audio if necessary and write to the streamer */
   442                 if (device->convert.needed) {
   443                     SDL_ConvertAudio(&device->convert);
   444                     if (istream == NULL) {
   445                         istream = device->fake_stream;
   446                     }
   447                     /*SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */
   448                     SDL_StreamWrite(&device->streamer, device->convert.buf,
   449                                     device->convert.len_cvt);
   450                 } else {
   451                     SDL_StreamWrite(&device->streamer, istream, istream_len);
   452                 }
   453             }
   454 
   455             /* Only output audio if the streamer has enough to output */
   456             if (SDL_StreamLength(&device->streamer) >= stream_len) {
   457                 /* Set up the output stream */
   458                 if (device->convert.needed) {
   459                     if (device->convert.buf) {
   460                         stream = device->convert.buf;
   461                     } else {
   462                         continue;
   463                     }
   464                 } else {
   465                     stream = current_audio.impl.GetDeviceBuf(device);
   466                     if (stream == NULL) {
   467                         stream = device->fake_stream;
   468                     }
   469                 }
   470 
   471                 /* Now read from the streamer */
   472                 SDL_StreamRead(&device->streamer, stream, stream_len);
   473 
   474                 /* Ready current buffer for play and change current buffer */
   475                 if (stream != device->fake_stream && !device->paused) {
   476                     current_audio.impl.PlayDevice(device);
   477                     /* Wait for an audio buffer to become available */
   478                     current_audio.impl.WaitDevice(device);
   479                 } else {
   480                     SDL_Delay((device->spec.samples * 1000) /
   481                               device->spec.freq);
   482                 }
   483             }
   484 
   485         }
   486     } else {
   487         /* Otherwise, do not use the streamer. This is the old code. */
   488 
   489         /* Loop, filling the audio buffers */
   490         while (device->enabled) {
   491 
   492             /* Fill the current buffer with sound */
   493             if (device->convert.needed) {
   494                 if (device->convert.buf) {
   495                     stream = device->convert.buf;
   496                 } else {
   497                     continue;
   498                 }
   499             } else {
   500                 stream = current_audio.impl.GetDeviceBuf(device);
   501                 if (stream == NULL) {
   502                     stream = device->fake_stream;
   503                 }
   504             }
   505 
   506             if (!device->paused) {
   507                 SDL_mutexP(device->mixer_lock);
   508                 (*fill) (udata, stream, stream_len);
   509                 SDL_mutexV(device->mixer_lock);
   510             }
   511 
   512             /* Convert the audio if necessary */
   513             if (device->convert.needed) {
   514                 SDL_ConvertAudio(&device->convert);
   515                 stream = current_audio.impl.GetDeviceBuf(device);
   516                 if (stream == NULL) {
   517                     stream = device->fake_stream;
   518                 }
   519                 SDL_memcpy(stream, device->convert.buf,
   520                            device->convert.len_cvt);
   521             }
   522 
   523             /* Ready current buffer for play and change current buffer */
   524             if (stream != device->fake_stream && !device->paused) {
   525                 current_audio.impl.PlayDevice(device);
   526                 /* Wait for an audio buffer to become available */
   527                 current_audio.impl.WaitDevice(device);
   528             } else {
   529                 SDL_Delay((device->spec.samples * 1000) / device->spec.freq);
   530             }
   531         }
   532     }
   533 
   534     /* Wait for the audio to drain.. */
   535     current_audio.impl.WaitDone(device);
   536 
   537     /* If necessary, deinit the streamer */
   538     if (device->use_streamer == 1)
   539         SDL_StreamDeinit(&device->streamer);
   540 
   541     return (0);
   542 }
   543 
   544 
   545 static SDL_AudioFormat
   546 SDL_ParseAudioFormat(const char *string)
   547 {
   548 #define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) return AUDIO_##x
   549     CHECK_FMT_STRING(U8);
   550     CHECK_FMT_STRING(S8);
   551     CHECK_FMT_STRING(U16LSB);
   552     CHECK_FMT_STRING(S16LSB);
   553     CHECK_FMT_STRING(U16MSB);
   554     CHECK_FMT_STRING(S16MSB);
   555     CHECK_FMT_STRING(U16SYS);
   556     CHECK_FMT_STRING(S16SYS);
   557     CHECK_FMT_STRING(U16);
   558     CHECK_FMT_STRING(S16);
   559     CHECK_FMT_STRING(S32LSB);
   560     CHECK_FMT_STRING(S32MSB);
   561     CHECK_FMT_STRING(S32SYS);
   562     CHECK_FMT_STRING(S32);
   563     CHECK_FMT_STRING(F32LSB);
   564     CHECK_FMT_STRING(F32MSB);
   565     CHECK_FMT_STRING(F32SYS);
   566     CHECK_FMT_STRING(F32);
   567 #undef CHECK_FMT_STRING
   568     return 0;
   569 }
   570 
   571 int
   572 SDL_GetNumAudioDrivers(void)
   573 {
   574     return (SDL_arraysize(bootstrap) - 1);
   575 }
   576 
   577 const char *
   578 SDL_GetAudioDriver(int index)
   579 {
   580     if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
   581         return (bootstrap[index]->name);
   582     }
   583     return (NULL);
   584 }
   585 
   586 int
   587 SDL_AudioInit(const char *driver_name)
   588 {
   589     int i = 0;
   590     int initialized = 0;
   591     int tried_to_init = 0;
   592     int rc = 0;
   593     int best_choice = -1;
   594 
   595     if (SDL_WasInit(SDL_INIT_AUDIO)) {
   596         SDL_AudioQuit();        /* shutdown driver if already running. */
   597     }
   598 
   599     SDL_memset(&current_audio, '\0', sizeof(current_audio));
   600     SDL_memset(open_devices, '\0', sizeof(open_devices));
   601 
   602     /* Select the proper audio driver */
   603     if (driver_name == NULL) {
   604         driver_name = SDL_getenv("SDL_AUDIODRIVER");
   605     }
   606 
   607     for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
   608         /* make sure we should even try this driver before doing so... */
   609         const AudioBootStrap *backend = bootstrap[i];
   610         if (((driver_name) && (SDL_strcasecmp(backend->name, driver_name))) ||
   611             ((!driver_name) && (backend->demand_only))) {
   612             continue;
   613         }
   614 
   615         tried_to_init = 1;
   616         SDL_memset(&current_audio, 0, sizeof(current_audio));
   617         current_audio.name = backend->name;
   618         current_audio.desc = backend->desc;
   619         rc = backend->init(&current_audio.impl);
   620         if (rc == 2) {          /* init'd, and devices available. Take it! */
   621             initialized = 1;
   622             best_choice = i;
   623         } else if (rc == 1) {   /* init'd, but can't see any devices. */
   624             if (current_audio.impl.Deinitialize) {
   625                 current_audio.impl.Deinitialize();
   626             }
   627             if (best_choice == -1) {
   628                 best_choice = i;
   629             }
   630         }
   631     }
   632 
   633     /* No definite choice. Pick one that works but can't promise a device. */
   634     if ((!initialized) && (best_choice != -1)) {
   635         const AudioBootStrap *backend = bootstrap[best_choice];
   636         SDL_memset(&current_audio, 0, sizeof(current_audio));
   637         current_audio.name = backend->name;
   638         current_audio.desc = backend->desc;
   639         initialized = (backend->init(&current_audio.impl) > 0);
   640     }
   641 
   642     if (!initialized) {
   643         /* specific drivers will set the error message if they fail... */
   644         if (!tried_to_init) {
   645             if (driver_name) {
   646                 SDL_SetError("%s not available", driver_name);
   647             } else {
   648                 SDL_SetError("No available audio device");
   649             }
   650         }
   651 
   652         SDL_memset(&current_audio, 0, sizeof(current_audio));
   653         return (-1);            /* No driver was available, so fail. */
   654     }
   655 
   656     finalize_audio_entry_points();
   657 
   658     return (0);
   659 }
   660 
   661 /*
   662  * Get the current audio driver name
   663  */
   664 const char *
   665 SDL_GetCurrentAudioDriver()
   666 {
   667     return current_audio.name;
   668 }
   669 
   670 
   671 int
   672 SDL_GetNumAudioDevices(int iscapture)
   673 {
   674     if (!SDL_WasInit(SDL_INIT_AUDIO)) {
   675         return -1;
   676     }
   677     if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
   678         return 0;
   679     }
   680 
   681     if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
   682         return 1;
   683     }
   684 
   685     if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   686         return 1;
   687     }
   688 
   689     return current_audio.impl.DetectDevices(iscapture);
   690 }
   691 
   692 
   693 const char *
   694 SDL_GetAudioDeviceName(int index, int iscapture)
   695 {
   696     if (!SDL_WasInit(SDL_INIT_AUDIO)) {
   697         SDL_SetError("Audio subsystem is not initialized");
   698         return NULL;
   699     }
   700 
   701     if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
   702         SDL_SetError("No capture support");
   703         return NULL;
   704     }
   705 
   706     if (index < 0) {
   707         SDL_SetError("No such device");
   708         return NULL;
   709     }
   710 
   711     if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
   712         return DEFAULT_INPUT_DEVNAME;
   713     }
   714 
   715     if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   716         return DEFAULT_OUTPUT_DEVNAME;
   717     }
   718 
   719     return current_audio.impl.GetDeviceName(index, iscapture);
   720 }
   721 
   722 
   723 static void
   724 close_audio_device(SDL_AudioDevice * device)
   725 {
   726     device->enabled = 0;
   727     if (device->thread != NULL) {
   728         SDL_WaitThread(device->thread, NULL);
   729     }
   730     if (device->mixer_lock != NULL) {
   731         SDL_DestroyMutex(device->mixer_lock);
   732     }
   733     if (device->fake_stream != NULL) {
   734         SDL_FreeAudioMem(device->fake_stream);
   735     }
   736     if (device->convert.needed) {
   737         SDL_FreeAudioMem(device->convert.buf);
   738     }
   739     if (device->opened) {
   740         current_audio.impl.CloseDevice(device);
   741         device->opened = 0;
   742     }
   743     SDL_FreeAudioMem(device);
   744 }
   745 
   746 
   747 /*
   748  * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
   749  *  Fills in a sanitized copy in (prepared).
   750  *  Returns non-zero if okay, zero on fatal parameters in (orig).
   751  */
   752 static int
   753 prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
   754 {
   755     SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
   756 
   757     if (orig->callback == NULL) {
   758         SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   759         return 0;
   760     }
   761 
   762     if (orig->freq == 0) {
   763         const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
   764         if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
   765             prepared->freq = 22050;     /* a reasonable default */
   766         }
   767     }
   768 
   769     if (orig->format == 0) {
   770         const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
   771         if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
   772             prepared->format = AUDIO_S16;       /* a reasonable default */
   773         }
   774     }
   775 
   776     switch (orig->channels) {
   777     case 0:{
   778             const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
   779             if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) {
   780                 prepared->channels = 2; /* a reasonable default */
   781             }
   782             break;
   783         }
   784     case 1:                    /* Mono */
   785     case 2:                    /* Stereo */
   786     case 4:                    /* surround */
   787     case 6:                    /* surround with center and lfe */
   788         break;
   789     default:
   790         SDL_SetError("Unsupported number of audio channels.");
   791         return 0;
   792     }
   793 
   794     if (orig->samples == 0) {
   795         const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
   796         if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) {
   797             /* Pick a default of ~46 ms at desired frequency */
   798             /* !!! FIXME: remove this when the non-Po2 resampling is in. */
   799             const int samples = (prepared->freq / 1000) * 46;
   800             int power2 = 1;
   801             while (power2 < samples) {
   802                 power2 *= 2;
   803             }
   804             prepared->samples = power2;
   805         }
   806     }
   807 
   808     /* Calculate the silence and size of the audio specification */
   809     SDL_CalculateAudioSpec(prepared);
   810 
   811     return 1;
   812 }
   813 
   814 
   815 static SDL_AudioDeviceID
   816 open_audio_device(const char *devname, int iscapture,
   817                   const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
   818                   int allowed_changes, int min_id)
   819 {
   820     SDL_AudioDeviceID id = 0;
   821     SDL_AudioSpec _obtained;
   822     SDL_AudioDevice *device;
   823     SDL_bool build_cvt;
   824     int i = 0;
   825 
   826     if (!SDL_WasInit(SDL_INIT_AUDIO)) {
   827         SDL_SetError("Audio subsystem is not initialized");
   828         return 0;
   829     }
   830 
   831     if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
   832         SDL_SetError("No capture support");
   833         return 0;
   834     }
   835 
   836     if (!obtained) {
   837         obtained = &_obtained;
   838     }
   839     if (!prepare_audiospec(desired, obtained)) {
   840         return 0;
   841     }
   842 
   843     /* If app doesn't care about a specific device, let the user override. */
   844     if (devname == NULL) {
   845         devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
   846     }
   847 
   848     /*
   849      * Catch device names at the high level for the simple case...
   850      * This lets us have a basic "device enumeration" for systems that
   851      *  don't have multiple devices, but makes sure the device name is
   852      *  always NULL when it hits the low level.
   853      *
   854      * Also make sure that the simple case prevents multiple simultaneous
   855      *  opens of the default system device.
   856      */
   857 
   858     if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
   859         if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
   860             SDL_SetError("No such device");
   861             return 0;
   862         }
   863         devname = NULL;
   864 
   865         for (i = 0; i < SDL_arraysize(open_devices); i++) {
   866             if ((open_devices[i]) && (open_devices[i]->iscapture)) {
   867                 SDL_SetError("Audio device already open");
   868                 return 0;
   869             }
   870         }
   871     }
   872 
   873     if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   874         if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
   875             SDL_SetError("No such device");
   876             return 0;
   877         }
   878         devname = NULL;
   879 
   880         for (i = 0; i < SDL_arraysize(open_devices); i++) {
   881             if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
   882                 SDL_SetError("Audio device already open");
   883                 return 0;
   884             }
   885         }
   886     }
   887 
   888     device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
   889     if (device == NULL) {
   890         SDL_OutOfMemory();
   891         return 0;
   892     }
   893     SDL_memset(device, '\0', sizeof(SDL_AudioDevice));
   894     device->spec = *obtained;
   895     device->enabled = 1;
   896     device->paused = 1;
   897     device->iscapture = iscapture;
   898 
   899     /* Create a semaphore for locking the sound buffers */
   900     if (!current_audio.impl.SkipMixerLock) {
   901         device->mixer_lock = SDL_CreateMutex();
   902         if (device->mixer_lock == NULL) {
   903             close_audio_device(device);
   904             SDL_SetError("Couldn't create mixer lock");
   905             return 0;
   906         }
   907     }
   908 
   909     if (!current_audio.impl.OpenDevice(device, devname, iscapture)) {
   910         close_audio_device(device);
   911         return 0;
   912     }
   913     device->opened = 1;
   914 
   915     /* Allocate a fake audio memory buffer */
   916     device->fake_stream = SDL_AllocAudioMem(device->spec.size);
   917     if (device->fake_stream == NULL) {
   918         close_audio_device(device);
   919         SDL_OutOfMemory();
   920         return 0;
   921     }
   922 
   923     /* If the audio driver changes the buffer size, accept it */
   924     if (device->spec.samples != obtained->samples) {
   925         obtained->samples = device->spec.samples;
   926         SDL_CalculateAudioSpec(obtained);
   927     }
   928 
   929     /* See if we need to do any conversion */
   930     build_cvt = SDL_FALSE;
   931     if (obtained->freq != device->spec.freq) {
   932         if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) {
   933             obtained->freq = device->spec.freq;
   934         } else {
   935             build_cvt = SDL_TRUE;
   936         }
   937     }
   938     if (obtained->format != device->spec.format) {
   939         if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) {
   940             obtained->format = device->spec.format;
   941         } else {
   942             build_cvt = SDL_TRUE;
   943         }
   944     }
   945     if (obtained->channels != device->spec.channels) {
   946         if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) {
   947             obtained->channels = device->spec.channels;
   948         } else {
   949             build_cvt = SDL_TRUE;
   950         }
   951     }
   952     if (build_cvt) {
   953         /* Build an audio conversion block */
   954         if (SDL_BuildAudioCVT(&device->convert,
   955                               obtained->format, obtained->channels,
   956                               obtained->freq,
   957                               device->spec.format, device->spec.channels,
   958                               device->spec.freq) < 0) {
   959             close_audio_device(device);
   960             return 0;
   961         }
   962         if (device->convert.needed) {
   963             device->convert.len = (int) (((double) obtained->size) /
   964                                          device->convert.len_ratio);
   965 
   966             device->convert.buf =
   967                 (Uint8 *) SDL_AllocAudioMem(device->convert.len *
   968                                             device->convert.len_mult);
   969             if (device->convert.buf == NULL) {
   970                 close_audio_device(device);
   971                 SDL_OutOfMemory();
   972                 return 0;
   973             }
   974         }
   975     }
   976 
   977     /* Find an available device ID and store the structure... */
   978     for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
   979         if (open_devices[id] == NULL) {
   980             open_devices[id] = device;
   981             break;
   982         }
   983     }
   984 
   985     if (id == SDL_arraysize(open_devices)) {
   986         SDL_SetError("Too many open audio devices");
   987         close_audio_device(device);
   988         return 0;
   989     }
   990 
   991     /* Start the audio thread if necessary */
   992     if (!current_audio.impl.ProvidesOwnCallbackThread) {
   993         /* Start the audio thread */
   994 /* !!! FIXME: this is nasty. */
   995 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   996 #undef SDL_CreateThread
   997         device->thread = SDL_CreateThread(SDL_RunAudio, device, NULL, NULL);
   998 #else
   999         device->thread = SDL_CreateThread(SDL_RunAudio, device);
  1000 #endif
  1001         if (device->thread == NULL) {
  1002             SDL_CloseAudioDevice(id + 1);
  1003             SDL_SetError("Couldn't create audio thread");
  1004             return 0;
  1005         }
  1006     }
  1007 
  1008     return id + 1;
  1009 }
  1010 
  1011 
  1012 int
  1013 SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
  1014 {
  1015     SDL_AudioDeviceID id = 0;
  1016 
  1017     /* Start up the audio driver, if necessary. This is legacy behaviour! */
  1018     if (!SDL_WasInit(SDL_INIT_AUDIO)) {
  1019         if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
  1020             return (-1);
  1021         }
  1022     }
  1023 
  1024     /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
  1025     if (open_devices[0] != NULL) {
  1026         SDL_SetError("Audio device is already opened");
  1027         return (-1);
  1028     }
  1029 
  1030     if (obtained) {
  1031         id = open_audio_device(NULL, 0, desired, obtained,
  1032                                SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
  1033     } else {
  1034         id = open_audio_device(NULL, 0, desired, desired, 0, 1);
  1035     }
  1036     if (id > 1) {               /* this should never happen in theory... */
  1037         SDL_CloseAudioDevice(id);
  1038         SDL_SetError("Internal error"); /* MUST be Device ID #1! */
  1039         return (-1);
  1040     }
  1041 
  1042     return ((id == 0) ? -1 : 0);
  1043 }
  1044 
  1045 SDL_AudioDeviceID
  1046 SDL_OpenAudioDevice(const char *device, int iscapture,
  1047                     const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
  1048                     int allowed_changes)
  1049 {
  1050     return open_audio_device(device, iscapture, desired, obtained,
  1051                              allowed_changes, 2);
  1052 }
  1053 
  1054 SDL_audiostatus
  1055 SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
  1056 {
  1057     SDL_AudioDevice *device = get_audio_device(devid);
  1058     SDL_audiostatus status = SDL_AUDIO_STOPPED;
  1059     if (device && device->enabled) {
  1060         if (device->paused) {
  1061             status = SDL_AUDIO_PAUSED;
  1062         } else {
  1063             status = SDL_AUDIO_PLAYING;
  1064         }
  1065     }
  1066     return (status);
  1067 }
  1068 
  1069 
  1070 SDL_audiostatus
  1071 SDL_GetAudioStatus(void)
  1072 {
  1073     return SDL_GetAudioDeviceStatus(1);
  1074 }
  1075 
  1076 void
  1077 SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
  1078 {
  1079     SDL_AudioDevice *device = get_audio_device(devid);
  1080     if (device) {
  1081         device->paused = pause_on;
  1082     }
  1083 }
  1084 
  1085 void
  1086 SDL_PauseAudio(int pause_on)
  1087 {
  1088     SDL_PauseAudioDevice(1, pause_on);
  1089 }
  1090 
  1091 
  1092 void
  1093 SDL_LockAudioDevice(SDL_AudioDeviceID devid)
  1094 {
  1095     /* Obtain a lock on the mixing buffers */
  1096     SDL_AudioDevice *device = get_audio_device(devid);
  1097     if (device) {
  1098         current_audio.impl.LockDevice(device);
  1099     }
  1100 }
  1101 
  1102 void
  1103 SDL_LockAudio(void)
  1104 {
  1105     SDL_LockAudioDevice(1);
  1106 }
  1107 
  1108 void
  1109 SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
  1110 {
  1111     /* Obtain a lock on the mixing buffers */
  1112     SDL_AudioDevice *device = get_audio_device(devid);
  1113     if (device) {
  1114         current_audio.impl.UnlockDevice(device);
  1115     }
  1116 }
  1117 
  1118 void
  1119 SDL_UnlockAudio(void)
  1120 {
  1121     SDL_UnlockAudioDevice(1);
  1122 }
  1123 
  1124 void
  1125 SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
  1126 {
  1127     SDL_AudioDevice *device = get_audio_device(devid);
  1128     if (device) {
  1129         close_audio_device(device);
  1130         open_devices[devid - 1] = NULL;
  1131     }
  1132 }
  1133 
  1134 void
  1135 SDL_CloseAudio(void)
  1136 {
  1137     SDL_CloseAudioDevice(1);
  1138 }
  1139 
  1140 void
  1141 SDL_AudioQuit(void)
  1142 {
  1143     SDL_AudioDeviceID i;
  1144     for (i = 0; i < SDL_arraysize(open_devices); i++) {
  1145         SDL_CloseAudioDevice(i);
  1146     }
  1147 
  1148     /* Free the driver data */
  1149     current_audio.impl.Deinitialize();
  1150     SDL_memset(&current_audio, '\0', sizeof(current_audio));
  1151     SDL_memset(open_devices, '\0', sizeof(open_devices));
  1152 }
  1153 
  1154 #define NUM_FORMATS 10
  1155 static int format_idx;
  1156 static int format_idx_sub;
  1157 static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
  1158     {AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
  1159      AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
  1160     {AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
  1161      AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
  1162     {AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB,
  1163      AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
  1164     {AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB,
  1165      AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
  1166     {AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB,
  1167      AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
  1168     {AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB,
  1169      AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
  1170     {AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB,
  1171      AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
  1172     {AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB,
  1173      AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
  1174     {AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB,
  1175      AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
  1176     {AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB,
  1177      AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
  1178 };
  1179 
  1180 SDL_AudioFormat
  1181 SDL_FirstAudioFormat(SDL_AudioFormat format)
  1182 {
  1183     for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
  1184         if (format_list[format_idx][0] == format) {
  1185             break;
  1186         }
  1187     }
  1188     format_idx_sub = 0;
  1189     return (SDL_NextAudioFormat());
  1190 }
  1191 
  1192 SDL_AudioFormat
  1193 SDL_NextAudioFormat(void)
  1194 {
  1195     if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
  1196         return (0);
  1197     }
  1198     return (format_list[format_idx][format_idx_sub++]);
  1199 }
  1200 
  1201 void
  1202 SDL_CalculateAudioSpec(SDL_AudioSpec * spec)
  1203 {
  1204     switch (spec->format) {
  1205     case AUDIO_U8:
  1206         spec->silence = 0x80;
  1207         break;
  1208     default:
  1209         spec->silence = 0x00;
  1210         break;
  1211     }
  1212     spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
  1213     spec->size *= spec->channels;
  1214     spec->size *= spec->samples;
  1215 }
  1216 
  1217 
  1218 /*
  1219  * Moved here from SDL_mixer.c, since it relies on internals of an opened
  1220  *  audio device (and is deprecated, by the way!).
  1221  */
  1222 void
  1223 SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
  1224 {
  1225     /* Mix the user-level audio format */
  1226     SDL_AudioDevice *device = get_audio_device(1);
  1227     if (device != NULL) {
  1228         SDL_AudioFormat format;
  1229         if (device->convert.needed) {
  1230             format = device->convert.src_format;
  1231         } else {
  1232             format = device->spec.format;
  1233         }
  1234         SDL_MixAudioFormat(dst, src, format, len, volume);
  1235     }
  1236 }
  1237 
  1238 /* vi: set ts=4 sw=4 expandtab: */