src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 09 Jan 2017 20:37:52 -0800
changeset 10805 30a00d960dc9
parent 10804 1502bb751ca4
child 10814 938218064f67
permissions -rw-r--r--
Fixed bug 3552 - Building SDL in release mode fails under VS 2017 RC

Lukasz Biel

Tried to compile SDL2 using newest version of VS.

Got:
SDL_audiocvt.obj : error LNK2019: unresolved external symbol memcpy referenced in function SDL_ResampleCVT
1>E:\Users\dotPo\Lib\SDL\VisualC\x64\Release\SDL2.dll : fatal error LNK1120: 1 unresolved externals

whole compilation process: http://pastebin.com/eWDAvBce

Steps to reproduce:
clone http://hg.libsdl.org/SDL using tortoise hg,
open SDL\VisualC\SDL.sln,
when promted if should retarget solution click ok,
select release x64 build type,
Build/Build Solution

attempt 2, using Visual Studio cmake support:
open folder SDL\
select release x64 build type,
run CMake\Build CMakeLists.txt
build fails

When switched to debug build type, buils succeeds in both cases.
VS 2017 is still beta.
     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++) = (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 /* 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 * sizeof(float);
   200     const int total = (inbuflen / framelen);
   201     const int finalpos = total - chans;
   202     const double src_incr = 1.0 / rate_incr;
   203     double idx = 0.0;
   204     float *dst = outbuf;
   205     int consumed = 0;
   206     int i;
   207 
   208     SDL_assert((inbuflen % framelen) == 0);
   209 
   210     while (consumed < total) {
   211         const int pos = ((int)idx) * chans;
   212         const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
   213         SDL_assert(dst < (outbuf + (outbuflen / framelen)));
   214         for (i = 0; i < chans; i++) {
   215             const float val = *(src++);
   216             *(dst++) = (val + last_sample[i]) * 0.5f;
   217             last_sample[i] = val;
   218         }
   219         consumed = pos + chans;
   220         idx += src_incr;
   221     }
   222 
   223     return (int)((dst - outbuf) * sizeof(float));
   224 }
   225 
   226 
   227 int
   228 SDL_ConvertAudio(SDL_AudioCVT * cvt)
   229 {
   230     /* !!! FIXME: (cvt) should be const; stack-copy it here. */
   231     /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
   232 
   233     /* Make sure there's data to convert */
   234     if (cvt->buf == NULL) {
   235         return SDL_SetError("No buffer allocated for conversion");
   236     }
   237 
   238     /* Return okay if no conversion is necessary */
   239     cvt->len_cvt = cvt->len;
   240     if (cvt->filters[0] == NULL) {
   241         return 0;
   242     }
   243 
   244     /* Set up the conversion and go! */
   245     cvt->filter_index = 0;
   246     cvt->filters[0] (cvt, cvt->src_format);
   247     return 0;
   248 }
   249 
   250 static void SDLCALL
   251 SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   252 {
   253 #if DEBUG_CONVERT
   254     printf("Converting byte order\n");
   255 #endif
   256 
   257     switch (SDL_AUDIO_BITSIZE(format)) {
   258         #define CASESWAP(b) \
   259             case b: { \
   260                 Uint##b *ptr = (Uint##b *) cvt->buf; \
   261                 int i; \
   262                 for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
   263                     *ptr = SDL_Swap##b(*ptr); \
   264                 } \
   265                 break; \
   266             }
   267 
   268         CASESWAP(16);
   269         CASESWAP(32);
   270         CASESWAP(64);
   271 
   272         #undef CASESWAP
   273 
   274         default: SDL_assert(!"unhandled byteswap datatype!"); break;
   275     }
   276 
   277     if (cvt->filters[++cvt->filter_index]) {
   278         /* flip endian flag for data. */
   279         if (format & SDL_AUDIO_MASK_ENDIAN) {
   280             format &= ~SDL_AUDIO_MASK_ENDIAN;
   281         } else {
   282             format |= SDL_AUDIO_MASK_ENDIAN;
   283         }
   284         cvt->filters[cvt->filter_index](cvt, format);
   285     }
   286 }
   287 
   288 
   289 static int
   290 SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
   291 {
   292     int retval = 0;  /* 0 == no conversion necessary. */
   293 
   294     if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   295         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   296         retval = 1;  /* added a converter. */
   297     }
   298 
   299     if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
   300         const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
   301         const Uint16 dst_bitsize = 32;
   302         SDL_AudioFilter filter = NULL;
   303 
   304         switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   305             case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
   306             case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
   307             case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
   308             case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
   309             case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
   310             default: SDL_assert(!"Unexpected audio format!"); break;
   311         }
   312 
   313         if (!filter) {
   314             return SDL_SetError("No conversion available for these formats");
   315         }
   316 
   317         cvt->filters[cvt->filter_index++] = filter;
   318         if (src_bitsize < dst_bitsize) {
   319             const int mult = (dst_bitsize / src_bitsize);
   320             cvt->len_mult *= mult;
   321             cvt->len_ratio *= mult;
   322         } else if (src_bitsize > dst_bitsize) {
   323             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   324         }
   325 
   326         retval = 1;  /* added a converter. */
   327     }
   328 
   329     return retval;
   330 }
   331 
   332 static int
   333 SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
   334 {
   335     int retval = 0;  /* 0 == no conversion necessary. */
   336 
   337     if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
   338         const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
   339         const Uint16 src_bitsize = 32;
   340         SDL_AudioFilter filter = NULL;
   341         switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   342             case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
   343             case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
   344             case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
   345             case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
   346             case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
   347             default: SDL_assert(!"Unexpected audio format!"); break;
   348         }
   349 
   350         if (!filter) {
   351             return SDL_SetError("No conversion available for these formats");
   352         }
   353 
   354         cvt->filters[cvt->filter_index++] = filter;
   355         if (src_bitsize < dst_bitsize) {
   356             const int mult = (dst_bitsize / src_bitsize);
   357             cvt->len_mult *= mult;
   358             cvt->len_ratio *= mult;
   359         } else if (src_bitsize > dst_bitsize) {
   360             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   361         }
   362         retval = 1;  /* added a converter. */
   363     }
   364 
   365     if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   366         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   367         retval = 1;  /* added a converter. */
   368     }
   369 
   370     return retval;
   371 }
   372 
   373 static void
   374 SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
   375 {
   376     const float *src = (const float *) cvt->buf;
   377     const int srclen = cvt->len_cvt;
   378     float *dst = (float *) (cvt->buf + srclen);
   379     const int dstlen = (cvt->len * cvt->len_mult) - srclen;
   380     float state[8];
   381 
   382     SDL_assert(format == AUDIO_F32SYS);
   383 
   384     SDL_memcpy(state, src, chans*sizeof(*src));
   385 
   386     cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
   387 
   388     SDL_memcpy(cvt->buf, dst, cvt->len_cvt);
   389     if (cvt->filters[++cvt->filter_index]) {
   390         cvt->filters[cvt->filter_index](cvt, format);
   391     }
   392 }
   393 
   394 /* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
   395    !!! FIXME:  store channel info, so we have to have function entry
   396    !!! FIXME:  points for each supported channel count and multiple
   397    !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
   398 #define RESAMPLER_FUNCS(chans) \
   399     static void SDLCALL \
   400     SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   401         SDL_ResampleCVT(cvt, chans, format); \
   402     }
   403 RESAMPLER_FUNCS(1)
   404 RESAMPLER_FUNCS(2)
   405 RESAMPLER_FUNCS(4)
   406 RESAMPLER_FUNCS(6)
   407 RESAMPLER_FUNCS(8)
   408 #undef RESAMPLER_FUNCS
   409 
   410 static SDL_AudioFilter
   411 ChooseCVTResampler(const int dst_channels)
   412 {
   413     switch (dst_channels) {
   414         case 1: return SDL_ResampleCVT_c1;
   415         case 2: return SDL_ResampleCVT_c2;
   416         case 4: return SDL_ResampleCVT_c4;
   417         case 6: return SDL_ResampleCVT_c6;
   418         case 8: return SDL_ResampleCVT_c8;
   419         default: break;
   420     }
   421 
   422     return NULL;
   423 }
   424 
   425 static int
   426 SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
   427                           const int src_rate, const int dst_rate)
   428 {
   429     SDL_AudioFilter filter;
   430 
   431     if (src_rate == dst_rate) {
   432         return 0;  /* no conversion necessary. */
   433     }
   434 
   435     filter = ChooseCVTResampler(dst_channels);
   436     if (filter == NULL) {
   437         return SDL_SetError("No conversion available for these rates");
   438     }
   439 
   440     /* Update (cvt) with filter details... */
   441     cvt->filters[cvt->filter_index++] = filter;
   442     if (src_rate < dst_rate) {
   443         const double mult = ((double) dst_rate) / ((double) src_rate);
   444         cvt->len_mult *= (int) SDL_ceil(mult);
   445         cvt->len_ratio *= mult;
   446     } else {
   447         cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   448     }
   449 
   450     /* the buffer is big enough to hold the destination now, but
   451        we need it large enough to hold a separate scratch buffer. */
   452     cvt->len_mult *= 2;
   453 
   454     return 1;               /* added a converter. */
   455 }
   456 
   457 
   458 /* Creates a set of audio filters to convert from one format to another.
   459    Returns -1 if the format conversion is not supported, 0 if there's
   460    no conversion needed, or 1 if the audio filter is set up.
   461 */
   462 
   463 int
   464 SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
   465                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
   466                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
   467 {
   468     /* Sanity check target pointer */
   469     if (cvt == NULL) {
   470         return SDL_InvalidParamError("cvt");
   471     }
   472 
   473     /* Make sure we zero out the audio conversion before error checking */
   474     SDL_zerop(cvt);
   475 
   476     /* there are no unsigned types over 16 bits, so catch this up front. */
   477     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
   478         return SDL_SetError("Invalid source format");
   479     }
   480     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
   481         return SDL_SetError("Invalid destination format");
   482     }
   483 
   484     /* prevent possible divisions by zero, etc. */
   485     if ((src_channels == 0) || (dst_channels == 0)) {
   486         return SDL_SetError("Source or destination channels is zero");
   487     }
   488     if ((src_rate == 0) || (dst_rate == 0)) {
   489         return SDL_SetError("Source or destination rate is zero");
   490     }
   491 #if DEBUG_CONVERT
   492     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
   493            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
   494 #endif
   495 
   496     /* Start off with no conversion necessary */
   497     cvt->src_format = src_fmt;
   498     cvt->dst_format = dst_fmt;
   499     cvt->needed = 0;
   500     cvt->filter_index = 0;
   501     cvt->filters[0] = NULL;
   502     cvt->len_mult = 1;
   503     cvt->len_ratio = 1.0;
   504     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   505 
   506     /* Type conversion goes like this now:
   507         - byteswap to CPU native format first if necessary.
   508         - convert to native Float32 if necessary.
   509         - resample and change channel count if necessary.
   510         - convert back to native format.
   511         - byteswap back to foreign format if necessary.
   512 
   513        The expectation is we can process data faster in float32
   514        (possibly with SIMD), and making several passes over the same
   515        buffer is likely to be CPU cache-friendly, avoiding the
   516        biggest performance hit in modern times. Previously we had
   517        (script-generated) custom converters for every data type and
   518        it was a bloat on SDL compile times and final library size. */
   519 
   520     /* see if we can skip float conversion entirely. */
   521     if (src_rate == dst_rate && src_channels == dst_channels) {
   522         if (src_fmt == dst_fmt) {
   523             return 0;
   524         }
   525 
   526         /* just a byteswap needed? */
   527         if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
   528             cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   529             cvt->needed = 1;
   530             return 1;
   531         }
   532     }
   533 
   534     /* Convert data types, if necessary. Updates (cvt). */
   535     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
   536         return -1;              /* shouldn't happen, but just in case... */
   537     }
   538 
   539     /* Channel conversion */
   540     if (src_channels != dst_channels) {
   541         if ((src_channels == 1) && (dst_channels > 1)) {
   542             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   543             cvt->len_mult *= 2;
   544             src_channels = 2;
   545             cvt->len_ratio *= 2;
   546         }
   547         if ((src_channels == 2) && (dst_channels == 6)) {
   548             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
   549             src_channels = 6;
   550             cvt->len_mult *= 3;
   551             cvt->len_ratio *= 3;
   552         }
   553         if ((src_channels == 2) && (dst_channels == 4)) {
   554             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
   555             src_channels = 4;
   556             cvt->len_mult *= 2;
   557             cvt->len_ratio *= 2;
   558         }
   559         while ((src_channels * 2) <= dst_channels) {
   560             cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
   561             cvt->len_mult *= 2;
   562             src_channels *= 2;
   563             cvt->len_ratio *= 2;
   564         }
   565         if ((src_channels == 6) && (dst_channels <= 2)) {
   566             cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
   567             src_channels = 2;
   568             cvt->len_ratio /= 3;
   569         }
   570         if ((src_channels == 6) && (dst_channels == 4)) {
   571             cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
   572             src_channels = 4;
   573             cvt->len_ratio /= 2;
   574         }
   575         /* This assumes that 4 channel audio is in the format:
   576            Left {front/back} + Right {front/back}
   577            so converting to L/R stereo works properly.
   578          */
   579         while (((src_channels % 2) == 0) &&
   580                ((src_channels / 2) >= dst_channels)) {
   581             cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono;
   582             src_channels /= 2;
   583             cvt->len_ratio /= 2;
   584         }
   585         if (src_channels != dst_channels) {
   586             /* Uh oh.. */ ;
   587         }
   588     }
   589 
   590     /* Do rate conversion, if necessary. Updates (cvt). */
   591     if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
   592         return -1;              /* shouldn't happen, but just in case... */
   593     }
   594 
   595     /* Move to final data type. */
   596     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
   597         return -1;              /* shouldn't happen, but just in case... */
   598     }
   599 
   600     cvt->needed = (cvt->filter_index != 0);
   601     return (cvt->needed);
   602 }
   603 
   604 typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
   605 typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   606 typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
   607 
   608 struct SDL_AudioStream
   609 {
   610     SDL_AudioCVT cvt_before_resampling;
   611     SDL_AudioCVT cvt_after_resampling;
   612     SDL_DataQueue *queue;
   613     Uint8 *work_buffer;
   614     int work_buffer_len;
   615     Uint8 *resample_buffer;
   616     int resample_buffer_len;
   617     int src_sample_frame_size;
   618     SDL_AudioFormat src_format;
   619     Uint8 src_channels;
   620     int src_rate;
   621     int dst_sample_frame_size;
   622     SDL_AudioFormat dst_format;
   623     Uint8 dst_channels;
   624     int dst_rate;
   625     double rate_incr;
   626     Uint8 pre_resample_channels;
   627     int packetlen;
   628     void *resampler_state;
   629     SDL_ResampleAudioStreamFunc resampler_func;
   630     SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
   631     SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
   632 };
   633 
   634 #ifdef HAVE_LIBSAMPLERATE_H
   635 static int
   636 SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   637 {
   638     const int framelen = sizeof(float) * stream->pre_resample_channels;
   639     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   640     SRC_DATA data;
   641     int result;
   642 
   643     data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
   644     data.input_frames = inbuflen / framelen;
   645     data.input_frames_used = 0;
   646 
   647     data.data_out = outbuf;
   648     data.output_frames = outbuflen / framelen;
   649 
   650     data.end_of_input = 0;
   651     data.src_ratio = stream->rate_incr;
   652 
   653     result = SRC_src_process(state, &data);
   654     if (result != 0) {
   655         SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
   656         return 0;
   657     }
   658 
   659     /* If this fails, we need to store them off somewhere */
   660     SDL_assert(data.input_frames_used == data.input_frames);
   661 
   662     return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
   663 }
   664 
   665 static void
   666 SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
   667 {
   668     SRC_src_reset((SRC_STATE *)stream->resampler_state);
   669 }
   670 
   671 static void
   672 SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
   673 {
   674     SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
   675     if (state) {
   676         SRC_src_delete(state);
   677     }
   678 
   679     stream->resampler_state = NULL;
   680     stream->resampler_func = NULL;
   681     stream->reset_resampler_func = NULL;
   682     stream->cleanup_resampler_func = NULL;
   683 }
   684 
   685 static SDL_bool
   686 SetupLibSampleRateResampling(SDL_AudioStream *stream)
   687 {
   688     int result = 0;
   689     SRC_STATE *state = NULL;
   690 
   691     if (SRC_available) {
   692         state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
   693         if (!state) {
   694             SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
   695         }
   696     }
   697 
   698     if (!state) {
   699         SDL_CleanupAudioStreamResampler_SRC(stream);
   700         return SDL_FALSE;
   701     }
   702 
   703     stream->resampler_state = state;
   704     stream->resampler_func = SDL_ResampleAudioStream_SRC;
   705     stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
   706     stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
   707 
   708     return SDL_TRUE;
   709 }
   710 #endif /* HAVE_LIBSAMPLERATE_H */
   711 
   712 
   713 typedef struct
   714 {
   715     SDL_bool resampler_seeded;
   716     float resampler_state[8];
   717 } SDL_AudioStreamResamplerState;
   718 
   719 static int
   720 SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
   721 {
   722     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   723     const int chans = (int)stream->pre_resample_channels;
   724 
   725     SDL_assert(chans <= SDL_arraysize(state->resampler_state));
   726 
   727     if (!state->resampler_seeded) {
   728         int i;
   729         for (i = 0; i < chans; i++) {
   730             state->resampler_state[i] = inbuf[i];
   731         }
   732         state->resampler_seeded = SDL_TRUE;
   733     }
   734 
   735     return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
   736 }
   737 
   738 static void
   739 SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
   740 {
   741     SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
   742     state->resampler_seeded = SDL_FALSE;
   743 }
   744 
   745 static void
   746 SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
   747 {
   748     SDL_free(stream->resampler_state);
   749 }
   750 
   751 SDL_AudioStream *
   752 SDL_NewAudioStream(const SDL_AudioFormat src_format,
   753                    const Uint8 src_channels,
   754                    const int src_rate,
   755                    const SDL_AudioFormat dst_format,
   756                    const Uint8 dst_channels,
   757                    const int dst_rate)
   758 {
   759     const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
   760     Uint8 pre_resample_channels;
   761     SDL_AudioStream *retval;
   762 
   763     retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
   764     if (!retval) {
   765         return NULL;
   766     }
   767 
   768     /* If increasing channels, do it after resampling, since we'd just
   769        do more work to resample duplicate channels. If we're decreasing, do
   770        it first so we resample the interpolated data instead of interpolating
   771        the resampled data (!!! FIXME: decide if that works in practice, though!). */
   772     pre_resample_channels = SDL_min(src_channels, dst_channels);
   773 
   774     retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
   775     retval->src_format = src_format;
   776     retval->src_channels = src_channels;
   777     retval->src_rate = src_rate;
   778     retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
   779     retval->dst_format = dst_format;
   780     retval->dst_channels = dst_channels;
   781     retval->dst_rate = dst_rate;
   782     retval->pre_resample_channels = pre_resample_channels;
   783     retval->packetlen = packetlen;
   784     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
   785 
   786     /* Not resampling? It's an easy conversion (and maybe not even that!). */
   787     if (src_rate == dst_rate) {
   788         retval->cvt_before_resampling.needed = SDL_FALSE;
   789         retval->cvt_before_resampling.len_mult = 1;
   790         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   791             SDL_FreeAudioStream(retval);
   792             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   793         }
   794     } else {
   795         /* Don't resample at first. Just get us to Float32 format. */
   796         /* !!! FIXME: convert to int32 on devices without hardware float. */
   797         if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
   798             SDL_FreeAudioStream(retval);
   799             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   800         }
   801 
   802 #ifdef HAVE_LIBSAMPLERATE_H
   803         SetupLibSampleRateResampling(retval);
   804 #endif
   805 
   806         if (!retval->resampler_func) {
   807             retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
   808             if (!retval->resampler_state) {
   809                 SDL_FreeAudioStream(retval);
   810                 SDL_OutOfMemory();
   811                 return NULL;
   812             }
   813             retval->resampler_func = SDL_ResampleAudioStream;
   814             retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
   815             retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
   816         }
   817 
   818         /* Convert us to the final format after resampling. */
   819         if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
   820             SDL_FreeAudioStream(retval);
   821             return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
   822         }
   823     }
   824 
   825     retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
   826     if (!retval->queue) {
   827         SDL_FreeAudioStream(retval);
   828         return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
   829     }
   830 
   831     return retval;
   832 }
   833 
   834 static Uint8 *
   835 EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
   836 {
   837     if (*len < newlen) {
   838         void *ptr = SDL_realloc(*buf, newlen);
   839         if (!ptr) {
   840             SDL_OutOfMemory();
   841             return NULL;
   842         }
   843         *buf = (Uint8 *) ptr;
   844         *len = newlen;
   845     }
   846     return *buf;
   847 }
   848 
   849 int
   850 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
   851 {
   852     int buflen = (int) _buflen;
   853 
   854     if (!stream) {
   855         return SDL_InvalidParamError("stream");
   856     } else if (!buf) {
   857         return SDL_InvalidParamError("buf");
   858     } else if (buflen == 0) {
   859         return 0;  /* nothing to do. */
   860     } else if ((buflen % stream->src_sample_frame_size) != 0) {
   861         return SDL_SetError("Can't add partial sample frames");
   862     }
   863 
   864     if (stream->cvt_before_resampling.needed) {
   865         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   866         Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   867         if (workbuf == NULL) {
   868             return -1;  /* probably out of memory. */
   869         }
   870         SDL_memcpy(workbuf, buf, buflen);
   871         stream->cvt_before_resampling.buf = workbuf;
   872         stream->cvt_before_resampling.len = buflen;
   873         if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
   874             return -1;   /* uhoh! */
   875         }
   876         buf = workbuf;
   877         buflen = stream->cvt_before_resampling.len_cvt;
   878     }
   879 
   880     if (stream->dst_rate != stream->src_rate) {
   881         const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
   882         float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   883         if (workbuf == NULL) {
   884             return -1;  /* probably out of memory. */
   885         }
   886         buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
   887         buf = workbuf;
   888     }
   889 
   890     if (stream->cvt_after_resampling.needed) {
   891         const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
   892         Uint8 *workbuf;
   893 
   894         if (buf == stream->resample_buffer) {
   895             workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
   896         } else {
   897             const int inplace = (buf == stream->work_buffer);
   898             workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
   899             if (workbuf && !inplace) {
   900                 SDL_memcpy(workbuf, buf, buflen);
   901             }
   902         }
   903 
   904         if (workbuf == NULL) {
   905             return -1;  /* probably out of memory. */
   906         }
   907 
   908         stream->cvt_after_resampling.buf = workbuf;
   909         stream->cvt_after_resampling.len = buflen;
   910         if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
   911             return -1;   /* uhoh! */
   912         }
   913         buf = workbuf;
   914         buflen = stream->cvt_after_resampling.len_cvt;
   915     }
   916 
   917     return SDL_WriteToDataQueue(stream->queue, buf, buflen);
   918 }
   919 
   920 void
   921 SDL_AudioStreamClear(SDL_AudioStream *stream)
   922 {
   923     if (!stream) {
   924         SDL_InvalidParamError("stream");
   925     } else {
   926         SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
   927         if (stream->reset_resampler_func) {
   928             stream->reset_resampler_func(stream);
   929         }
   930     }
   931 }
   932 
   933 
   934 /* get converted/resampled data from the stream */
   935 int
   936 SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
   937 {
   938     if (!stream) {
   939         return SDL_InvalidParamError("stream");
   940     } else if (!buf) {
   941         return SDL_InvalidParamError("buf");
   942     } else if (len == 0) {
   943         return 0;  /* nothing to do. */
   944     } else if ((len % stream->dst_sample_frame_size) != 0) {
   945         return SDL_SetError("Can't request partial sample frames");
   946     }
   947 
   948     return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
   949 }
   950 
   951 /* number of converted/resampled bytes available */
   952 int
   953 SDL_AudioStreamAvailable(SDL_AudioStream *stream)
   954 {
   955     return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
   956 }
   957 
   958 /* dispose of a stream */
   959 void
   960 SDL_FreeAudioStream(SDL_AudioStream *stream)
   961 {
   962     if (stream) {
   963         if (stream->cleanup_resampler_func) {
   964             stream->cleanup_resampler_func(stream);
   965         }
   966         SDL_FreeDataQueue(stream->queue);
   967         SDL_free(stream->work_buffer);
   968         SDL_free(stream->resample_buffer);
   969         SDL_free(stream);
   970     }
   971 }
   972 
   973 /* vi: set ts=4 sw=4 expandtab: */
   974