src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:33:28 -0800
changeset 10737 3406a0f8b041
parent 10591 9717b0804467
child 10756 073957aca821
permissions -rw-r--r--
Updated copyright for 2017
     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 
    30 
    31 /* Effectively mix right and left channels into a single channel */
    32 static void SDLCALL
    33 SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    34 {
    35     float *dst = (float *) cvt->buf;
    36     const float *src = dst;
    37     int i;
    38 
    39     LOG_DEBUG_CONVERT("stereo", "mono");
    40     SDL_assert(format == AUDIO_F32SYS);
    41 
    42     for (i = cvt->len_cvt / 8; i; --i, src += 2) {
    43         *(dst++) = (float) ((((double) src[0]) + ((double) src[1])) * 0.5);
    44     }
    45 
    46     cvt->len_cvt /= 2;
    47     if (cvt->filters[++cvt->filter_index]) {
    48         cvt->filters[cvt->filter_index] (cvt, format);
    49     }
    50 }
    51 
    52 
    53 /* Discard top 4 channels */
    54 static void SDLCALL
    55 SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    56 {
    57     float *dst = (float *) cvt->buf;
    58     const float *src = dst;
    59     int i;
    60 
    61     LOG_DEBUG_CONVERT("6 channels", "stereo");
    62     SDL_assert(format == AUDIO_F32SYS);
    63 
    64     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
    65         dst[0] = src[0];
    66         dst[1] = src[1];
    67     }
    68 
    69     cvt->len_cvt /= 3;
    70     if (cvt->filters[++cvt->filter_index]) {
    71         cvt->filters[cvt->filter_index] (cvt, format);
    72     }
    73 }
    74 
    75 
    76 /* Discard top 2 channels of 6 */
    77 static void SDLCALL
    78 SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
    79 {
    80     float *dst = (float *) cvt->buf;
    81     const float *src = dst;
    82     int i;
    83 
    84     LOG_DEBUG_CONVERT("6 channels", "quad");
    85     SDL_assert(format == AUDIO_F32SYS);
    86 
    87     for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
    88         dst[0] = src[0];
    89         dst[1] = src[1];
    90         dst[2] = src[2];
    91         dst[3] = src[3];
    92     }
    93 
    94     cvt->len_cvt /= 6;
    95     cvt->len_cvt *= 4;
    96     if (cvt->filters[++cvt->filter_index]) {
    97         cvt->filters[cvt->filter_index] (cvt, format);
    98     }
    99 }
   100 
   101 /* Duplicate a mono channel to both stereo channels */
   102 static void SDLCALL
   103 SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   104 {
   105     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   106     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   107     int i;
   108 
   109     LOG_DEBUG_CONVERT("mono", "stereo");
   110     SDL_assert(format == AUDIO_F32SYS);
   111 
   112     for (i = cvt->len_cvt / sizeof (float); i; --i) {
   113         src--;
   114         dst -= 2;
   115         dst[0] = dst[1] = *src;
   116     }
   117 
   118     cvt->len_cvt *= 2;
   119     if (cvt->filters[++cvt->filter_index]) {
   120         cvt->filters[cvt->filter_index] (cvt, format);
   121     }
   122 }
   123 
   124 
   125 /* Duplicate a stereo channel to a pseudo-5.1 stream */
   126 static void SDLCALL
   127 SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   128 {
   129     int i;
   130     float lf, rf, ce;
   131     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   132     float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
   133 
   134     LOG_DEBUG_CONVERT("stereo", "5.1");
   135     SDL_assert(format == AUDIO_F32SYS);
   136 
   137     for (i = cvt->len_cvt / 8; i; --i) {
   138         dst -= 6;
   139         src -= 2;
   140         lf = src[0];
   141         rf = src[1];
   142         ce = (lf * 0.5f) + (rf * 0.5f);
   143         dst[0] = src[0];
   144         dst[1] = src[1];
   145         dst[2] = lf - ce;
   146         dst[3] = rf - ce;
   147         dst[4] = dst[5] = ce;
   148     }
   149 
   150     cvt->len_cvt *= 3;
   151     if (cvt->filters[++cvt->filter_index]) {
   152         cvt->filters[cvt->filter_index] (cvt, format);
   153     }
   154 }
   155 
   156 
   157 /* Duplicate a stereo channel to a pseudo-4.0 stream */
   158 static void SDLCALL
   159 SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   160 {
   161     const float *src = (const float *) (cvt->buf + cvt->len_cvt);
   162     float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
   163     float lf, rf, ce;
   164     int i;
   165 
   166     LOG_DEBUG_CONVERT("stereo", "quad");
   167     SDL_assert(format == AUDIO_F32SYS);
   168 
   169     for (i = cvt->len_cvt / 8; i; --i) {
   170         dst -= 4;
   171         src -= 2;
   172         lf = src[0];
   173         rf = src[1];
   174         ce = (lf / 2) + (rf / 2);
   175         dst[0] = src[0];
   176         dst[1] = src[1];
   177         dst[2] = lf - ce;
   178         dst[3] = rf - ce;
   179     }
   180 
   181     cvt->len_cvt *= 2;
   182     if (cvt->filters[++cvt->filter_index]) {
   183         cvt->filters[cvt->filter_index] (cvt, format);
   184     }
   185 }
   186 
   187 
   188 int
   189 SDL_ConvertAudio(SDL_AudioCVT * cvt)
   190 {
   191     /* !!! FIXME: (cvt) should be const; stack-copy it here. */
   192     /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
   193 
   194     /* Make sure there's data to convert */
   195     if (cvt->buf == NULL) {
   196         return SDL_SetError("No buffer allocated for conversion");
   197     }
   198 
   199     /* Return okay if no conversion is necessary */
   200     cvt->len_cvt = cvt->len;
   201     if (cvt->filters[0] == NULL) {
   202         return 0;
   203     }
   204 
   205     /* Set up the conversion and go! */
   206     cvt->filter_index = 0;
   207     cvt->filters[0] (cvt, cvt->src_format);
   208     return 0;
   209 }
   210 
   211 static void SDLCALL
   212 SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
   213 {
   214 #if DEBUG_CONVERT
   215     printf("Converting byte order\n");
   216 #endif
   217 
   218     switch (SDL_AUDIO_BITSIZE(format)) {
   219         #define CASESWAP(b) \
   220             case b: { \
   221                 Uint##b *ptr = (Uint##b *) cvt->buf; \
   222                 int i; \
   223                 for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
   224                     *ptr = SDL_Swap##b(*ptr); \
   225                 } \
   226                 break; \
   227             }
   228 
   229         CASESWAP(16);
   230         CASESWAP(32);
   231         CASESWAP(64);
   232 
   233         #undef CASESWAP
   234 
   235         default: SDL_assert(!"unhandled byteswap datatype!"); break;
   236     }
   237 
   238     if (cvt->filters[++cvt->filter_index]) {
   239         /* flip endian flag for data. */
   240         if (format & SDL_AUDIO_MASK_ENDIAN) {
   241             format &= ~SDL_AUDIO_MASK_ENDIAN;
   242         } else {
   243             format |= SDL_AUDIO_MASK_ENDIAN;
   244         }
   245         cvt->filters[cvt->filter_index](cvt, format);
   246     }
   247 }
   248 
   249 
   250 static int
   251 SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
   252 {
   253     int retval = 0;  /* 0 == no conversion necessary. */
   254 
   255     if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   256         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   257         retval = 1;  /* added a converter. */
   258     }
   259 
   260     if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
   261         const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
   262         const Uint16 dst_bitsize = 32;
   263         SDL_AudioFilter filter = NULL;
   264 
   265         switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   266             case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
   267             case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
   268             case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
   269             case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
   270             case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
   271             default: SDL_assert(!"Unexpected audio format!"); break;
   272         }
   273 
   274         if (!filter) {
   275             return SDL_SetError("No conversion available for these formats");
   276         }
   277 
   278         cvt->filters[cvt->filter_index++] = filter;
   279         if (src_bitsize < dst_bitsize) {
   280             const int mult = (dst_bitsize / src_bitsize);
   281             cvt->len_mult *= mult;
   282             cvt->len_ratio *= mult;
   283         } else if (src_bitsize > dst_bitsize) {
   284             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   285         }
   286 
   287         retval = 1;  /* added a converter. */
   288     }
   289 
   290     return retval;
   291 }
   292 
   293 static int
   294 SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
   295 {
   296     int retval = 0;  /* 0 == no conversion necessary. */
   297 
   298     if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
   299         const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
   300         const Uint16 src_bitsize = 32;
   301         SDL_AudioFilter filter = NULL;
   302         switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
   303             case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
   304             case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
   305             case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
   306             case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
   307             case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
   308             default: SDL_assert(!"Unexpected audio format!"); break;
   309         }
   310 
   311         if (!filter) {
   312             return SDL_SetError("No conversion available for these formats");
   313         }
   314 
   315         cvt->filters[cvt->filter_index++] = filter;
   316         if (src_bitsize < dst_bitsize) {
   317             const int mult = (dst_bitsize / src_bitsize);
   318             cvt->len_mult *= mult;
   319             cvt->len_ratio *= mult;
   320         } else if (src_bitsize > dst_bitsize) {
   321             cvt->len_ratio /= (src_bitsize / dst_bitsize);
   322         }
   323         retval = 1;  /* added a converter. */
   324     }
   325 
   326     if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
   327         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   328         retval = 1;  /* added a converter. */
   329     }
   330 
   331     return retval;
   332 }
   333 
   334 static int
   335 SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
   336 {
   337     int retval = 0;
   338 
   339     /* If we only built with the arbitrary resamplers, ignore multiples. */
   340     int lo, hi;
   341     int div;
   342 
   343     SDL_assert(src_rate != 0);
   344     SDL_assert(dst_rate != 0);
   345     SDL_assert(src_rate != dst_rate);
   346 
   347     if (src_rate < dst_rate) {
   348         lo = src_rate;
   349         hi = dst_rate;
   350     } else {
   351         lo = dst_rate;
   352         hi = src_rate;
   353     }
   354 
   355     /* zero means "not a supported multiple" ... we only do 2x and 4x. */
   356     if ((hi % lo) != 0)
   357         return 0;               /* not a multiple. */
   358 
   359     div = hi / lo;
   360     retval = ((div == 2) || (div == 4)) ? div : 0;
   361 
   362     return retval;
   363 }
   364 
   365 #define RESAMPLER_FUNCS(chans) \
   366     static void SDLCALL \
   367     SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   368         SDL_assert(format == AUDIO_F32SYS); \
   369         SDL_Upsample_Arbitrary(cvt, chans); \
   370     }\
   371     static void SDLCALL \
   372     SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   373         SDL_assert(format == AUDIO_F32SYS); \
   374         SDL_Downsample_Arbitrary(cvt, chans); \
   375     } \
   376     static void SDLCALL \
   377     SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   378         SDL_assert(format == AUDIO_F32SYS); \
   379         SDL_Upsample_x2(cvt, chans); \
   380     } \
   381     static void SDLCALL \
   382     SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   383         SDL_assert(format == AUDIO_F32SYS); \
   384         SDL_Downsample_Multiple(cvt, 2, chans); \
   385     } \
   386     static void SDLCALL \
   387     SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   388         SDL_assert(format == AUDIO_F32SYS); \
   389         SDL_Upsample_x4(cvt, chans); \
   390     } \
   391     static void SDLCALL \
   392     SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
   393         SDL_assert(format == AUDIO_F32SYS); \
   394         SDL_Downsample_Multiple(cvt, 4, chans); \
   395     }
   396 RESAMPLER_FUNCS(1)
   397 RESAMPLER_FUNCS(2)
   398 RESAMPLER_FUNCS(4)
   399 RESAMPLER_FUNCS(6)
   400 RESAMPLER_FUNCS(8)
   401 #undef RESAMPLER_FUNCS
   402 
   403 static int
   404 SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
   405                           int src_rate, int dst_rate)
   406 {
   407     if (src_rate != dst_rate) {
   408         const int upsample = (src_rate < dst_rate) ? 1 : 0;
   409         const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
   410         SDL_AudioFilter filter = NULL;
   411 
   412         #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
   413             case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
   414             case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
   415             case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
   416             case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
   417             case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
   418             default: break; \
   419         }
   420 
   421         if (upsample) {
   422             if (multiple == 0) {
   423                 PICK_CHANNEL_FILTER(Upsample, Arbitrary);
   424             } else if (multiple == 2) {
   425                 PICK_CHANNEL_FILTER(Upsample, x2);
   426             } else if (multiple == 4) {
   427                 PICK_CHANNEL_FILTER(Upsample, x4);
   428             }
   429         } else {
   430             if (multiple == 0) {
   431                 PICK_CHANNEL_FILTER(Downsample, Arbitrary);
   432             } else if (multiple == 2) {
   433                 PICK_CHANNEL_FILTER(Downsample, x2);
   434             } else if (multiple == 4) {
   435                 PICK_CHANNEL_FILTER(Downsample, x4);
   436             }
   437         }
   438 
   439         #undef PICK_CHANNEL_FILTER
   440 
   441         if (filter == NULL) {
   442             return SDL_SetError("No conversion available for these rates");
   443         }
   444 
   445         /* Update (cvt) with filter details... */
   446         cvt->filters[cvt->filter_index++] = filter;
   447         if (src_rate < dst_rate) {
   448             const double mult = ((double) dst_rate) / ((double) src_rate);
   449             cvt->len_mult *= (int) SDL_ceil(mult);
   450             cvt->len_ratio *= mult;
   451         } else {
   452             cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
   453         }
   454 
   455         return 1;               /* added a converter. */
   456     }
   457 
   458     return 0;                   /* no conversion necessary. */
   459 }
   460 
   461 
   462 /* Creates a set of audio filters to convert from one format to another.
   463    Returns -1 if the format conversion is not supported, 0 if there's
   464    no conversion needed, or 1 if the audio filter is set up.
   465 */
   466 
   467 int
   468 SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
   469                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
   470                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
   471 {
   472     /* Sanity check target pointer */
   473     if (cvt == NULL) {
   474         return SDL_InvalidParamError("cvt");
   475     }
   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     SDL_zerop(cvt);
   499     cvt->src_format = src_fmt;
   500     cvt->dst_format = dst_fmt;
   501     cvt->needed = 0;
   502     cvt->filter_index = 0;
   503     cvt->filters[0] = NULL;
   504     cvt->len_mult = 1;
   505     cvt->len_ratio = 1.0;
   506     cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
   507 
   508     /* Type conversion goes like this now:
   509         - byteswap to CPU native format first if necessary.
   510         - convert to native Float32 if necessary.
   511         - resample and change channel count if necessary.
   512         - convert back to native format.
   513         - byteswap back to foreign format if necessary.
   514 
   515        The expectation is we can process data faster in float32
   516        (possibly with SIMD), and making several passes over the same
   517        buffer in is likely to be CPU cache-friendly, avoiding the
   518        biggest performance hit in modern times. Previously we had
   519        (script-generated) custom converters for every data type and
   520        it was a bloat on SDL compile times and final library size. */
   521 
   522     /* see if we can skip float conversion entirely (just a byteswap needed). */
   523     if ((src_rate == dst_rate) && (src_channels == dst_channels) &&
   524         ((src_fmt != dst_fmt) &&
   525          ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)))) {
   526         cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
   527         cvt->needed = 1;
   528         return 1;
   529     }
   530 
   531     /* Convert data types, if necessary. Updates (cvt). */
   532     if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) == -1) {
   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) ==
   589         -1) {
   590         return -1;              /* shouldn't happen, but just in case... */
   591     }
   592 
   593     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
   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 /* vi: set ts=4 sw=4 expandtab: */
   602