src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 06 Jan 2017 01:02:58 -0500
changeset 10764 f9bf759e9dd1
parent 10759 d698fdebebfe
child 10767 b6046389b839
permissions -rw-r--r--
audio: Fixed SDL_AudioStreamGet() function parameters.

There was a draft of this where it did audio conversion into the final buffer,
if there was enough room available past what you asked for, but that interface
got removed, so the parameters didn't make sense (and we were using the
wrong one in any case, too!).
     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_assert.h"
    29 #include "../SDL_dataqueue.h"
    30 
    31 
    32 /* Effectively mix right and left channels into a single channel */
    33 static void SDLCALL
    34 SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    35 {
    36     float *dst = (float *) cvt->buf;
    37     const float *src = dst;
    38     int i;
    39 
    40     LOG_DEBUG_CONVERT("stereo", "mono");
    41     SDL_assert(format == AUDIO_F32SYS);
    42 
    43     for (i = cvt->len_cvt / 8; i; --i, src += 2) {
    44         *(dst++) = (float) ((((double) src[0]) + ((double) src[1])) * 0.5);
    45     }
    46 
    47     cvt->len_cvt /= 2;
    48     if (cvt->filters[++cvt->filter_index]) {
    49         cvt->filters[cvt->filter_index] (cvt, format);
    50     }
    51 }
    52 
    53 
    54 /* Discard top 4 channels */
    55 static void SDLCALL
    56 SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    57 {
    58     float *dst = (float *) cvt->buf;
    59     const float *src = dst;
    60     int i;
    61 
    62     LOG_DEBUG_CONVERT("6 channels", "stereo");
    63     SDL_assert(format == AUDIO_F32SYS);
    64 
    65     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
    66         dst[0] = src[0];
    67         dst[1] = src[1];
    68     }
    69 
    70     cvt->len_cvt /= 3;
    71     if (cvt->filters[++cvt->filter_index]) {
    72         cvt->filters[cvt->filter_index] (cvt, format);
    73     }
    74 }
    75 
    76 
    77 /* Discard top 2 channels of 6 */
    78 static void SDLCALL
    79 SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    80 {
    81     float *dst = (float *) cvt->buf;
    82     const float *src = dst;
    83     int i;
    84 
    85     LOG_DEBUG_CONVERT("6 channels", "quad");
    86     SDL_assert(format == AUDIO_F32SYS);
    87 
    88     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
    89         dst[0] = src[0];
    90         dst[1] = src[1];
    91         dst[2] = src[2];
    92         dst[3] = src[3];
    93     }
    94 
    95     cvt->len_cvt /= 6;
    96     cvt->len_cvt *= 4;
    97     if (cvt->filters[++cvt->filter_index]) {
    98         cvt->filters[cvt->filter_index] (cvt, format);
    99     }
   100 }
   101 
   102 /* Duplicate a mono channel to both stereo channels */
   103 static void SDLCALL
   104 SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   105 {
   106     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   107     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   108     int i;
   109 
   110     LOG_DEBUG_CONVERT("mono", "stereo");
   111     SDL_assert(format == AUDIO_F32SYS);
   112 
   113     for (i = cvt->len_cvt / sizeof (float); i; --i) {
   114         src--;
   115         dst -= 2;
   116         dst[0] = dst[1] = *src;
   117     }
   118 
   119     cvt->len_cvt *= 2;
   120     if (cvt->filters[++cvt->filter_index]) {
   121         cvt->filters[cvt->filter_index] (cvt, format);
   122     }
   123 }
   124 
   125 
   126 /* Duplicate a stereo channel to a pseudo-5.1 stream */
   127 static void SDLCALL
   128 SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   129 {
   130     int i;
   131     float lf, rf, ce;
   132     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   133     float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
   134 
   135     LOG_DEBUG_CONVERT("stereo", "5.1");
   136     SDL_assert(format == AUDIO_F32SYS);
   137 
   138     for (i = cvt->len_cvt / 8; i; --i) {
   139         dst -= 6;
   140         src -= 2;
   141         lf = src[0];
   142         rf = src[1];
   143         ce = (lf * 0.5f) + (rf * 0.5f);
   144         dst[0] = src[0];
   145         dst[1] = src[1];
   146         dst[2] = lf - ce;
   147         dst[3] = rf - ce;
   148         dst[4] = dst[5] = ce;
   149     }
   150 
   151     cvt->len_cvt *= 3;
   152     if (cvt->filters[++cvt->filter_index]) {
   153         cvt->filters[cvt->filter_index] (cvt, format);
   154     }
   155 }
   156 
   157 
   158 /* Duplicate a stereo channel to a pseudo-4.0 stream */
   159 static void SDLCALL
   160 SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   161 {
   162     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   163     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   164     float lf, rf, ce;
   165     int i;
   166 
   167     LOG_DEBUG_CONVERT("stereo", "quad");
   168     SDL_assert(format == AUDIO_F32SYS);
   169 
   170     for (i = cvt->len_cvt / 8; i; --i) {
   171         dst -= 4;
   172         src -= 2;
   173         lf = src[0];
   174         rf = src[1];
   175         ce = (lf / 2) + (rf / 2);
   176         dst[0] = src[0];
   177         dst[1] = src[1];
   178         dst[2] = lf - ce;
   179         dst[3] = rf - ce;
   180     }
   181 
   182     cvt->len_cvt *= 2;
   183     if (cvt->filters[++cvt->filter_index]) {
   184         cvt->filters[cvt->filter_index] (cvt, format);
   185     }
   186 }
   187 
   188 
   189 int
   190 SDL_ConvertAudio(SDL_AudioCVT * cvt)
   191 {
   192     /* !!! FIXME: (cvt) should be const; stack-copy it here. */
   193     /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
   194 
   195     /* Make sure there's data to convert */
   196     if (cvt->buf == NULL) {
   197         return SDL_SetError("No buffer allocated for conversion");
   198     }
   199 
   200     /* Return okay if no conversion is necessary */
   201     cvt->len_cvt = cvt->len;
   202     if (cvt->filters[0] == NULL) {
   203         return 0;
   204     }
   205 
   206     /* Set up the conversion and go! */
   207     cvt->filter_index = 0;
   208     cvt->filters[0] (cvt, cvt->src_format);
   209     return 0;
   210 }
   211 
   212 static void SDLCALL
   213 SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   214 {
   215 #if DEBUG_CONVERT
   216     printf("Converting byte order\n");
   217 #endif
   218 
   219     switch (SDL_AUDIO_BITSIZE(format)) {
   220         #define CASESWAP(b) \
   221             case b: { \
   222                 Uint##b *ptr = (Uint##b *) cvt->buf; \
   223                 int i; \
   224                 for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
   225                     *ptr = SDL_Swap##b(*ptr); \
   226                 } \
   227                 break; \
   228             }
   229 
   230         CASESWAP(16);
   231         CASESWAP(32);
   232         CASESWAP(64);
   233 
   234         #undef CASESWAP
   235 
   236         default: SDL_assert(!"unhandled byteswap datatype!"); break;
   237     }
   238 
   239     if (cvt->filters[++cvt->filter_index]) {
   240         /* flip endian flag for data. */
   241         if (format & SDL_AUDIO_MASK_ENDIAN) {
   242             format &= ~SDL_AUDIO_MASK_ENDIAN;
   243         } else {
   244             format |= SDL_AUDIO_MASK_ENDIAN;
   245         }
   246         cvt->filters[cvt->filter_index](cvt, format);
   247     }
   248 }
   249 
   250 
   251 static int
   252 SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
   253 {
   254     int retval = 0;  /* 0 == no conversion necessary. */
   255 
   256     if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   257         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   258         retval = 1;  /* added a converter. */
   259     }
   260 
   261     if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
   262         const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
   263         const Uint16 dst_bitsize = 32;
   264         SDL_AudioFilter filter = NULL;
   265 
   266         switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   267             case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
   268             case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
   269             case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
   270             case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
   271             case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
   272             default: SDL_assert(!"Unexpected audio format!"); break;
   273         }
   274 
   275         if (!filter) {
   276             return SDL_SetError("No conversion available for these formats");
   277         }
   278 
   279         cvt->filters[cvt->filter_index++] = filter;
   280         if (src_bitsize < dst_bitsize) {
   281             const int mult = (dst_bitsize / src_bitsize);
   282             cvt->len_mult *= mult;
   283             cvt->len_ratio *= mult;
   284         } else if (src_bitsize > dst_bitsize) {
   285             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   286         }
   287 
   288         retval = 1;  /* added a converter. */
   289     }
   290 
   291     return retval;
   292 }
   293 
   294 static int
   295 SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
   296 {
   297     int retval = 0;  /* 0 == no conversion necessary. */
   298 
   299     if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
   300         const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
   301         const Uint16 src_bitsize = 32;
   302         SDL_AudioFilter filter = NULL;
   303         switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   304             case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
   305             case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
   306             case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
   307             case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
   308             case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
   309             default: SDL_assert(!"Unexpected audio format!"); break;
   310         }
   311 
   312         if (!filter) {
   313             return SDL_SetError("No conversion available for these formats");
   314         }
   315 
   316         cvt->filters[cvt->filter_index++] = filter;
   317         if (src_bitsize < dst_bitsize) {
   318             const int mult = (dst_bitsize / src_bitsize);
   319             cvt->len_mult *= mult;
   320             cvt->len_ratio *= mult;
   321         } else if (src_bitsize > dst_bitsize) {
   322             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   323         }
   324         retval = 1;  /* added a converter. */
   325     }
   326 
   327     if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   328         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   329         retval = 1;  /* added a converter. */
   330     }
   331 
   332     return retval;
   333 }
   334 
   335 
   336 /* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
   337    !!! FIXME:  channel info or integer sample rates, so we have to have
   338    !!! FIXME:  function entry points for each supported channel count and
   339    !!! FIXME:  multiple vs arbitrary. When we rev the ABI, remove this. */
   340 #define RESAMPLER_FUNCS(chans) \
   341     static void SDLCALL \
   342     SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   343         SDL_assert(format == AUDIO_F32SYS); \
   344         SDL_Upsample_Multiple(cvt, chans); \
   345     } \
   346     static void SDLCALL \
   347     SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   348         SDL_assert(format == AUDIO_F32SYS); \
   349         SDL_Upsample_Arbitrary(cvt, chans); \
   350     }\
   351     static void SDLCALL \
   352     SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   353         SDL_assert(format == AUDIO_F32SYS); \
   354         SDL_Downsample_Multiple(cvt, chans); \
   355     } \
   356     static void SDLCALL \
   357     SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   358         SDL_assert(format == AUDIO_F32SYS); \
   359         SDL_Downsample_Arbitrary(cvt, chans); \
   360     }
   361 RESAMPLER_FUNCS(1)
   362 RESAMPLER_FUNCS(2)
   363 RESAMPLER_FUNCS(4)
   364 RESAMPLER_FUNCS(6)
   365 RESAMPLER_FUNCS(8)
   366 #undef RESAMPLER_FUNCS
   367 
   368 static int
   369 SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
   370 {
   371     int lo, hi;
   372 
   373     SDL_assert(src_rate != 0);
   374     SDL_assert(dst_rate != 0);
   375     SDL_assert(src_rate != dst_rate);
   376 
   377     if (src_rate < dst_rate) {
   378         lo = src_rate;
   379         hi = dst_rate;
   380     } else {
   381         lo = dst_rate;
   382         hi = src_rate;
   383     }
   384 
   385     if ((hi % lo) != 0)
   386         return 0;               /* not a multiple. */
   387 
   388     return hi / lo;
   389 }
   390 
   391 static SDL_AudioFilter
   392 ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
   393 {
   394     const int upsample = (src_rate < dst_rate) ? 1 : 0;
   395     const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
   396     SDL_AudioFilter filter = NULL;
   397 
   398     #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
   399         case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
   400         case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
   401         case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
   402         case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
   403         case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
   404         default: break; \
   405     }
   406 
   407     if (upsample) {
   408         if (multiple) {
   409             PICK_CHANNEL_FILTER(Upsample, Multiple);
   410         } else {
   411             PICK_CHANNEL_FILTER(Upsample, Arbitrary);
   412         }
   413     } else {
   414         if (multiple) {
   415             PICK_CHANNEL_FILTER(Downsample, Multiple);
   416         } else {
   417             PICK_CHANNEL_FILTER(Downsample, Arbitrary);
   418         }
   419     }
   420 
   421     #undef PICK_CHANNEL_FILTER
   422 
   423     return filter;
   424 }
   425 
   426 static int
   427 SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
   428                           const int src_rate, const int dst_rate)
   429 {
   430     SDL_AudioFilter filter;
   431 
   432     if (src_rate == dst_rate) {
   433         return 0;  /* no conversion necessary. */
   434     }
   435 
   436     filter = ChooseResampler(dst_channels, src_rate, dst_rate);
   437     if (filter == NULL) {
   438         return SDL_SetError("No conversion available for these rates");
   439     }
   440 
   441     /* Update (cvt) with filter details... */
   442     cvt->filters[cvt->filter_index++] = filter;
   443     if (src_rate < dst_rate) {
   444         const double mult = ((double) dst_rate) / ((double) src_rate);
   445         cvt->len_mult *= (int) SDL_ceil(mult);
   446         cvt->len_ratio *= mult;
   447     } else {
   448         cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   449     }
   450 
   451     return 1;               /* added a converter. */
   452 }
   453 
   454 
   455 /* Creates a set of audio filters to convert from one format to another.
   456    Returns -1 if the format conversion is not supported, 0 if there's
   457    no conversion needed, or 1 if the audio filter is set up.
   458 */
   459 
   460 int
   461 SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
   462                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
   463                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
   464 {
   465     /* Sanity check target pointer */
   466     if (cvt == NULL) {
   467         return SDL_InvalidParamError("cvt");
   468     }
   469 
   470     /* there are no unsigned types over 16 bits, so catch this up front. */
   471     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
   472         return SDL_SetError("Invalid source format");
   473     }
   474     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
   475         return SDL_SetError("Invalid destination format");
   476     }
   477 
   478     /* prevent possible divisions by zero, etc. */
   479     if ((src_channels == 0) || (dst_channels == 0)) {
   480         return SDL_SetError("Source or destination channels is zero");
   481     }
   482     if ((src_rate == 0) || (dst_rate == 0)) {
   483         return SDL_SetError("Source or destination rate is zero");
   484     }
   485 #if DEBUG_CONVERT
   486     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
   487            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
   488 #endif
   489 
   490     /* Start off with no conversion necessary */
   491     SDL_zerop(cvt);
   492     cvt->src_format = src_fmt;
   493     cvt->dst_format = dst_fmt;
   494     cvt->needed = 0;
   495     cvt->filter_index = 0;
   496     cvt->filters[0] = NULL;
   497     cvt->len_mult = 1;
   498     cvt->len_ratio = 1.0;
   499     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   500 
   501     /* Type conversion goes like this now:
   502         - byteswap to CPU native format first if necessary.
   503         - convert to native Float32 if necessary.
   504         - resample and change channel count if necessary.
   505         - convert back to native format.
   506         - byteswap back to foreign format if necessary.
   507 
   508        The expectation is we can process data faster in float32
   509        (possibly with SIMD), and making several passes over the same
   510        buffer is likely to be CPU cache-friendly, avoiding the
   511        biggest performance hit in modern times. Previously we had
   512        (script-generated) custom converters for every data type and
   513        it was a bloat on SDL compile times and final library size. */
   514 
   515     /* see if we can skip float conversion entirely (just a byteswap needed). */
   516     if ((src_rate == dst_rate) && (src_channels == dst_channels) &&
   517         ((src_fmt != dst_fmt) &&
   518          ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)))) {
   519         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   520         cvt->needed = 1;
   521         return 1;
   522     }
   523 
   524     /* Convert data types, if necessary. Updates (cvt). */
   525     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) == -1) {
   526         return -1;              /* shouldn't happen, but just in case... */
   527     }
   528 
   529     /* Channel conversion */
   530     if (src_channels != dst_channels) {
   531         if ((src_channels == 1) && (dst_channels > 1)) {
   532             cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   533             cvt->len_mult *= 2;
   534             src_channels = 2;
   535             cvt->len_ratio *= 2;
   536         }
   537         if ((src_channels == 2) && (dst_channels == 6)) {
   538             cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
   539             src_channels = 6;
   540             cvt->len_mult *= 3;
   541             cvt->len_ratio *= 3;
   542         }
   543         if ((src_channels == 2) && (dst_channels == 4)) {
   544             cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
   545             src_channels = 4;
   546             cvt->len_mult *= 2;
   547             cvt->len_ratio *= 2;
   548         }
   549         while ((src_channels * 2) <= dst_channels) {
   550             cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   551             cvt->len_mult *= 2;
   552             src_channels *= 2;
   553             cvt->len_ratio *= 2;
   554         }
   555         if ((src_channels == 6) && (dst_channels <= 2)) {
   556             cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
   557             src_channels = 2;
   558             cvt->len_ratio /= 3;
   559         }
   560         if ((src_channels == 6) && (dst_channels == 4)) {
   561             cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
   562             src_channels = 4;
   563             cvt->len_ratio /= 2;
   564         }
   565         /* This assumes that 4 channel audio is in the format:
   566            Left {front/back} + Right {front/back}
   567            so converting to L/R stereo works properly.
   568          */
   569         while (((src_channels % 2) == 0) &&
   570                ((src_channels / 2) >= dst_channels)) {
   571             cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
   572             src_channels /= 2;
   573             cvt->len_ratio /= 2;
   574         }
   575         if (src_channels != dst_channels) {
   576             /* Uh oh.. */ ;
   577         }
   578     }
   579 
   580     /* Do rate conversion, if necessary. Updates (cvt). */
   581     if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) {
   582         return -1;              /* shouldn't happen, but just in case... */
   583     }
   584 
   585     /* Move to final data type. */
   586     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
   587         return -1;              /* shouldn't happen, but just in case... */
   588     }
   589 
   590     cvt->needed = (cvt->filter_index != 0);
   591     return (cvt->needed);
   592 }
   593 
   594 
   595 struct SDL_AudioStream
   596 {
   597     SDL_AudioCVT cvt_before_resampling;
   598     SDL_AudioCVT cvt_after_resampling;
   599     SDL_DataQueue *queue;
   600     Uint8 *work_buffer;
   601     int work_buffer_len;
   602     Uint8 *resample_buffer;
   603     int resample_buffer_len;
   604     int src_sample_frame_size;
   605     SDL_AudioFormat src_format;
   606     Uint8 src_channels;
   607     int src_rate;
   608     int dst_sample_frame_size;
   609     SDL_AudioFormat dst_format;
   610     Uint8 dst_channels;
   611     int dst_rate;
   612     double rate_incr;
   613     Uint8 pre_resample_channels;
   614     SDL_bool resampler_seeded;
   615     float resampler_state[8];
   616     int packetlen;
   617 };
   618 
   619 SDL_AudioStream *SDL_NewAudioStream(const SDL_AudioFormat src_format,
   620                                     const Uint8 src_channels,
   621                                     const int src_rate,
   622                                     const SDL_AudioFormat dst_format,
   623                                     const Uint8 dst_channels,
   624                                     const int dst_rate)
   625 {
   626     const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
   627     Uint8 pre_resample_channels;
   628     SDL_AudioStream *retval;
   629 
   630     retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
   631     if (!retval) {
   632         return NULL;
   633     }
   634 
   635     /* If increasing channels, do it after resampling, since we'd just
   636        do more work to resample duplicate channels. If we're decreasing, do
   637        it first so we resample the interpolated data instead of interpolating
   638        the resampled data (!!! FIXME: decide if that works in practice, though!). */
   639     pre_resample_channels = SDL_min(src_channels, dst_channels);
   640 
   641     retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
   642     retval->src_format = src_format;
   643     retval->src_channels = src_channels;
   644     retval->src_rate = src_rate;
   645     retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
   646     retval->dst_format = dst_format;
   647     retval->dst_channels = dst_channels;
   648     retval->dst_rate = dst_rate;
   649     retval->pre_resample_channels = pre_resample_channels;
   650     retval->packetlen = packetlen;
   651     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
   652 
   653     /* Not resampling? It's an easy conversion (and maybe not even that!). */
   654     if (src_rate == dst_rate) {
   655         retval->cvt_before_resampling.needed = SDL_FALSE;
   656         retval->cvt_before_resampling.len_mult = 1;
   657         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   658             SDL_free(retval);
   659             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   660         }
   661     } else {
   662         /* Don't resample at first. Just get us to Float32 format. */
   663         /* !!! FIXME: convert to int32 on devices without hardware float. */
   664         if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) == -1) {
   665             SDL_free(retval);
   666             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   667         }
   668 
   669         /* Convert us to the final format after resampling. */
   670         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   671             SDL_free(retval);
   672             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   673         }
   674     }
   675 
   676     retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
   677     if (!retval->queue) {
   678         SDL_free(retval);
   679         return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
   680     }
   681 
   682     return retval;
   683 }
   684 
   685 
   686 static int
   687 ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   688 {
   689     /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
   690     const int chans = (int) stream->pre_resample_channels;
   691     const int framelen = chans * sizeof (float);
   692     const int total = (inbuflen / framelen);
   693     const int finalpos = total - chans;
   694     const double src_incr = 1.0 / stream->rate_incr;
   695     double idx = 0.0;
   696     float *dst = outbuf;
   697     float last_sample[SDL_arraysize(stream->resampler_state)];
   698     int consumed = 0;
   699     int i;
   700 
   701     SDL_assert(chans <= SDL_arraysize(last_sample));
   702     SDL_assert((inbuflen % framelen) == 0);
   703 
   704     if (!stream->resampler_seeded) {
   705         for (i = 0; i < chans; i++) {
   706             stream->resampler_state[i] = inbuf[i];
   707         }
   708         stream->resampler_seeded = SDL_TRUE;
   709     }
   710 
   711     for (i = 0; i < chans; i++) {
   712         last_sample[i] = stream->resampler_state[i];
   713     }
   714 
   715     while (consumed < total) {
   716         const int pos = ((int) idx) * chans;
   717         const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
   718         SDL_assert(dst < (outbuf + (outbuflen / framelen)));
   719         for (i = 0; i < chans; i++) {
   720             const float val = *(src++);
   721             *(dst++) = (val + last_sample[i]) * 0.5f;
   722             last_sample[i] = val;
   723         }
   724         consumed = pos + chans;
   725         idx += src_incr;
   726     }
   727 
   728     for (i = 0; i < chans; i++) {
   729         stream->resampler_state[i] = last_sample[i];
   730     }
   731 
   732     return (int) ((dst - outbuf) * sizeof (float));
   733 }
   734 
   735 static Uint8 *
   736 EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
   737 {
   738     if (*len < newlen) {
   739         void *ptr = SDL_realloc(*buf, newlen);
   740         if (!ptr) {
   741             SDL_OutOfMemory();
   742             return NULL;
   743         }
   744         *buf = (Uint8 *) ptr;
   745         *len = newlen;
   746     }
   747     return *buf;
   748 }
   749 
   750 int
   751 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
   752 {
   753     int buflen = (int) _buflen;
   754 
   755     if (!stream) {
   756         return SDL_InvalidParamError("stream");
   757     } else if (!buf) {
   758         return SDL_InvalidParamError("buf");
   759     } else if (buflen == 0) {
   760         return 0;  /* nothing to do. */
   761     } else if ((buflen % stream->src_sample_frame_size) != 0) {
   762         return SDL_SetError("Can't add partial sample frames");
   763     }
   764 
   765     if (stream->cvt_before_resampling.needed) {
   766         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   767         Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   768         if (workbuf == NULL) {
   769             return -1;  /* probably out of memory. */
   770         }
   771         SDL_memcpy(workbuf, buf, buflen);
   772         stream->cvt_before_resampling.buf = workbuf;
   773         stream->cvt_before_resampling.len = buflen;
   774         if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
   775             return -1;   /* uhoh! */
   776         }
   777         buf = workbuf;
   778         buflen = stream->cvt_before_resampling.len_cvt;
   779     }
   780 
   781     if (stream->dst_rate != stream->src_rate) {
   782         const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
   783         float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   784         if (workbuf == NULL) {
   785             return -1;  /* probably out of memory. */
   786         }
   787         buflen = ResampleAudioStream(stream, (float *) buf, buflen, workbuf, workbuflen);
   788         buf = workbuf;
   789     }
   790 
   791     if (stream->cvt_after_resampling.needed) {
   792         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   793         Uint8 *workbuf;
   794 
   795         if (buf == stream->resample_buffer) {
   796             workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   797         } else {
   798             const int inplace = (buf == stream->work_buffer);
   799             workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   800             if (workbuf && !inplace) {
   801                 SDL_memcpy(workbuf, buf, buflen);
   802             }
   803         }
   804 
   805         if (workbuf == NULL) {
   806             return -1;  /* probably out of memory. */
   807         }
   808 
   809         stream->cvt_after_resampling.buf = workbuf;
   810         stream->cvt_after_resampling.len = buflen;
   811         if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
   812             return -1;   /* uhoh! */
   813         }
   814         buf = workbuf;
   815         buflen = stream->cvt_after_resampling.len_cvt;
   816     }
   817 
   818     return SDL_WriteToDataQueue(stream->queue, buf, buflen);
   819 }
   820 
   821 void
   822 SDL_AudioStreamClear(SDL_AudioStream *stream)
   823 {
   824     if (!stream) {
   825         SDL_InvalidParamError("stream");
   826     } else {
   827         SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
   828         stream->resampler_seeded = SDL_FALSE;
   829     }
   830 }
   831 
   832 
   833 /* get converted/resampled data from the stream */
   834 int
   835 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
   836 {
   837     if (!stream) {
   838         return SDL_InvalidParamError("stream");
   839     } else if (!buf) {
   840         return SDL_InvalidParamError("buf");
   841     } else if (len == 0) {
   842         return 0;  /* nothing to do. */
   843     } else if ((len % stream->dst_sample_frame_size) != 0) {
   844         return SDL_SetError("Can't request partial sample frames");
   845     }
   846 
   847     return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
   848 }
   849 
   850 /* number of converted/resampled bytes available */
   851 int
   852 SDL_AudioStreamAvailable(SDL_AudioStream *stream)
   853 {
   854     return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
   855 }
   856 
   857 /* dispose of a stream */
   858 void
   859 SDL_FreeAudioStream(SDL_AudioStream *stream)
   860 {
   861     if (stream) {
   862         SDL_FreeDataQueue(stream->queue);
   863         SDL_free(stream->work_buffer);
   864         SDL_free(stream->resample_buffer);
   865         SDL_free(stream);
   866     }
   867 }
   868 
   869 /* vi: set ts=4 sw=4 expandtab: */
   870