src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Jan 2017 20:18:59 -0500
changeset 10831 fcbb4d7f2344
parent 10830 92013fad89d1
child 10832 189266031c6f
permissions -rw-r--r--
audio: don't cast to double in SDL_ConvertStereoToMono().

It's expensive and (hopefully) unnecessary. If this becomes an overflow
problem, we could multiply both values by 0.5f before adding them, but let's
see if we can get by without the extra multiplication first.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 /* Functions for audio drivers to perform runtime conversion of audio format */
    24 
    25 #include "SDL_audio.h"
    26 #include "SDL_audio_c.h"
    27 
    28 #include "SDL_loadso.h"
    29 #include "SDL_assert.h"
    30 #include "../SDL_dataqueue.h"
    31 
    32 /* Effectively mix right and left channels into a single channel */
    33 static void SDLCALL
    34 SDL_ConvertStereoToMono(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++) = (src[0] + src[1]) * 0.5f;
    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 /* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
    55 static void SDLCALL
    56 SDL_Convert51ToStereo(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("5.1", "stereo");
    63     SDL_assert(format == AUDIO_F32SYS);
    64 
    65     /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
    66     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
    67         const double front_center = (double) src[2];
    68         dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0);  /* left */
    69         dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0);  /* right */
    70     }
    71 
    72     cvt->len_cvt /= 3;
    73     if (cvt->filters[++cvt->filter_index]) {
    74         cvt->filters[cvt->filter_index] (cvt, format);
    75     }
    76 }
    77 
    78 
    79 /* Convert from 5.1 to quad */
    80 static void SDLCALL
    81 SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    82 {
    83     float *dst = (float *) cvt->buf;
    84     const float *src = dst;
    85     int i;
    86 
    87     LOG_DEBUG_CONVERT("5.1", "quad");
    88     SDL_assert(format == AUDIO_F32SYS);
    89 
    90     /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
    91     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
    92         /* FIXME: this is a good candidate for SIMD. */
    93         const double front_center = (double) src[2];
    94         dst[0] = (float) ((src[0] + front_center) * 0.5);  /* FL */
    95         dst[1] = (float) ((src[1] + front_center) * 0.5);  /* FR */
    96         dst[2] = (float) ((src[4] + front_center) * 0.5);  /* BL */
    97         dst[3] = (float) ((src[5] + front_center) * 0.5);  /* BR */
    98     }
    99 
   100     cvt->len_cvt /= 6;
   101     cvt->len_cvt *= 4;
   102     if (cvt->filters[++cvt->filter_index]) {
   103         cvt->filters[cvt->filter_index] (cvt, format);
   104     }
   105 }
   106 
   107 
   108 /* Duplicate a mono channel to both stereo channels */
   109 static void SDLCALL
   110 SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   111 {
   112     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   113     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   114     int i;
   115 
   116     LOG_DEBUG_CONVERT("mono", "stereo");
   117     SDL_assert(format == AUDIO_F32SYS);
   118 
   119     for (i = cvt->len_cvt / sizeof (float); i; --i) {
   120         src--;
   121         dst -= 2;
   122         dst[0] = dst[1] = *src;
   123     }
   124 
   125     cvt->len_cvt *= 2;
   126     if (cvt->filters[++cvt->filter_index]) {
   127         cvt->filters[cvt->filter_index] (cvt, format);
   128     }
   129 }
   130 
   131 
   132 /* Duplicate a stereo channel to a pseudo-5.1 stream */
   133 static void SDLCALL
   134 SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   135 {
   136     int i;
   137     float lf, rf, ce;
   138     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   139     float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
   140 
   141     LOG_DEBUG_CONVERT("stereo", "5.1");
   142     SDL_assert(format == AUDIO_F32SYS);
   143 
   144     for (i = cvt->len_cvt / 8; i; --i) {
   145         dst -= 6;
   146         src -= 2;
   147         lf = src[0];
   148         rf = src[1];
   149         ce = (lf + rf) * 0.5f;
   150         dst[0] = lf + (lf - ce);  /* FL */
   151         dst[1] = rf + (rf - ce);  /* FR */
   152         dst[2] = ce;  /* FC */
   153         dst[3] = ce;  /* !!! FIXME: wrong! This is the subwoofer. */
   154         dst[4] = lf;  /* BL */
   155         dst[5] = rf;  /* BR */
   156     }
   157 
   158     cvt->len_cvt *= 3;
   159     if (cvt->filters[++cvt->filter_index]) {
   160         cvt->filters[cvt->filter_index] (cvt, format);
   161     }
   162 }
   163 
   164 
   165 /* Duplicate a stereo channel to a pseudo-4.0 stream */
   166 static void SDLCALL
   167 SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   168 {
   169     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   170     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   171     float lf, rf;
   172     int i;
   173 
   174     LOG_DEBUG_CONVERT("stereo", "quad");
   175     SDL_assert(format == AUDIO_F32SYS);
   176 
   177     for (i = cvt->len_cvt / 8; i; --i) {
   178         dst -= 4;
   179         src -= 2;
   180         lf = src[0];
   181         rf = src[1];
   182         dst[0] = lf;  /* FL */
   183         dst[1] = rf;  /* FR */
   184         dst[2] = lf;  /* BL */
   185         dst[3] = rf;  /* BR */
   186     }
   187 
   188     cvt->len_cvt *= 2;
   189     if (cvt->filters[++cvt->filter_index]) {
   190         cvt->filters[cvt->filter_index] (cvt, format);
   191     }
   192 }
   193 
   194 static int
   195 SDL_ResampleAudioSimple(const int chans, const double rate_incr,
   196                         float *last_sample, const float *inbuf,
   197                         const int inbuflen, float *outbuf, const int outbuflen)
   198 {
   199     const int framelen = chans * (int)sizeof (float);
   200     const int total = (inbuflen / framelen);
   201     const int finalpos = (total * chans) - chans;
   202     const int dest_samples = (int)(((double)total) * rate_incr);
   203     const double src_incr = 1.0 / rate_incr;
   204     float *dst = outbuf;
   205     float *target = (dst + (dest_samples * chans));
   206     double idx = 0.0;
   207     int i;
   208 
   209     SDL_assert((dest_samples * framelen) <= outbuflen);
   210     SDL_assert((inbuflen % framelen) == 0);
   211 
   212     while (dst < target) {
   213         const int pos = ((int)idx) * chans;
   214         const float *src = &inbuf[pos];
   215         SDL_assert(pos <= finalpos);
   216         for (i = 0; i < chans; i++) {
   217             const float val = *(src++);
   218             *(dst++) = (val + last_sample[i]) * 0.5f;
   219             last_sample[i] = val;
   220         }
   221         idx += src_incr;
   222     }
   223 
   224     return (int) ((dst - outbuf) * (int)sizeof(float));
   225 }
   226 
   227 
   228 int
   229 SDL_ConvertAudio(SDL_AudioCVT * cvt)
   230 {
   231     /* !!! FIXME: (cvt) should be const; stack-copy it here. */
   232     /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
   233 
   234     /* Make sure there's data to convert */
   235     if (cvt->buf == NULL) {
   236         return SDL_SetError("No buffer allocated for conversion");
   237     }
   238 
   239     /* Return okay if no conversion is necessary */
   240     cvt->len_cvt = cvt->len;
   241     if (cvt->filters[0] == NULL) {
   242         return 0;
   243     }
   244 
   245     /* Set up the conversion and go! */
   246     cvt->filter_index = 0;
   247     cvt->filters[0] (cvt, cvt->src_format);
   248     return 0;
   249 }
   250 
   251 static void SDLCALL
   252 SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   253 {
   254 #if DEBUG_CONVERT
   255     printf("Converting byte order\n");
   256 #endif
   257 
   258     switch (SDL_AUDIO_BITSIZE(format)) {
   259         #define CASESWAP(b) \
   260             case b: { \
   261                 Uint##b *ptr = (Uint##b *) cvt->buf; \
   262                 int i; \
   263                 for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
   264                     *ptr = SDL_Swap##b(*ptr); \
   265                 } \
   266                 break; \
   267             }
   268 
   269         CASESWAP(16);
   270         CASESWAP(32);
   271         CASESWAP(64);
   272 
   273         #undef CASESWAP
   274 
   275         default: SDL_assert(!"unhandled byteswap datatype!"); break;
   276     }
   277 
   278     if (cvt->filters[++cvt->filter_index]) {
   279         /* flip endian flag for data. */
   280         if (format & SDL_AUDIO_MASK_ENDIAN) {
   281             format &= ~SDL_AUDIO_MASK_ENDIAN;
   282         } else {
   283             format |= SDL_AUDIO_MASK_ENDIAN;
   284         }
   285         cvt->filters[cvt->filter_index](cvt, format);
   286     }
   287 }
   288 
   289 
   290 static int
   291 SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
   292 {
   293     int retval = 0;  /* 0 == no conversion necessary. */
   294 
   295     if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   296         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   297         retval = 1;  /* added a converter. */
   298     }
   299 
   300     if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
   301         const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
   302         const Uint16 dst_bitsize = 32;
   303         SDL_AudioFilter filter = NULL;
   304 
   305         switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   306             case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
   307             case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
   308             case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
   309             case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
   310             case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
   311             default: SDL_assert(!"Unexpected audio format!"); break;
   312         }
   313 
   314         if (!filter) {
   315             return SDL_SetError("No conversion available for these formats");
   316         }
   317 
   318         cvt->filters[cvt->filter_index++] = filter;
   319         if (src_bitsize < dst_bitsize) {
   320             const int mult = (dst_bitsize / src_bitsize);
   321             cvt->len_mult *= mult;
   322             cvt->len_ratio *= mult;
   323         } else if (src_bitsize > dst_bitsize) {
   324             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   325         }
   326 
   327         retval = 1;  /* added a converter. */
   328     }
   329 
   330     return retval;
   331 }
   332 
   333 static int
   334 SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
   335 {
   336     int retval = 0;  /* 0 == no conversion necessary. */
   337 
   338     if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
   339         const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
   340         const Uint16 src_bitsize = 32;
   341         SDL_AudioFilter filter = NULL;
   342         switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   343             case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
   344             case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
   345             case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
   346             case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
   347             case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
   348             default: SDL_assert(!"Unexpected audio format!"); break;
   349         }
   350 
   351         if (!filter) {
   352             return SDL_SetError("No conversion available for these formats");
   353         }
   354 
   355         cvt->filters[cvt->filter_index++] = filter;
   356         if (src_bitsize < dst_bitsize) {
   357             const int mult = (dst_bitsize / src_bitsize);
   358             cvt->len_mult *= mult;
   359             cvt->len_ratio *= mult;
   360         } else if (src_bitsize > dst_bitsize) {
   361             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   362         }
   363         retval = 1;  /* added a converter. */
   364     }
   365 
   366     if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   367         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   368         retval = 1;  /* added a converter. */
   369     }
   370 
   371     return retval;
   372 }
   373 
   374 static void
   375 SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
   376 {
   377     const float *src = (const float *) cvt->buf;
   378     const int srclen = cvt->len_cvt;
   379     float *dst = (float *) (cvt->buf + srclen);
   380     const int dstlen = (cvt->len * cvt->len_mult) - srclen;
   381     float state[8];
   382 
   383     SDL_assert(format == AUDIO_F32SYS);
   384 
   385     SDL_memcpy(state, src, chans*sizeof(*src));
   386 
   387     cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
   388 
   389     SDL_memcpy(cvt->buf, dst, cvt->len_cvt);
   390     if (cvt->filters[++cvt->filter_index]) {
   391         cvt->filters[cvt->filter_index](cvt, format);
   392     }
   393 }
   394 
   395 /* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
   396    !!! FIXME:  store channel info, so we have to have function entry
   397    !!! FIXME:  points for each supported channel count and multiple
   398    !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
   399 #define RESAMPLER_FUNCS(chans) \
   400     static void SDLCALL \
   401     SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   402         SDL_ResampleCVT(cvt, chans, format); \
   403     }
   404 RESAMPLER_FUNCS(1)
   405 RESAMPLER_FUNCS(2)
   406 RESAMPLER_FUNCS(4)
   407 RESAMPLER_FUNCS(6)
   408 RESAMPLER_FUNCS(8)
   409 #undef RESAMPLER_FUNCS
   410 
   411 static SDL_AudioFilter
   412 ChooseCVTResampler(const int dst_channels)
   413 {
   414     switch (dst_channels) {
   415         case 1: return SDL_ResampleCVT_c1;
   416         case 2: return SDL_ResampleCVT_c2;
   417         case 4: return SDL_ResampleCVT_c4;
   418         case 6: return SDL_ResampleCVT_c6;
   419         case 8: return SDL_ResampleCVT_c8;
   420         default: break;
   421     }
   422 
   423     return NULL;
   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 = ChooseCVTResampler(dst_channels);
   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     /* the buffer is big enough to hold the destination now, but
   452        we need it large enough to hold a separate scratch buffer. */
   453     cvt->len_mult *= 2;
   454 
   455     return 1;               /* added a converter. */
   456 }
   457 
   458 
   459 /* Creates a set of audio filters to convert from one format to another.
   460    Returns -1 if the format conversion is not supported, 0 if there's
   461    no conversion needed, or 1 if the audio filter is set up.
   462 */
   463 
   464 int
   465 SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
   466                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
   467                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
   468 {
   469     /* Sanity check target pointer */
   470     if (cvt == NULL) {
   471         return SDL_InvalidParamError("cvt");
   472     }
   473 
   474     /* Make sure we zero out the audio conversion before error checking */
   475     SDL_zerop(cvt);
   476 
   477     /* there are no unsigned types over 16 bits, so catch this up front. */
   478     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
   479         return SDL_SetError("Invalid source format");
   480     }
   481     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
   482         return SDL_SetError("Invalid destination format");
   483     }
   484 
   485     /* prevent possible divisions by zero, etc. */
   486     if ((src_channels == 0) || (dst_channels == 0)) {
   487         return SDL_SetError("Source or destination channels is zero");
   488     }
   489     if ((src_rate == 0) || (dst_rate == 0)) {
   490         return SDL_SetError("Source or destination rate is zero");
   491     }
   492 #if DEBUG_CONVERT
   493     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
   494            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
   495 #endif
   496 
   497     /* Start off with no conversion necessary */
   498     cvt->src_format = src_fmt;
   499     cvt->dst_format = dst_fmt;
   500     cvt->needed = 0;
   501     cvt->filter_index = 0;
   502     cvt->filters[0] = NULL;
   503     cvt->len_mult = 1;
   504     cvt->len_ratio = 1.0;
   505     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   506 
   507     /* Type conversion goes like this now:
   508         - byteswap to CPU native format first if necessary.
   509         - convert to native Float32 if necessary.
   510         - resample and change channel count if necessary.
   511         - convert back to native format.
   512         - byteswap back to foreign format if necessary.
   513 
   514        The expectation is we can process data faster in float32
   515        (possibly with SIMD), and making several passes over the same
   516        buffer is likely to be CPU cache-friendly, avoiding the
   517        biggest performance hit in modern times. Previously we had
   518        (script-generated) custom converters for every data type and
   519        it was a bloat on SDL compile times and final library size. */
   520 
   521     /* see if we can skip float conversion entirely. */
   522     if (src_rate == dst_rate && src_channels == dst_channels) {
   523         if (src_fmt == dst_fmt) {
   524             return 0;
   525         }
   526 
   527         /* just a byteswap needed? */
   528         if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
   529             cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   530             cvt->needed = 1;
   531             return 1;
   532         }
   533     }
   534 
   535     /* Convert data types, if necessary. Updates (cvt). */
   536     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
   537         return -1;              /* shouldn't happen, but just in case... */
   538     }
   539 
   540     /* Channel conversion */
   541     if (src_channels != dst_channels) {
   542         if ((src_channels == 1) && (dst_channels > 1)) {
   543             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   544             cvt->len_mult *= 2;
   545             src_channels = 2;
   546             cvt->len_ratio *= 2;
   547         }
   548         if ((src_channels == 2) && (dst_channels == 6)) {
   549             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
   550             src_channels = 6;
   551             cvt->len_mult *= 3;
   552             cvt->len_ratio *= 3;
   553         }
   554         if ((src_channels == 2) && (dst_channels == 4)) {
   555             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
   556             src_channels = 4;
   557             cvt->len_mult *= 2;
   558             cvt->len_ratio *= 2;
   559         }
   560         while ((src_channels * 2) <= dst_channels) {
   561             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   562             cvt->len_mult *= 2;
   563             src_channels *= 2;
   564             cvt->len_ratio *= 2;
   565         }
   566         if ((src_channels == 6) && (dst_channels <= 2)) {
   567             cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
   568             src_channels = 2;
   569             cvt->len_ratio /= 3;
   570         }
   571         if ((src_channels == 6) && (dst_channels == 4)) {
   572             cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
   573             src_channels = 4;
   574             cvt->len_ratio /= 2;
   575         }
   576         /* This assumes that 4 channel audio is in the format:
   577            Left {front/back} + Right {front/back}
   578            so converting to L/R stereo works properly.
   579          */
   580         while (((src_channels % 2) == 0) &&
   581                ((src_channels / 2) >= dst_channels)) {
   582             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono;
   583             src_channels /= 2;
   584             cvt->len_ratio /= 2;
   585         }
   586         if (src_channels != dst_channels) {
   587             /* Uh oh.. */ ;
   588         }
   589     }
   590 
   591     /* Do rate conversion, if necessary. Updates (cvt). */
   592     if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
   593         return -1;              /* shouldn't happen, but just in case... */
   594     }
   595 
   596     /* Move to final data type. */
   597     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
   598         return -1;              /* shouldn't happen, but just in case... */
   599     }
   600 
   601     cvt->needed = (cvt->filter_index != 0);
   602     return (cvt->needed);
   603 }
   604 
   605 typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
   606 typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   607 typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   608 
   609 struct SDL_AudioStream
   610 {
   611     SDL_AudioCVT cvt_before_resampling;
   612     SDL_AudioCVT cvt_after_resampling;
   613     SDL_DataQueue *queue;
   614     Uint8 *work_buffer;
   615     int work_buffer_len;
   616     Uint8 *resample_buffer;
   617     int resample_buffer_len;
   618     int src_sample_frame_size;
   619     SDL_AudioFormat src_format;
   620     Uint8 src_channels;
   621     int src_rate;
   622     int dst_sample_frame_size;
   623     SDL_AudioFormat dst_format;
   624     Uint8 dst_channels;
   625     int dst_rate;
   626     double rate_incr;
   627     Uint8 pre_resample_channels;
   628     int packetlen;
   629     void *resampler_state;
   630     SDL_ResampleAudioStreamFunc resampler_func;
   631     SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
   632     SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
   633 };
   634 
   635 #ifdef HAVE_LIBSAMPLERATE_H
   636 static int
   637 SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   638 {
   639     const int framelen = sizeof(float) * stream->pre_resample_channels;
   640     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   641     SRC_DATA data;
   642     int result;
   643 
   644     data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
   645     data.input_frames = inbuflen / framelen;
   646     data.input_frames_used = 0;
   647 
   648     data.data_out = outbuf;
   649     data.output_frames = outbuflen / framelen;
   650 
   651     data.end_of_input = 0;
   652     data.src_ratio = stream->rate_incr;
   653 
   654     result = SRC_src_process(state, &data);
   655     if (result != 0) {
   656         SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
   657         return 0;
   658     }
   659 
   660     /* If this fails, we need to store them off somewhere */
   661     SDL_assert(data.input_frames_used == data.input_frames);
   662 
   663     return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
   664 }
   665 
   666 static void
   667 SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
   668 {
   669     SRC_src_reset((SRC_STATE *)stream->resampler_state);
   670 }
   671 
   672 static void
   673 SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
   674 {
   675     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   676     if (state) {
   677         SRC_src_delete(state);
   678     }
   679 
   680     stream->resampler_state = NULL;
   681     stream->resampler_func = NULL;
   682     stream->reset_resampler_func = NULL;
   683     stream->cleanup_resampler_func = NULL;
   684 }
   685 
   686 static SDL_bool
   687 SetupLibSampleRateResampling(SDL_AudioStream *stream)
   688 {
   689     int result = 0;
   690     SRC_STATE *state = NULL;
   691 
   692     if (SRC_available) {
   693         state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
   694         if (!state) {
   695             SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
   696         }
   697     }
   698 
   699     if (!state) {
   700         SDL_CleanupAudioStreamResampler_SRC(stream);
   701         return SDL_FALSE;
   702     }
   703 
   704     stream->resampler_state = state;
   705     stream->resampler_func = SDL_ResampleAudioStream_SRC;
   706     stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
   707     stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
   708 
   709     return SDL_TRUE;
   710 }
   711 #endif /* HAVE_LIBSAMPLERATE_H */
   712 
   713 
   714 typedef struct
   715 {
   716     SDL_bool resampler_seeded;
   717     float resampler_state[8];
   718 } SDL_AudioStreamResamplerState;
   719 
   720 static int
   721 SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   722 {
   723     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   724     const int chans = (int)stream->pre_resample_channels;
   725 
   726     SDL_assert(chans <= SDL_arraysize(state->resampler_state));
   727 
   728     if (!state->resampler_seeded) {
   729         int i;
   730         for (i = 0; i < chans; i++) {
   731             state->resampler_state[i] = inbuf[i];
   732         }
   733         state->resampler_seeded = SDL_TRUE;
   734     }
   735 
   736     return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
   737 }
   738 
   739 static void
   740 SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
   741 {
   742     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   743     state->resampler_seeded = SDL_FALSE;
   744 }
   745 
   746 static void
   747 SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
   748 {
   749     SDL_free(stream->resampler_state);
   750 }
   751 
   752 SDL_AudioStream *
   753 SDL_NewAudioStream(const SDL_AudioFormat src_format,
   754                    const Uint8 src_channels,
   755                    const int src_rate,
   756                    const SDL_AudioFormat dst_format,
   757                    const Uint8 dst_channels,
   758                    const int dst_rate)
   759 {
   760     const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
   761     Uint8 pre_resample_channels;
   762     SDL_AudioStream *retval;
   763 
   764     retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
   765     if (!retval) {
   766         return NULL;
   767     }
   768 
   769     /* If increasing channels, do it after resampling, since we'd just
   770        do more work to resample duplicate channels. If we're decreasing, do
   771        it first so we resample the interpolated data instead of interpolating
   772        the resampled data (!!! FIXME: decide if that works in practice, though!). */
   773     pre_resample_channels = SDL_min(src_channels, dst_channels);
   774 
   775     retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
   776     retval->src_format = src_format;
   777     retval->src_channels = src_channels;
   778     retval->src_rate = src_rate;
   779     retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
   780     retval->dst_format = dst_format;
   781     retval->dst_channels = dst_channels;
   782     retval->dst_rate = dst_rate;
   783     retval->pre_resample_channels = pre_resample_channels;
   784     retval->packetlen = packetlen;
   785     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
   786 
   787     /* Not resampling? It's an easy conversion (and maybe not even that!). */
   788     if (src_rate == dst_rate) {
   789         retval->cvt_before_resampling.needed = SDL_FALSE;
   790         retval->cvt_before_resampling.len_mult = 1;
   791         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   792             SDL_FreeAudioStream(retval);
   793             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   794         }
   795     } else {
   796         /* Don't resample at first. Just get us to Float32 format. */
   797         /* !!! FIXME: convert to int32 on devices without hardware float. */
   798         if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
   799             SDL_FreeAudioStream(retval);
   800             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   801         }
   802 
   803 #ifdef HAVE_LIBSAMPLERATE_H
   804         SetupLibSampleRateResampling(retval);
   805 #endif
   806 
   807         if (!retval->resampler_func) {
   808             retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
   809             if (!retval->resampler_state) {
   810                 SDL_FreeAudioStream(retval);
   811                 SDL_OutOfMemory();
   812                 return NULL;
   813             }
   814             retval->resampler_func = SDL_ResampleAudioStream;
   815             retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
   816             retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
   817         }
   818 
   819         /* Convert us to the final format after resampling. */
   820         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   821             SDL_FreeAudioStream(retval);
   822             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   823         }
   824     }
   825 
   826     retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
   827     if (!retval->queue) {
   828         SDL_FreeAudioStream(retval);
   829         return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
   830     }
   831 
   832     return retval;
   833 }
   834 
   835 static Uint8 *
   836 EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
   837 {
   838     if (*len < newlen) {
   839         void *ptr = SDL_realloc(*buf, newlen);
   840         if (!ptr) {
   841             SDL_OutOfMemory();
   842             return NULL;
   843         }
   844         *buf = (Uint8 *) ptr;
   845         *len = newlen;
   846     }
   847     return *buf;
   848 }
   849 
   850 int
   851 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
   852 {
   853     int buflen = (int) _buflen;
   854 
   855     if (!stream) {
   856         return SDL_InvalidParamError("stream");
   857     } else if (!buf) {
   858         return SDL_InvalidParamError("buf");
   859     } else if (buflen == 0) {
   860         return 0;  /* nothing to do. */
   861     } else if ((buflen % stream->src_sample_frame_size) != 0) {
   862         return SDL_SetError("Can't add partial sample frames");
   863     }
   864 
   865     if (stream->cvt_before_resampling.needed) {
   866         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   867         Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   868         if (workbuf == NULL) {
   869             return -1;  /* probably out of memory. */
   870         }
   871         SDL_memcpy(workbuf, buf, buflen);
   872         stream->cvt_before_resampling.buf = workbuf;
   873         stream->cvt_before_resampling.len = buflen;
   874         if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
   875             return -1;   /* uhoh! */
   876         }
   877         buf = workbuf;
   878         buflen = stream->cvt_before_resampling.len_cvt;
   879     }
   880 
   881     if (stream->dst_rate != stream->src_rate) {
   882         const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
   883         float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   884         if (workbuf == NULL) {
   885             return -1;  /* probably out of memory. */
   886         }
   887         buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
   888         buf = workbuf;
   889     }
   890 
   891     if (stream->cvt_after_resampling.needed) {
   892         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   893         Uint8 *workbuf;
   894 
   895         if (buf == stream->resample_buffer) {
   896             workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   897         } else {
   898             const int inplace = (buf == stream->work_buffer);
   899             workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   900             if (workbuf && !inplace) {
   901                 SDL_memcpy(workbuf, buf, buflen);
   902             }
   903         }
   904 
   905         if (workbuf == NULL) {
   906             return -1;  /* probably out of memory. */
   907         }
   908 
   909         stream->cvt_after_resampling.buf = workbuf;
   910         stream->cvt_after_resampling.len = buflen;
   911         if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
   912             return -1;   /* uhoh! */
   913         }
   914         buf = workbuf;
   915         buflen = stream->cvt_after_resampling.len_cvt;
   916     }
   917 
   918     return SDL_WriteToDataQueue(stream->queue, buf, buflen);
   919 }
   920 
   921 void
   922 SDL_AudioStreamClear(SDL_AudioStream *stream)
   923 {
   924     if (!stream) {
   925         SDL_InvalidParamError("stream");
   926     } else {
   927         SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
   928         if (stream->reset_resampler_func) {
   929             stream->reset_resampler_func(stream);
   930         }
   931     }
   932 }
   933 
   934 
   935 /* get converted/resampled data from the stream */
   936 int
   937 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
   938 {
   939     if (!stream) {
   940         return SDL_InvalidParamError("stream");
   941     } else if (!buf) {
   942         return SDL_InvalidParamError("buf");
   943     } else if (len == 0) {
   944         return 0;  /* nothing to do. */
   945     } else if ((len % stream->dst_sample_frame_size) != 0) {
   946         return SDL_SetError("Can't request partial sample frames");
   947     }
   948 
   949     return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
   950 }
   951 
   952 /* number of converted/resampled bytes available */
   953 int
   954 SDL_AudioStreamAvailable(SDL_AudioStream *stream)
   955 {
   956     return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
   957 }
   958 
   959 /* dispose of a stream */
   960 void
   961 SDL_FreeAudioStream(SDL_AudioStream *stream)
   962 {
   963     if (stream) {
   964         if (stream->cleanup_resampler_func) {
   965             stream->cleanup_resampler_func(stream);
   966         }
   967         SDL_FreeDataQueue(stream->queue);
   968         SDL_free(stream->work_buffer);
   969         SDL_free(stream->resample_buffer);
   970         SDL_free(stream);
   971     }
   972 }
   973 
   974 /* vi: set ts=4 sw=4 expandtab: */
   975