src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 23 Jan 2017 16:42:47 -0500
changeset 10839 64f7fccca56e
parent 10835 0e9e7a128391
child 10840 3c3708a0b217
permissions -rw-r--r--
audio: Fixed incorrect pointer in SDL_ResampleCVT_si16_c2().

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