src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 05 Jan 2017 23:53:46 -0800
changeset 10767 b6046389b839
parent 10764 f9bf759e9dd1
child 10773 016a40fda1bb
permissions -rw-r--r--
Don't do any audio conversion if none is necessary
     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     /* Make sure we zero out the audio conversion before error checking */
   471     SDL_zerop(cvt);
   472 
   473     /* there are no unsigned types over 16 bits, so catch this up front. */
   474     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
   475         return SDL_SetError("Invalid source format");
   476     }
   477     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
   478         return SDL_SetError("Invalid destination format");
   479     }
   480 
   481     /* prevent possible divisions by zero, etc. */
   482     if ((src_channels == 0) || (dst_channels == 0)) {
   483         return SDL_SetError("Source or destination channels is zero");
   484     }
   485     if ((src_rate == 0) || (dst_rate == 0)) {
   486         return SDL_SetError("Source or destination rate is zero");
   487     }
   488 #if DEBUG_CONVERT
   489     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
   490            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
   491 #endif
   492 
   493     /* Start off with no conversion necessary */
   494     cvt->src_format = src_fmt;
   495     cvt->dst_format = dst_fmt;
   496     cvt->needed = 0;
   497     cvt->filter_index = 0;
   498     cvt->filters[0] = NULL;
   499     cvt->len_mult = 1;
   500     cvt->len_ratio = 1.0;
   501     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   502 
   503     /* Type conversion goes like this now:
   504         - byteswap to CPU native format first if necessary.
   505         - convert to native Float32 if necessary.
   506         - resample and change channel count if necessary.
   507         - convert back to native format.
   508         - byteswap back to foreign format if necessary.
   509 
   510        The expectation is we can process data faster in float32
   511        (possibly with SIMD), and making several passes over the same
   512        buffer is likely to be CPU cache-friendly, avoiding the
   513        biggest performance hit in modern times. Previously we had
   514        (script-generated) custom converters for every data type and
   515        it was a bloat on SDL compile times and final library size. */
   516 
   517     /* see if we can skip float conversion entirely. */
   518     if (src_rate == dst_rate && src_channels == dst_channels) {
   519         if (src_fmt == dst_fmt) {
   520             return 0;
   521         }
   522 
   523         /* just a byteswap needed? */
   524         if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
   525             cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   526             cvt->needed = 1;
   527             return 1;
   528         }
   529     }
   530 
   531     /* Convert data types, if necessary. Updates (cvt). */
   532     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
   533         return -1;              /* shouldn't happen, but just in case... */
   534     }
   535 
   536     /* Channel conversion */
   537     if (src_channels != dst_channels) {
   538         if ((src_channels == 1) && (dst_channels > 1)) {
   539             cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   540             cvt->len_mult *= 2;
   541             src_channels = 2;
   542             cvt->len_ratio *= 2;
   543         }
   544         if ((src_channels == 2) && (dst_channels == 6)) {
   545             cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
   546             src_channels = 6;
   547             cvt->len_mult *= 3;
   548             cvt->len_ratio *= 3;
   549         }
   550         if ((src_channels == 2) && (dst_channels == 4)) {
   551             cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
   552             src_channels = 4;
   553             cvt->len_mult *= 2;
   554             cvt->len_ratio *= 2;
   555         }
   556         while ((src_channels * 2) <= dst_channels) {
   557             cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
   558             cvt->len_mult *= 2;
   559             src_channels *= 2;
   560             cvt->len_ratio *= 2;
   561         }
   562         if ((src_channels == 6) && (dst_channels <= 2)) {
   563             cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
   564             src_channels = 2;
   565             cvt->len_ratio /= 3;
   566         }
   567         if ((src_channels == 6) && (dst_channels == 4)) {
   568             cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
   569             src_channels = 4;
   570             cvt->len_ratio /= 2;
   571         }
   572         /* This assumes that 4 channel audio is in the format:
   573            Left {front/back} + Right {front/back}
   574            so converting to L/R stereo works properly.
   575          */
   576         while (((src_channels % 2) == 0) &&
   577                ((src_channels / 2) >= dst_channels)) {
   578             cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
   579             src_channels /= 2;
   580             cvt->len_ratio /= 2;
   581         }
   582         if (src_channels != dst_channels) {
   583             /* Uh oh.. */ ;
   584         }
   585     }
   586 
   587     /* Do rate conversion, if necessary. Updates (cvt). */
   588     if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
   589         return -1;              /* shouldn't happen, but just in case... */
   590     }
   591 
   592     /* Move to final data type. */
   593     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
   594         return -1;              /* shouldn't happen, but just in case... */
   595     }
   596 
   597     cvt->needed = (cvt->filter_index != 0);
   598     return (cvt->needed);
   599 }
   600 
   601 
   602 struct SDL_AudioStream
   603 {
   604     SDL_AudioCVT cvt_before_resampling;
   605     SDL_AudioCVT cvt_after_resampling;
   606     SDL_DataQueue *queue;
   607     Uint8 *work_buffer;
   608     int work_buffer_len;
   609     Uint8 *resample_buffer;
   610     int resample_buffer_len;
   611     int src_sample_frame_size;
   612     SDL_AudioFormat src_format;
   613     Uint8 src_channels;
   614     int src_rate;
   615     int dst_sample_frame_size;
   616     SDL_AudioFormat dst_format;
   617     Uint8 dst_channels;
   618     int dst_rate;
   619     double rate_incr;
   620     Uint8 pre_resample_channels;
   621     SDL_bool resampler_seeded;
   622     float resampler_state[8];
   623     int packetlen;
   624 };
   625 
   626 SDL_AudioStream *SDL_NewAudioStream(const SDL_AudioFormat src_format,
   627                                     const Uint8 src_channels,
   628                                     const int src_rate,
   629                                     const SDL_AudioFormat dst_format,
   630                                     const Uint8 dst_channels,
   631                                     const int dst_rate)
   632 {
   633     const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
   634     Uint8 pre_resample_channels;
   635     SDL_AudioStream *retval;
   636 
   637     retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
   638     if (!retval) {
   639         return NULL;
   640     }
   641 
   642     /* If increasing channels, do it after resampling, since we'd just
   643        do more work to resample duplicate channels. If we're decreasing, do
   644        it first so we resample the interpolated data instead of interpolating
   645        the resampled data (!!! FIXME: decide if that works in practice, though!). */
   646     pre_resample_channels = SDL_min(src_channels, dst_channels);
   647 
   648     retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
   649     retval->src_format = src_format;
   650     retval->src_channels = src_channels;
   651     retval->src_rate = src_rate;
   652     retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
   653     retval->dst_format = dst_format;
   654     retval->dst_channels = dst_channels;
   655     retval->dst_rate = dst_rate;
   656     retval->pre_resample_channels = pre_resample_channels;
   657     retval->packetlen = packetlen;
   658     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
   659 
   660     /* Not resampling? It's an easy conversion (and maybe not even that!). */
   661     if (src_rate == dst_rate) {
   662         retval->cvt_before_resampling.needed = SDL_FALSE;
   663         retval->cvt_before_resampling.len_mult = 1;
   664         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   665             SDL_free(retval);
   666             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   667         }
   668     } else {
   669         /* Don't resample at first. Just get us to Float32 format. */
   670         /* !!! FIXME: convert to int32 on devices without hardware float. */
   671         if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) == -1) {
   672             SDL_free(retval);
   673             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   674         }
   675 
   676         /* Convert us to the final format after resampling. */
   677         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
   678             SDL_free(retval);
   679             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   680         }
   681     }
   682 
   683     retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
   684     if (!retval->queue) {
   685         SDL_free(retval);
   686         return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
   687     }
   688 
   689     return retval;
   690 }
   691 
   692 
   693 static int
   694 ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   695 {
   696     /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
   697     const int chans = (int) stream->pre_resample_channels;
   698     const int framelen = chans * sizeof (float);
   699     const int total = (inbuflen / framelen);
   700     const int finalpos = total - chans;
   701     const double src_incr = 1.0 / stream->rate_incr;
   702     double idx = 0.0;
   703     float *dst = outbuf;
   704     float last_sample[SDL_arraysize(stream->resampler_state)];
   705     int consumed = 0;
   706     int i;
   707 
   708     SDL_assert(chans <= SDL_arraysize(last_sample));
   709     SDL_assert((inbuflen % framelen) == 0);
   710 
   711     if (!stream->resampler_seeded) {
   712         for (i = 0; i < chans; i++) {
   713             stream->resampler_state[i] = inbuf[i];
   714         }
   715         stream->resampler_seeded = SDL_TRUE;
   716     }
   717 
   718     for (i = 0; i < chans; i++) {
   719         last_sample[i] = stream->resampler_state[i];
   720     }
   721 
   722     while (consumed < total) {
   723         const int pos = ((int) idx) * chans;
   724         const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
   725         SDL_assert(dst < (outbuf + (outbuflen / framelen)));
   726         for (i = 0; i < chans; i++) {
   727             const float val = *(src++);
   728             *(dst++) = (val + last_sample[i]) * 0.5f;
   729             last_sample[i] = val;
   730         }
   731         consumed = pos + chans;
   732         idx += src_incr;
   733     }
   734 
   735     for (i = 0; i < chans; i++) {
   736         stream->resampler_state[i] = last_sample[i];
   737     }
   738 
   739     return (int) ((dst - outbuf) * sizeof (float));
   740 }
   741 
   742 static Uint8 *
   743 EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
   744 {
   745     if (*len < newlen) {
   746         void *ptr = SDL_realloc(*buf, newlen);
   747         if (!ptr) {
   748             SDL_OutOfMemory();
   749             return NULL;
   750         }
   751         *buf = (Uint8 *) ptr;
   752         *len = newlen;
   753     }
   754     return *buf;
   755 }
   756 
   757 int
   758 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
   759 {
   760     int buflen = (int) _buflen;
   761 
   762     if (!stream) {
   763         return SDL_InvalidParamError("stream");
   764     } else if (!buf) {
   765         return SDL_InvalidParamError("buf");
   766     } else if (buflen == 0) {
   767         return 0;  /* nothing to do. */
   768     } else if ((buflen % stream->src_sample_frame_size) != 0) {
   769         return SDL_SetError("Can't add partial sample frames");
   770     }
   771 
   772     if (stream->cvt_before_resampling.needed) {
   773         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   774         Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   775         if (workbuf == NULL) {
   776             return -1;  /* probably out of memory. */
   777         }
   778         SDL_memcpy(workbuf, buf, buflen);
   779         stream->cvt_before_resampling.buf = workbuf;
   780         stream->cvt_before_resampling.len = buflen;
   781         if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
   782             return -1;   /* uhoh! */
   783         }
   784         buf = workbuf;
   785         buflen = stream->cvt_before_resampling.len_cvt;
   786     }
   787 
   788     if (stream->dst_rate != stream->src_rate) {
   789         const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
   790         float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   791         if (workbuf == NULL) {
   792             return -1;  /* probably out of memory. */
   793         }
   794         buflen = ResampleAudioStream(stream, (float *) buf, buflen, workbuf, workbuflen);
   795         buf = workbuf;
   796     }
   797 
   798     if (stream->cvt_after_resampling.needed) {
   799         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   800         Uint8 *workbuf;
   801 
   802         if (buf == stream->resample_buffer) {
   803             workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   804         } else {
   805             const int inplace = (buf == stream->work_buffer);
   806             workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   807             if (workbuf && !inplace) {
   808                 SDL_memcpy(workbuf, buf, buflen);
   809             }
   810         }
   811 
   812         if (workbuf == NULL) {
   813             return -1;  /* probably out of memory. */
   814         }
   815 
   816         stream->cvt_after_resampling.buf = workbuf;
   817         stream->cvt_after_resampling.len = buflen;
   818         if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
   819             return -1;   /* uhoh! */
   820         }
   821         buf = workbuf;
   822         buflen = stream->cvt_after_resampling.len_cvt;
   823     }
   824 
   825     return SDL_WriteToDataQueue(stream->queue, buf, buflen);
   826 }
   827 
   828 void
   829 SDL_AudioStreamClear(SDL_AudioStream *stream)
   830 {
   831     if (!stream) {
   832         SDL_InvalidParamError("stream");
   833     } else {
   834         SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
   835         stream->resampler_seeded = SDL_FALSE;
   836     }
   837 }
   838 
   839 
   840 /* get converted/resampled data from the stream */
   841 int
   842 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
   843 {
   844     if (!stream) {
   845         return SDL_InvalidParamError("stream");
   846     } else if (!buf) {
   847         return SDL_InvalidParamError("buf");
   848     } else if (len == 0) {
   849         return 0;  /* nothing to do. */
   850     } else if ((len % stream->dst_sample_frame_size) != 0) {
   851         return SDL_SetError("Can't request partial sample frames");
   852     }
   853 
   854     return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
   855 }
   856 
   857 /* number of converted/resampled bytes available */
   858 int
   859 SDL_AudioStreamAvailable(SDL_AudioStream *stream)
   860 {
   861     return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
   862 }
   863 
   864 /* dispose of a stream */
   865 void
   866 SDL_FreeAudioStream(SDL_AudioStream *stream)
   867 {
   868     if (stream) {
   869         SDL_FreeDataQueue(stream->queue);
   870         SDL_free(stream->work_buffer);
   871         SDL_free(stream->resample_buffer);
   872         SDL_free(stream);
   873     }
   874 }
   875 
   876 /* vi: set ts=4 sw=4 expandtab: */
   877