src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Jan 2017 20:27:48 -0500
changeset 10834 336efe4fc373
parent 10833 86f6353f1aae
child 10835 0e9e7a128391
permissions -rw-r--r--
audio: Special case for resampling stereo AUDIO_S16SYS audio data.

This is a fairly common case, so we avoid the conversion to/from float here.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 /* Functions for audio drivers to perform runtime conversion of audio format */
    24 
    25 #include "SDL_audio.h"
    26 #include "SDL_audio_c.h"
    27 
    28 #include "SDL_loadso.h"
    29 #include "SDL_assert.h"
    30 #include "../SDL_dataqueue.h"
    31 
    32 /* !!! FIXME: wire this up to the configure script, etc. */
    33 #include "SDL_cpuinfo.h"
    34 #define HAVE_SSE3_INTRINSICS 0
    35 
    36 #if HAVE_SSE3_INTRINSICS
    37 #include <pmmintrin.h>
    38 #endif
    39 
    40 #if HAVE_SSE3_INTRINSICS
    41 /* Effectively mix right and left channels into a single channel */
    42 static void SDLCALL
    43 SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    44 {
    45     float *dst = (float *) cvt->buf;
    46     const float *src = dst;
    47     int i = cvt->len_cvt / 8;
    48 
    49     LOG_DEBUG_CONVERT("stereo", "mono (using SSE3)");
    50     SDL_assert(format == AUDIO_F32SYS);
    51 
    52     /* We can only do this if dst is aligned to 16 bytes; since src is the
    53        same pointer and it moves by 2, it can't be forcibly aligned. */
    54     if ((((size_t) dst) & 15) == 0) {
    55         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
    56         const __m128 divby2 = _mm_set1_ps(0.5f);
    57         while (i >= 4) {   /* 4 * float32 */
    58             _mm_store_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_load_ps(src), _mm_load_ps(src+4)), divby2));
    59             i -= 4; src += 8; dst += 4;
    60         }
    61     }
    62 
    63     /* Finish off any leftovers with scalar operations. */
    64     while (i) {
    65         *dst = (src[0] + src[1]) * 0.5f;
    66         dst++; i--; src += 2;
    67     }
    68 
    69     cvt->len_cvt /= 2;
    70     if (cvt->filters[++cvt->filter_index]) {
    71         cvt->filters[cvt->filter_index] (cvt, format);
    72     }
    73 }
    74 #endif
    75 
    76 /* Effectively mix right and left channels into a single channel */
    77 static void SDLCALL
    78 SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    79 {
    80     float *dst = (float *) cvt->buf;
    81     const float *src = dst;
    82     int i;
    83 
    84     LOG_DEBUG_CONVERT("stereo", "mono");
    85     SDL_assert(format == AUDIO_F32SYS);
    86 
    87     for (i = cvt->len_cvt / 8; i; --i, src += 2) {
    88         *(dst++) = (src[0] + src[1]) * 0.5f;
    89     }
    90 
    91     cvt->len_cvt /= 2;
    92     if (cvt->filters[++cvt->filter_index]) {
    93         cvt->filters[cvt->filter_index] (cvt, format);
    94     }
    95 }
    96 
    97 
    98 /* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
    99 static void SDLCALL
   100 SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   101 {
   102     float *dst = (float *) cvt->buf;
   103     const float *src = dst;
   104     int i;
   105 
   106     LOG_DEBUG_CONVERT("5.1", "stereo");
   107     SDL_assert(format == AUDIO_F32SYS);
   108 
   109     /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
   110     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
   111         const double front_center = (double) src[2];
   112         dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0);  /* left */
   113         dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0);  /* right */
   114     }
   115 
   116     cvt->len_cvt /= 3;
   117     if (cvt->filters[++cvt->filter_index]) {
   118         cvt->filters[cvt->filter_index] (cvt, format);
   119     }
   120 }
   121 
   122 
   123 /* Convert from 5.1 to quad */
   124 static void SDLCALL
   125 SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   126 {
   127     float *dst = (float *) cvt->buf;
   128     const float *src = dst;
   129     int i;
   130 
   131     LOG_DEBUG_CONVERT("5.1", "quad");
   132     SDL_assert(format == AUDIO_F32SYS);
   133 
   134     /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
   135     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
   136         /* FIXME: this is a good candidate for SIMD. */
   137         const double front_center = (double) src[2];
   138         dst[0] = (float) ((src[0] + front_center) * 0.5);  /* FL */
   139         dst[1] = (float) ((src[1] + front_center) * 0.5);  /* FR */
   140         dst[2] = (float) ((src[4] + front_center) * 0.5);  /* BL */
   141         dst[3] = (float) ((src[5] + front_center) * 0.5);  /* BR */
   142     }
   143 
   144     cvt->len_cvt /= 6;
   145     cvt->len_cvt *= 4;
   146     if (cvt->filters[++cvt->filter_index]) {
   147         cvt->filters[cvt->filter_index] (cvt, format);
   148     }
   149 }
   150 
   151 
   152 /* Duplicate a mono channel to both stereo channels */
   153 static void SDLCALL
   154 SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   155 {
   156     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   157     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   158     int i;
   159 
   160     LOG_DEBUG_CONVERT("mono", "stereo");
   161     SDL_assert(format == AUDIO_F32SYS);
   162 
   163     for (i = cvt->len_cvt / sizeof (float); i; --i) {
   164         src--;
   165         dst -= 2;
   166         dst[0] = dst[1] = *src;
   167     }
   168 
   169     cvt->len_cvt *= 2;
   170     if (cvt->filters[++cvt->filter_index]) {
   171         cvt->filters[cvt->filter_index] (cvt, format);
   172     }
   173 }
   174 
   175 
   176 /* Duplicate a stereo channel to a pseudo-5.1 stream */
   177 static void SDLCALL
   178 SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   179 {
   180     int i;
   181     float lf, rf, ce;
   182     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   183     float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
   184 
   185     LOG_DEBUG_CONVERT("stereo", "5.1");
   186     SDL_assert(format == AUDIO_F32SYS);
   187 
   188     for (i = cvt->len_cvt / 8; i; --i) {
   189         dst -= 6;
   190         src -= 2;
   191         lf = src[0];
   192         rf = src[1];
   193         ce = (lf + rf) * 0.5f;
   194         dst[0] = lf + (lf - ce);  /* FL */
   195         dst[1] = rf + (rf - ce);  /* FR */
   196         dst[2] = ce;  /* FC */
   197         dst[3] = ce;  /* !!! FIXME: wrong! This is the subwoofer. */
   198         dst[4] = lf;  /* BL */
   199         dst[5] = rf;  /* BR */
   200     }
   201 
   202     cvt->len_cvt *= 3;
   203     if (cvt->filters[++cvt->filter_index]) {
   204         cvt->filters[cvt->filter_index] (cvt, format);
   205     }
   206 }
   207 
   208 
   209 /* Duplicate a stereo channel to a pseudo-4.0 stream */
   210 static void SDLCALL
   211 SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   212 {
   213     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   214     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   215     float lf, rf;
   216     int i;
   217 
   218     LOG_DEBUG_CONVERT("stereo", "quad");
   219     SDL_assert(format == AUDIO_F32SYS);
   220 
   221     for (i = cvt->len_cvt / 8; i; --i) {
   222         dst -= 4;
   223         src -= 2;
   224         lf = src[0];
   225         rf = src[1];
   226         dst[0] = lf;  /* FL */
   227         dst[1] = rf;  /* FR */
   228         dst[2] = lf;  /* BL */
   229         dst[3] = rf;  /* BR */
   230     }
   231 
   232     cvt->len_cvt *= 2;
   233     if (cvt->filters[++cvt->filter_index]) {
   234         cvt->filters[cvt->filter_index] (cvt, format);
   235     }
   236 }
   237 
   238 static int
   239 SDL_ResampleAudioSimple(const int chans, const double rate_incr,
   240                         float *last_sample, const float *inbuf,
   241                         const int inbuflen, float *outbuf, const int outbuflen)
   242 {
   243     const int framelen = chans * (int)sizeof (float);
   244     const int total = (inbuflen / framelen);
   245     const int finalpos = (total * chans) - chans;
   246     const int dest_samples = (int)(((double)total) * rate_incr);
   247     const double src_incr = 1.0 / rate_incr;
   248     float *dst;
   249     double idx;
   250     int i;
   251 
   252     SDL_assert((dest_samples * framelen) <= outbuflen);
   253     SDL_assert((inbuflen % framelen) == 0);
   254 
   255     if (rate_incr > 1.0) {
   256         float *target = (outbuf + chans);
   257         const float *earlier_sample = &inbuf[finalpos];
   258         float final_sample[8];
   259         dst = outbuf + (dest_samples * chans);
   260         idx = (double) total;
   261 
   262         /* save this off so we can correctly maintain state between runs. */
   263         SDL_memcpy(final_sample, &inbuf[finalpos], framelen);
   264 
   265         while (dst > target) {
   266             const int pos = ((int) idx) * chans;
   267             const float *src = &inbuf[pos];
   268             SDL_assert(pos >= 0.0);
   269             for (i = chans - 1; i >= 0; i--) {
   270                 const float val = *(--src);
   271                 *(--dst) = (val + earlier_sample[i]) * 0.5f;
   272             }
   273             earlier_sample = src;
   274             idx -= src_incr;
   275         }
   276 
   277         /* do last sample, interpolated against previous run's state. */
   278         for (i = chans - 1; i >= 0; i--) {
   279             const float val = inbuf[i];
   280             *(--dst) = (val + last_sample[i]) * 0.5f;
   281         }
   282         SDL_memcpy(last_sample, final_sample, framelen);
   283         dst = (outbuf + (dest_samples * chans)) - 1;
   284     } else {
   285         float *target = (outbuf + (dest_samples * chans));
   286         dst = outbuf;
   287         idx = 0.0;
   288         while (dst < target) {
   289             const int pos = ((int) idx) * chans;
   290             const float *src = &inbuf[pos];
   291             SDL_assert(pos <= finalpos);
   292             for (i = 0; i < chans; i++) {
   293                 const float val = *(src++);
   294                 *(dst++) = (val + last_sample[i]) * 0.5f;
   295                 last_sample[i] = val;
   296             }
   297             idx += src_incr;
   298         }
   299     }
   300 
   301     return (int) ((dst - outbuf) * ((int) sizeof (float)));
   302 }
   303 
   304 /* We keep one special-case fast path around for an extremely common audio format. */
   305 static int
   306 SDL_ResampleAudioSimple_si16_c2(const double rate_incr,
   307                         Sint16 *last_sample, const Sint16 *inbuf,
   308                         const int inbuflen, Sint16 *outbuf, const int outbuflen)
   309 {
   310     const int chans = 2;
   311     const int framelen = 4;  /* stereo 16 bit */
   312     const int total = (inbuflen / framelen);
   313     const int finalpos = (total * chans) - chans;
   314     const int dest_samples = (int)(((double)total) * rate_incr);
   315     const double src_incr = 1.0 / rate_incr;
   316     Sint16 *dst;
   317     double idx;
   318 
   319     SDL_assert((dest_samples * framelen) <= outbuflen);
   320     SDL_assert((inbuflen % framelen) == 0);
   321 
   322     if (rate_incr > 1.0) {
   323         Sint16 *target = (outbuf + chans);
   324         const Sint16 final_right = inbuf[finalpos+1];
   325         const Sint16 final_left = inbuf[finalpos];
   326         Sint16 earlier_right = inbuf[finalpos-1];
   327         Sint16 earlier_left = inbuf[finalpos-2];
   328         dst = outbuf + (dest_samples * chans);
   329         idx = (double) total;
   330 
   331         while (dst > target) {
   332             const int pos = ((int) idx) * chans;
   333             const Sint16 *src = &inbuf[pos];
   334             const Sint16 right = *(--src);
   335             const Sint16 left = *(--src);
   336             SDL_assert(pos >= 0.0);
   337             *(--dst) = (((Sint32) right) + ((Sint32) earlier_right)) >> 1;
   338             *(--dst) = (((Sint32) left) + ((Sint32) earlier_left)) >> 1;
   339             earlier_right = right;
   340             earlier_left = left;
   341             idx -= src_incr;
   342         }
   343 
   344         /* do last sample, interpolated against previous run's state. */
   345         *(--dst) = (((Sint32) inbuf[1]) + ((Sint32) last_sample[1])) >> 1;
   346         *(--dst) = (((Sint32) inbuf[0]) + ((Sint32) last_sample[0])) >> 1;
   347         last_sample[1] = final_right;
   348         last_sample[0] = final_left;
   349 
   350         dst = (outbuf + (dest_samples * chans)) - 1;
   351     } else {
   352         Sint16 *target = (outbuf + (dest_samples * chans));
   353         dst = outbuf;
   354         idx = 0.0;
   355         while (dst < target) {
   356             const int pos = ((int) idx) * chans;
   357             const Sint16 *src = &inbuf[pos];
   358             const Sint16 left = *(src++);
   359             const Sint16 right = *(src++);
   360             SDL_assert(pos <= finalpos);
   361             *(dst++) = (((Sint32) left) + ((Sint32) last_sample[0])) >> 1;
   362             *(dst++) = (((Sint32) right) + ((Sint32) last_sample[1])) >> 1;
   363             last_sample[0] = left;
   364             last_sample[1] = right;
   365             idx += src_incr;
   366         }
   367     }
   368 
   369     return (int) ((dst - outbuf) * ((int) sizeof (Sint16)));
   370 }
   371 
   372 static void SDLCALL
   373 SDL_ResampleCVT_si16_c2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   374 {
   375     const Sint16 *src = (const Sint16 *) cvt->buf;
   376     const int srclen = cvt->len_cvt;
   377     Sint16 *dst = (Sint16 *) (cvt->buf + srclen);
   378     const int dstlen = (cvt->len * cvt->len_mult) - srclen;
   379     Sint16 state[2] = { src[0], src[1] };
   380 
   381     SDL_assert(format == AUDIO_S16SYS);
   382 
   383     cvt->len_cvt = SDL_ResampleAudioSimple_si16_c2(cvt->rate_incr, state, src, srclen, dst, dstlen);
   384     if (cvt->filters[++cvt->filter_index]) {
   385         cvt->filters[cvt->filter_index](cvt, format);
   386     }
   387 }
   388 
   389 
   390 int
   391 SDL_ConvertAudio(SDL_AudioCVT * cvt)
   392 {
   393     /* !!! FIXME: (cvt) should be const; stack-copy it here. */
   394     /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
   395 
   396     /* Make sure there's data to convert */
   397     if (cvt->buf == NULL) {
   398         return SDL_SetError("No buffer allocated for conversion");
   399     }
   400 
   401     /* Return okay if no conversion is necessary */
   402     cvt->len_cvt = cvt->len;
   403     if (cvt->filters[0] == NULL) {
   404         return 0;
   405     }
   406 
   407     /* Set up the conversion and go! */
   408     cvt->filter_index = 0;
   409     cvt->filters[0] (cvt, cvt->src_format);
   410     return 0;
   411 }
   412 
   413 static void SDLCALL
   414 SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   415 {
   416 #if DEBUG_CONVERT
   417     printf("Converting byte order\n");
   418 #endif
   419 
   420     switch (SDL_AUDIO_BITSIZE(format)) {
   421         #define CASESWAP(b) \
   422             case b: { \
   423                 Uint##b *ptr = (Uint##b *) cvt->buf; \
   424                 int i; \
   425                 for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
   426                     *ptr = SDL_Swap##b(*ptr); \
   427                 } \
   428                 break; \
   429             }
   430 
   431         CASESWAP(16);
   432         CASESWAP(32);
   433         CASESWAP(64);
   434 
   435         #undef CASESWAP
   436 
   437         default: SDL_assert(!"unhandled byteswap datatype!"); break;
   438     }
   439 
   440     if (cvt->filters[++cvt->filter_index]) {
   441         /* flip endian flag for data. */
   442         if (format & SDL_AUDIO_MASK_ENDIAN) {
   443             format &= ~SDL_AUDIO_MASK_ENDIAN;
   444         } else {
   445             format |= SDL_AUDIO_MASK_ENDIAN;
   446         }
   447         cvt->filters[cvt->filter_index](cvt, format);
   448     }
   449 }
   450 
   451 
   452 static int
   453 SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
   454 {
   455     int retval = 0;  /* 0 == no conversion necessary. */
   456 
   457     if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   458         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   459         retval = 1;  /* added a converter. */
   460     }
   461 
   462     if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
   463         const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
   464         const Uint16 dst_bitsize = 32;
   465         SDL_AudioFilter filter = NULL;
   466 
   467         switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   468             case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
   469             case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
   470             case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
   471             case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
   472             case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
   473             default: SDL_assert(!"Unexpected audio format!"); break;
   474         }
   475 
   476         if (!filter) {
   477             return SDL_SetError("No conversion available for these formats");
   478         }
   479 
   480         cvt->filters[cvt->filter_index++] = filter;
   481         if (src_bitsize < dst_bitsize) {
   482             const int mult = (dst_bitsize / src_bitsize);
   483             cvt->len_mult *= mult;
   484             cvt->len_ratio *= mult;
   485         } else if (src_bitsize > dst_bitsize) {
   486             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   487         }
   488 
   489         retval = 1;  /* added a converter. */
   490     }
   491 
   492     return retval;
   493 }
   494 
   495 static int
   496 SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
   497 {
   498     int retval = 0;  /* 0 == no conversion necessary. */
   499 
   500     if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
   501         const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
   502         const Uint16 src_bitsize = 32;
   503         SDL_AudioFilter filter = NULL;
   504         switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   505             case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
   506             case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
   507             case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
   508             case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
   509             case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
   510             default: SDL_assert(!"Unexpected audio format!"); break;
   511         }
   512 
   513         if (!filter) {
   514             return SDL_SetError("No conversion available for these formats");
   515         }
   516 
   517         cvt->filters[cvt->filter_index++] = filter;
   518         if (src_bitsize < dst_bitsize) {
   519             const int mult = (dst_bitsize / src_bitsize);
   520             cvt->len_mult *= mult;
   521             cvt->len_ratio *= mult;
   522         } else if (src_bitsize > dst_bitsize) {
   523             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   524         }
   525         retval = 1;  /* added a converter. */
   526     }
   527 
   528     if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   529         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   530         retval = 1;  /* added a converter. */
   531     }
   532 
   533     return retval;
   534 }
   535 
   536 static void
   537 SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
   538 {
   539     const float *src = (const float *) cvt->buf;
   540     const int srclen = cvt->len_cvt;
   541     float *dst = (float *) cvt->buf;
   542     const int dstlen = (cvt->len * cvt->len_mult);
   543     float state[8];
   544 
   545     SDL_assert(format == AUDIO_F32SYS);
   546 
   547     SDL_memcpy(state, src, chans*sizeof(*src));
   548 
   549     cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
   550     if (cvt->filters[++cvt->filter_index]) {
   551         cvt->filters[cvt->filter_index](cvt, format);
   552     }
   553 }
   554 
   555 /* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
   556    !!! FIXME:  store channel info, so we have to have function entry
   557    !!! FIXME:  points for each supported channel count and multiple
   558    !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
   559 #define RESAMPLER_FUNCS(chans) \
   560     static void SDLCALL \
   561     SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   562         SDL_ResampleCVT(cvt, chans, format); \
   563     }
   564 RESAMPLER_FUNCS(1)
   565 RESAMPLER_FUNCS(2)
   566 RESAMPLER_FUNCS(4)
   567 RESAMPLER_FUNCS(6)
   568 RESAMPLER_FUNCS(8)
   569 #undef RESAMPLER_FUNCS
   570 
   571 static SDL_AudioFilter
   572 ChooseCVTResampler(const int dst_channels)
   573 {
   574     switch (dst_channels) {
   575         case 1: return SDL_ResampleCVT_c1;
   576         case 2: return SDL_ResampleCVT_c2;
   577         case 4: return SDL_ResampleCVT_c4;
   578         case 6: return SDL_ResampleCVT_c6;
   579         case 8: return SDL_ResampleCVT_c8;
   580         default: break;
   581     }
   582 
   583     return NULL;
   584 }
   585 
   586 static int
   587 SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
   588                           const int src_rate, const int dst_rate)
   589 {
   590     SDL_AudioFilter filter;
   591 
   592     if (src_rate == dst_rate) {
   593         return 0;  /* no conversion necessary. */
   594     }
   595 
   596     filter = ChooseCVTResampler(dst_channels);
   597     if (filter == NULL) {
   598         return SDL_SetError("No conversion available for these rates");
   599     }
   600 
   601     /* Update (cvt) with filter details... */
   602     cvt->filters[cvt->filter_index++] = filter;
   603     if (src_rate < dst_rate) {
   604         const double mult = ((double) dst_rate) / ((double) src_rate);
   605         cvt->len_mult *= (int) SDL_ceil(mult);
   606         cvt->len_ratio *= mult;
   607     } else {
   608         cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   609     }
   610 
   611     return 1;               /* added a converter. */
   612 }
   613 
   614 
   615 /* Creates a set of audio filters to convert from one format to another.
   616    Returns -1 if the format conversion is not supported, 0 if there's
   617    no conversion needed, or 1 if the audio filter is set up.
   618 */
   619 
   620 int
   621 SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
   622                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
   623                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
   624 {
   625     /* Sanity check target pointer */
   626     if (cvt == NULL) {
   627         return SDL_InvalidParamError("cvt");
   628     }
   629 
   630     /* Make sure we zero out the audio conversion before error checking */
   631     SDL_zerop(cvt);
   632 
   633     /* there are no unsigned types over 16 bits, so catch this up front. */
   634     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
   635         return SDL_SetError("Invalid source format");
   636     }
   637     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
   638         return SDL_SetError("Invalid destination format");
   639     }
   640 
   641     /* prevent possible divisions by zero, etc. */
   642     if ((src_channels == 0) || (dst_channels == 0)) {
   643         return SDL_SetError("Source or destination channels is zero");
   644     }
   645     if ((src_rate == 0) || (dst_rate == 0)) {
   646         return SDL_SetError("Source or destination rate is zero");
   647     }
   648 #if DEBUG_CONVERT
   649     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
   650            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
   651 #endif
   652 
   653     /* Start off with no conversion necessary */
   654     cvt->src_format = src_fmt;
   655     cvt->dst_format = dst_fmt;
   656     cvt->needed = 0;
   657     cvt->filter_index = 0;
   658     cvt->filters[0] = NULL;
   659     cvt->len_mult = 1;
   660     cvt->len_ratio = 1.0;
   661     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   662 
   663     /* SDL now favors float32 as its preferred internal format, and considers
   664        everything else to be a degenerate case that we might have to make
   665        multiple passes over the data to convert to and from float32 as
   666        necessary. That being said, we keep one special case around for
   667        efficiency: stereo data in Sint16 format, in the native byte order,
   668        that only needs resampling. This is likely to be the most popular
   669        legacy format, that apps, hardware and the OS are likely to be able
   670        to process directly, so we handle this one case directly without
   671        unnecessary conversions. This means that apps on embedded devices
   672        without floating point hardware should consider aiming for this
   673        format as well. */
   674     if ((src_channels == 2) && (dst_channels == 2) && (src_fmt == AUDIO_S16SYS) && (dst_fmt == AUDIO_S16SYS) && (src_rate != dst_rate)) {
   675         cvt->needed = 1;
   676         cvt->filters[cvt->filter_index++] = SDL_ResampleCVT_si16_c2;
   677         if (src_rate < dst_rate) {
   678             const double mult = ((double) dst_rate) / ((double) src_rate);
   679             cvt->len_mult *= (int) SDL_ceil(mult);
   680             cvt->len_ratio *= mult;
   681         } else {
   682             cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   683         }
   684         return 1;
   685     }
   686 
   687     /* Type conversion goes like this now:
   688         - byteswap to CPU native format first if necessary.
   689         - convert to native Float32 if necessary.
   690         - resample and change channel count if necessary.
   691         - convert back to native format.
   692         - byteswap back to foreign format if necessary.
   693 
   694        The expectation is we can process data faster in float32
   695        (possibly with SIMD), and making several passes over the same
   696        buffer is likely to be CPU cache-friendly, avoiding the
   697        biggest performance hit in modern times. Previously we had
   698        (script-generated) custom converters for every data type and
   699        it was a bloat on SDL compile times and final library size. */
   700 
   701     /* see if we can skip float conversion entirely. */
   702     if (src_rate == dst_rate && src_channels == dst_channels) {
   703         if (src_fmt == dst_fmt) {
   704             return 0;
   705         }
   706 
   707         /* just a byteswap needed? */
   708         if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
   709             cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   710             cvt->needed = 1;
   711             return 1;
   712         }
   713     }
   714 
   715     /* Convert data types, if necessary. Updates (cvt). */
   716     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
   717         return -1;              /* shouldn't happen, but just in case... */
   718     }
   719 
   720     /* Channel conversion */
   721     if (src_channels != dst_channels) {
   722         if ((src_channels == 1) && (dst_channels > 1)) {
   723             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   724             cvt->len_mult *= 2;
   725             src_channels = 2;
   726             cvt->len_ratio *= 2;
   727         }
   728         if ((src_channels == 2) && (dst_channels == 6)) {
   729             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
   730             src_channels = 6;
   731             cvt->len_mult *= 3;
   732             cvt->len_ratio *= 3;
   733         }
   734         if ((src_channels == 2) && (dst_channels == 4)) {
   735             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
   736             src_channels = 4;
   737             cvt->len_mult *= 2;
   738             cvt->len_ratio *= 2;
   739         }
   740         while ((src_channels * 2) <= dst_channels) {
   741             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   742             cvt->len_mult *= 2;
   743             src_channels *= 2;
   744             cvt->len_ratio *= 2;
   745         }
   746         if ((src_channels == 6) && (dst_channels <= 2)) {
   747             cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
   748             src_channels = 2;
   749             cvt->len_ratio /= 3;
   750         }
   751         if ((src_channels == 6) && (dst_channels == 4)) {
   752             cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
   753             src_channels = 4;
   754             cvt->len_ratio /= 2;
   755         }
   756         /* This assumes that 4 channel audio is in the format:
   757            Left {front/back} + Right {front/back}
   758            so converting to L/R stereo works properly.
   759          */
   760         while (((src_channels % 2) == 0) &&
   761                ((src_channels / 2) >= dst_channels)) {
   762             SDL_AudioFilter filter = NULL;
   763 
   764             #if HAVE_SSE3_INTRINSICS
   765             if (SDL_HasSSE3()) {
   766                 filter = SDL_ConvertStereoToMono_SSE3;
   767             }
   768             #endif
   769 
   770             if (!filter) {
   771                 filter = SDL_ConvertStereoToMono;
   772             }
   773 
   774             cvt->filters[cvt->filter_index++] = filter;
   775 
   776             src_channels /= 2;
   777             cvt->len_ratio /= 2;
   778         }
   779         if (src_channels != dst_channels) {
   780             /* Uh oh.. */ ;
   781         }
   782     }
   783 
   784     /* Do rate conversion, if necessary. Updates (cvt). */
   785     if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
   786         return -1;              /* shouldn't happen, but just in case... */
   787     }
   788 
   789     /* Move to final data type. */
   790     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
   791         return -1;              /* shouldn't happen, but just in case... */
   792     }
   793 
   794     cvt->needed = (cvt->filter_index != 0);
   795     return (cvt->needed);
   796 }
   797 
   798 typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
   799 typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   800 typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   801 
   802 struct SDL_AudioStream
   803 {
   804     SDL_AudioCVT cvt_before_resampling;
   805     SDL_AudioCVT cvt_after_resampling;
   806     SDL_DataQueue *queue;
   807     Uint8 *work_buffer;
   808     int work_buffer_len;
   809     Uint8 *resample_buffer;
   810     int resample_buffer_len;
   811     int src_sample_frame_size;
   812     SDL_AudioFormat src_format;
   813     Uint8 src_channels;
   814     int src_rate;
   815     int dst_sample_frame_size;
   816     SDL_AudioFormat dst_format;
   817     Uint8 dst_channels;
   818     int dst_rate;
   819     double rate_incr;
   820     Uint8 pre_resample_channels;
   821     int packetlen;
   822     void *resampler_state;
   823     SDL_ResampleAudioStreamFunc resampler_func;
   824     SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
   825     SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
   826 };
   827 
   828 #ifdef HAVE_LIBSAMPLERATE_H
   829 static int
   830 SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   831 {
   832     const int framelen = sizeof(float) * stream->pre_resample_channels;
   833     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   834     SRC_DATA data;
   835     int result;
   836 
   837     data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
   838     data.input_frames = inbuflen / framelen;
   839     data.input_frames_used = 0;
   840 
   841     data.data_out = outbuf;
   842     data.output_frames = outbuflen / framelen;
   843 
   844     data.end_of_input = 0;
   845     data.src_ratio = stream->rate_incr;
   846 
   847     result = SRC_src_process(state, &data);
   848     if (result != 0) {
   849         SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
   850         return 0;
   851     }
   852 
   853     /* If this fails, we need to store them off somewhere */
   854     SDL_assert(data.input_frames_used == data.input_frames);
   855 
   856     return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
   857 }
   858 
   859 static void
   860 SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
   861 {
   862     SRC_src_reset((SRC_STATE *)stream->resampler_state);
   863 }
   864 
   865 static void
   866 SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
   867 {
   868     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   869     if (state) {
   870         SRC_src_delete(state);
   871     }
   872 
   873     stream->resampler_state = NULL;
   874     stream->resampler_func = NULL;
   875     stream->reset_resampler_func = NULL;
   876     stream->cleanup_resampler_func = NULL;
   877 }
   878 
   879 static SDL_bool
   880 SetupLibSampleRateResampling(SDL_AudioStream *stream)
   881 {
   882     int result = 0;
   883     SRC_STATE *state = NULL;
   884 
   885     if (SRC_available) {
   886         state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
   887         if (!state) {
   888             SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
   889         }
   890     }
   891 
   892     if (!state) {
   893         SDL_CleanupAudioStreamResampler_SRC(stream);
   894         return SDL_FALSE;
   895     }
   896 
   897     stream->resampler_state = state;
   898     stream->resampler_func = SDL_ResampleAudioStream_SRC;
   899     stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
   900     stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
   901 
   902     return SDL_TRUE;
   903 }
   904 #endif /* HAVE_LIBSAMPLERATE_H */
   905 
   906 
   907 typedef struct
   908 {
   909     SDL_bool resampler_seeded;
   910     float resampler_state[8];
   911 } SDL_AudioStreamResamplerState;
   912 
   913 static int
   914 SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   915 {
   916     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   917     const int chans = (int)stream->pre_resample_channels;
   918 
   919     SDL_assert(chans <= SDL_arraysize(state->resampler_state));
   920 
   921     if (!state->resampler_seeded) {
   922         int i;
   923         for (i = 0; i < chans; i++) {
   924             state->resampler_state[i] = inbuf[i];
   925         }
   926         state->resampler_seeded = SDL_TRUE;
   927     }
   928 
   929     return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
   930 }
   931 
   932 static void
   933 SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
   934 {
   935     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   936     state->resampler_seeded = SDL_FALSE;
   937 }
   938 
   939 static void
   940 SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
   941 {
   942     SDL_free(stream->resampler_state);
   943 }
   944 
   945 SDL_AudioStream *
   946 SDL_NewAudioStream(const SDL_AudioFormat src_format,
   947                    const Uint8 src_channels,
   948                    const int src_rate,
   949                    const SDL_AudioFormat dst_format,
   950                    const Uint8 dst_channels,
   951                    const int dst_rate)
   952 {
   953     const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
   954     Uint8 pre_resample_channels;
   955     SDL_AudioStream *retval;
   956 
   957     retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
   958     if (!retval) {
   959         return NULL;
   960     }
   961 
   962     /* If increasing channels, do it after resampling, since we'd just
   963        do more work to resample duplicate channels. If we're decreasing, do
   964        it first so we resample the interpolated data instead of interpolating
   965        the resampled data (!!! FIXME: decide if that works in practice, though!). */
   966     pre_resample_channels = SDL_min(src_channels, dst_channels);
   967 
   968     retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
   969     retval->src_format = src_format;
   970     retval->src_channels = src_channels;
   971     retval->src_rate = src_rate;
   972     retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
   973     retval->dst_format = dst_format;
   974     retval->dst_channels = dst_channels;
   975     retval->dst_rate = dst_rate;
   976     retval->pre_resample_channels = pre_resample_channels;
   977     retval->packetlen = packetlen;
   978     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
   979 
   980     /* Not resampling? It's an easy conversion (and maybe not even that!). */
   981     if (src_rate == dst_rate) {
   982         retval->cvt_before_resampling.needed = SDL_FALSE;
   983         retval->cvt_before_resampling.len_mult = 1;
   984         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   985             SDL_FreeAudioStream(retval);
   986             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   987         }
   988     } else {
   989         /* Don't resample at first. Just get us to Float32 format. */
   990         /* !!! FIXME: convert to int32 on devices without hardware float. */
   991         if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
   992             SDL_FreeAudioStream(retval);
   993             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   994         }
   995 
   996 #ifdef HAVE_LIBSAMPLERATE_H
   997         SetupLibSampleRateResampling(retval);
   998 #endif
   999 
  1000         if (!retval->resampler_func) {
  1001             retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
  1002             if (!retval->resampler_state) {
  1003                 SDL_FreeAudioStream(retval);
  1004                 SDL_OutOfMemory();
  1005                 return NULL;
  1006             }
  1007             retval->resampler_func = SDL_ResampleAudioStream;
  1008             retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
  1009             retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
  1010         }
  1011 
  1012         /* Convert us to the final format after resampling. */
  1013         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
  1014             SDL_FreeAudioStream(retval);
  1015             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
  1016         }
  1017     }
  1018 
  1019     retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
  1020     if (!retval->queue) {
  1021         SDL_FreeAudioStream(retval);
  1022         return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
  1023     }
  1024 
  1025     return retval;
  1026 }
  1027 
  1028 static Uint8 *
  1029 EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
  1030 {
  1031     if (*len < newlen) {
  1032         void *ptr = SDL_realloc(*buf, newlen);
  1033         if (!ptr) {
  1034             SDL_OutOfMemory();
  1035             return NULL;
  1036         }
  1037         *buf = (Uint8 *) ptr;
  1038         *len = newlen;
  1039     }
  1040     return *buf;
  1041 }
  1042 
  1043 int
  1044 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
  1045 {
  1046     int buflen = (int) _buflen;
  1047 
  1048     if (!stream) {
  1049         return SDL_InvalidParamError("stream");
  1050     } else if (!buf) {
  1051         return SDL_InvalidParamError("buf");
  1052     } else if (buflen == 0) {
  1053         return 0;  /* nothing to do. */
  1054     } else if ((buflen % stream->src_sample_frame_size) != 0) {
  1055         return SDL_SetError("Can't add partial sample frames");
  1056     }
  1057 
  1058     if (stream->cvt_before_resampling.needed) {
  1059         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
  1060         Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
  1061         if (workbuf == NULL) {
  1062             return -1;  /* probably out of memory. */
  1063         }
  1064         SDL_memcpy(workbuf, buf, buflen);
  1065         stream->cvt_before_resampling.buf = workbuf;
  1066         stream->cvt_before_resampling.len = buflen;
  1067         if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
  1068             return -1;   /* uhoh! */
  1069         }
  1070         buf = workbuf;
  1071         buflen = stream->cvt_before_resampling.len_cvt;
  1072     }
  1073 
  1074     if (stream->dst_rate != stream->src_rate) {
  1075         const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
  1076         float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
  1077         if (workbuf == NULL) {
  1078             return -1;  /* probably out of memory. */
  1079         }
  1080         buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
  1081         buf = workbuf;
  1082     }
  1083 
  1084     if (stream->cvt_after_resampling.needed) {
  1085         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
  1086         Uint8 *workbuf;
  1087 
  1088         if (buf == stream->resample_buffer) {
  1089             workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
  1090         } else {
  1091             const int inplace = (buf == stream->work_buffer);
  1092             workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
  1093             if (workbuf && !inplace) {
  1094                 SDL_memcpy(workbuf, buf, buflen);
  1095             }
  1096         }
  1097 
  1098         if (workbuf == NULL) {
  1099             return -1;  /* probably out of memory. */
  1100         }
  1101 
  1102         stream->cvt_after_resampling.buf = workbuf;
  1103         stream->cvt_after_resampling.len = buflen;
  1104         if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
  1105             return -1;   /* uhoh! */
  1106         }
  1107         buf = workbuf;
  1108         buflen = stream->cvt_after_resampling.len_cvt;
  1109     }
  1110 
  1111     return SDL_WriteToDataQueue(stream->queue, buf, buflen);
  1112 }
  1113 
  1114 void
  1115 SDL_AudioStreamClear(SDL_AudioStream *stream)
  1116 {
  1117     if (!stream) {
  1118         SDL_InvalidParamError("stream");
  1119     } else {
  1120         SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
  1121         if (stream->reset_resampler_func) {
  1122             stream->reset_resampler_func(stream);
  1123         }
  1124     }
  1125 }
  1126 
  1127 
  1128 /* get converted/resampled data from the stream */
  1129 int
  1130 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
  1131 {
  1132     if (!stream) {
  1133         return SDL_InvalidParamError("stream");
  1134     } else if (!buf) {
  1135         return SDL_InvalidParamError("buf");
  1136     } else if (len == 0) {
  1137         return 0;  /* nothing to do. */
  1138     } else if ((len % stream->dst_sample_frame_size) != 0) {
  1139         return SDL_SetError("Can't request partial sample frames");
  1140     }
  1141 
  1142     return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
  1143 }
  1144 
  1145 /* number of converted/resampled bytes available */
  1146 int
  1147 SDL_AudioStreamAvailable(SDL_AudioStream *stream)
  1148 {
  1149     return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
  1150 }
  1151 
  1152 /* dispose of a stream */
  1153 void
  1154 SDL_FreeAudioStream(SDL_AudioStream *stream)
  1155 {
  1156     if (stream) {
  1157         if (stream->cleanup_resampler_func) {
  1158             stream->cleanup_resampler_func(stream);
  1159         }
  1160         SDL_FreeDataQueue(stream->queue);
  1161         SDL_free(stream->work_buffer);
  1162         SDL_free(stream->resample_buffer);
  1163         SDL_free(stream);
  1164     }
  1165 }
  1166 
  1167 /* vi: set ts=4 sw=4 expandtab: */
  1168