src/audio/sdlgenaudiocvt.pl
author Ryan C. Gordon
Mon, 28 Dec 2009 08:28:24 +0000
changeset 3602 bfa8d34ce03a
parent 3032 77c3e67f0740
child 4889 8c9cbb623d55
permissions -rwxr-xr-x
Fixed buffer overflows in resamplers.

I'm not confident this is a complete fix, but I'm not confident the current
resamplers are really worth keeping at all, either.
     1 #!/usr/bin/perl -w
     2 
     3 use warnings;
     4 use strict;
     5 
     6 my @audiotypes = qw(
     7     U8
     8     S8
     9     U16LSB
    10     S16LSB
    11     U16MSB
    12     S16MSB
    13     S32LSB
    14     S32MSB
    15     F32LSB
    16     F32MSB
    17 );
    18 
    19 my @channels = ( 1, 2, 4, 6, 8 );
    20 my %funcs;
    21 my $custom_converters = 0;
    22 
    23 
    24 sub getTypeConvertHashId {
    25     my ($from, $to) = @_;
    26     return "TYPECONVERTER $from/$to";
    27 }
    28 
    29 
    30 sub getResamplerHashId {
    31     my ($from, $channels, $upsample, $multiple) = @_;
    32     return "RESAMPLER $from/$channels/$upsample/$multiple";
    33 }
    34 
    35 
    36 sub outputHeader {
    37     print <<EOF;
    38 /* DO NOT EDIT!  This file is generated by sdlgenaudiocvt.pl */
    39 /*
    40     SDL - Simple DirectMedia Layer
    41     Copyright (C) 1997-2009 Sam Lantinga
    42 
    43     This library is free software; you can redistribute it and/or
    44     modify it under the terms of the GNU Lesser General Public
    45     License as published by the Free Software Foundation; either
    46     version 2.1 of the License, or (at your option) any later version.
    47 
    48     This library is distributed in the hope that it will be useful,
    49     but WITHOUT ANY WARRANTY; without even the implied warranty of
    50     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    51     Lesser General Public License for more details.
    52 
    53     You should have received a copy of the GNU Lesser General Public
    54     License along with this library; if not, write to the Free Software
    55     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    56 
    57     Sam Lantinga
    58     slouken\@libsdl.org
    59 */
    60 
    61 #include "SDL_config.h"
    62 #include "SDL_audio.h"
    63 #include "SDL_audio_c.h"
    64 
    65 #ifndef DEBUG_CONVERT
    66 #define DEBUG_CONVERT 0
    67 #endif
    68 
    69 
    70 /* If you can guarantee your data and need space, you can eliminate code... */
    71 
    72 /* Just build the arbitrary resamplers if you're saving code space. */
    73 #ifndef LESS_RESAMPLERS
    74 #define LESS_RESAMPLERS 0
    75 #endif
    76 
    77 /* Don't build any resamplers if you're REALLY saving code space. */
    78 #ifndef NO_RESAMPLERS
    79 #define NO_RESAMPLERS 0
    80 #endif
    81 
    82 /* Don't build any type converters if you're saving code space. */
    83 #ifndef NO_CONVERTERS
    84 #define NO_CONVERTERS 0
    85 #endif
    86 
    87 
    88 /* *INDENT-OFF* */
    89 
    90 EOF
    91 
    92     my @vals = ( 127, 32767, 2147483647 );
    93     foreach (@vals) {
    94         my $val = $_;
    95         my $fval = 1.0 / $val;
    96         print("#define DIVBY${val} ${fval}f\n");
    97     }
    98 
    99     print("\n");
   100 }
   101 
   102 sub outputFooter {
   103     print <<EOF;
   104 /* $custom_converters converters generated. */
   105 
   106 /* *INDENT-ON* */
   107 
   108 /* vi: set ts=4 sw=4 expandtab: */
   109 EOF
   110 }
   111 
   112 sub splittype {
   113     my $t = shift;
   114     my ($signed, $size, $endian) = $t =~ /([USF])(\d+)([LM]SB|)/;
   115     my $float = ($signed eq 'F') ? 1 : 0;
   116     $signed = (($float) or ($signed eq 'S')) ? 1 : 0;
   117     $endian = 'NONE' if ($endian eq '');
   118 
   119     my $ctype = '';
   120     if ($float) {
   121         $ctype = (($size == 32) ? 'float' : 'double');
   122     } else {
   123         $ctype = (($signed) ? 'S' : 'U') . "int${size}";
   124     }
   125 
   126     return ($signed, $float, $size, $endian, $ctype);
   127 }
   128 
   129 sub getSwapFunc {
   130     my ($size, $signed, $float, $endian, $val) = @_;
   131     my $BEorLE = (($endian eq 'MSB') ? 'BE' : 'LE');
   132     my $code = '';
   133 
   134     if ($float) {
   135         $code = "SDL_SwapFloat${BEorLE}($val)";
   136     } else {
   137         if ($size > 8) {
   138             $code = "SDL_Swap${BEorLE}${size}($val)";
   139         } else {
   140             $code = $val;
   141         }
   142 
   143         if (($signed) and (!$float)) {
   144             $code = "((Sint${size}) $code)";
   145         }
   146     }
   147 
   148     return "${code}";
   149 }
   150 
   151 
   152 sub maxIntVal {
   153     my $size = shift;
   154     if ($size == 8) {
   155         return 0x7F;
   156     } elsif ($size == 16) {
   157         return 0x7FFF;
   158     } elsif ($size == 32) {
   159         return 0x7FFFFFFF;
   160     }
   161 
   162     die("bug in script.\n");
   163 }
   164 
   165 sub getFloatToIntMult {
   166     my $size = shift;
   167     my $val = maxIntVal($size) . '.0';
   168     $val .= 'f' if ($size < 32);
   169     return $val;
   170 }
   171 
   172 sub getIntToFloatDivBy {
   173     my $size = shift;
   174     return 'DIVBY' . maxIntVal($size);
   175 }
   176 
   177 sub getSignFlipVal {
   178     my $size = shift;
   179     if ($size == 8) {
   180         return '0x80';
   181     } elsif ($size == 16) {
   182         return '0x8000';
   183     } elsif ($size == 32) {
   184         return '0x80000000';
   185     }
   186 
   187     die("bug in script.\n");
   188 }
   189 
   190 sub buildCvtFunc {
   191     my ($from, $to) = @_;
   192     my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   193     my ($tsigned, $tfloat, $tsize, $tendian, $tctype) = splittype($to);
   194     my $diffs = 0;
   195     $diffs++ if ($fsize != $tsize);
   196     $diffs++ if ($fsigned != $tsigned);
   197     $diffs++ if ($ffloat != $tfloat);
   198     $diffs++ if ($fendian ne $tendian);
   199 
   200     return if ($diffs == 0);
   201 
   202     my $hashid = getTypeConvertHashId($from, $to);
   203     if (1) { # !!! FIXME: if ($diffs > 1) {
   204         my $sym = "SDL_Convert_${from}_to_${to}";
   205         $funcs{$hashid} = $sym;
   206         $custom_converters++;
   207 
   208         # Always unsigned for ints, for possible byteswaps.
   209         my $srctype = (($ffloat) ? 'float' : "Uint${fsize}");
   210 
   211         print <<EOF;
   212 static void SDLCALL
   213 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   214 {
   215     int i;
   216     const $srctype *src;
   217     $tctype *dst;
   218 
   219 #if DEBUG_CONVERT
   220     fprintf(stderr, "Converting AUDIO_${from} to AUDIO_${to}.\\n");
   221 #endif
   222 
   223 EOF
   224 
   225         if ($fsize < $tsize) {
   226             my $mult = $tsize / $fsize;
   227             print <<EOF;
   228     src = ((const $srctype *) (cvt->buf + cvt->len_cvt)) - 1;
   229     dst = (($tctype *) (cvt->buf + cvt->len_cvt * $mult)) - 1;
   230     for (i = cvt->len_cvt / sizeof ($srctype); i; --i, --src, --dst) {
   231 EOF
   232         } else {
   233             print <<EOF;
   234     src = (const $srctype *) cvt->buf;
   235     dst = ($tctype *) cvt->buf;
   236     for (i = cvt->len_cvt / sizeof ($srctype); i; --i, ++src, ++dst) {
   237 EOF
   238         }
   239 
   240         # Have to convert to/from float/int.
   241         # !!! FIXME: cast through double for int32<->float?
   242         my $code = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, '*src');
   243         if ($ffloat != $tfloat) {
   244             if ($ffloat) {
   245                 my $mult = getFloatToIntMult($tsize);
   246                 if (!$tsigned) {   # bump from -1.0f/1.0f to 0.0f/2.0f
   247                     $code = "($code + 1.0f)";
   248                 }
   249                 $code = "(($tctype) ($code * $mult))";
   250             } else {
   251                 # $divby will be the reciprocal, to avoid pipeline stalls
   252                 #  from floating point division...so multiply it.
   253                 my $divby = getIntToFloatDivBy($fsize);
   254                 $code = "(((float) $code) * $divby)";
   255                 if (!$fsigned) {   # bump from 0.0f/2.0f to -1.0f/1.0f.
   256                     $code = "($code - 1.0f)";
   257                 }
   258             }
   259         } else {
   260             # All integer conversions here.
   261             if ($fsigned != $tsigned) {
   262                 my $signflipval = getSignFlipVal($fsize);
   263                 $code = "(($code) ^ $signflipval)";
   264             }
   265 
   266             my $shiftval = abs($fsize - $tsize);
   267             if ($fsize < $tsize) {
   268                 $code = "((($tctype) $code) << $shiftval)";
   269             } elsif ($fsize > $tsize) {
   270                 $code = "(($tctype) ($code >> $shiftval))";
   271             }
   272         }
   273 
   274         my $swap = getSwapFunc($tsize, $tsigned, $tfloat, $tendian, 'val');
   275 
   276         print <<EOF;
   277         const $tctype val = $code;
   278         *dst = ${swap};
   279     }
   280 
   281 EOF
   282 
   283         if ($fsize > $tsize) {
   284             my $divby = $fsize / $tsize;
   285             print("    cvt->len_cvt /= $divby;\n");
   286         } elsif ($fsize < $tsize) {
   287             my $mult = $tsize / $fsize;
   288             print("    cvt->len_cvt *= $mult;\n");
   289         }
   290 
   291         print <<EOF;
   292     if (cvt->filters[++cvt->filter_index]) {
   293         cvt->filters[cvt->filter_index] (cvt, AUDIO_$to);
   294     }
   295 }
   296 
   297 EOF
   298 
   299     } else {
   300         if ($fsigned != $tsigned) {
   301             $funcs{$hashid} = 'SDL_ConvertSigned';
   302         } elsif ($ffloat != $tfloat) {
   303             $funcs{$hashid} = 'SDL_ConvertFloat';
   304         } elsif ($fsize != $tsize) {
   305             $funcs{$hashid} = 'SDL_ConvertSize';
   306         } elsif ($fendian ne $tendian) {
   307             $funcs{$hashid} = 'SDL_ConvertEndian';
   308         } else {
   309             die("error in script.\n");
   310         }
   311     }
   312 }
   313 
   314 
   315 sub buildTypeConverters {
   316     print "#if !NO_CONVERTERS\n\n";
   317     foreach (@audiotypes) {
   318         my $from = $_;
   319         foreach (@audiotypes) {
   320             my $to = $_;
   321             buildCvtFunc($from, $to);
   322         }
   323     }
   324     print "#endif  /* !NO_CONVERTERS */\n\n\n";
   325 
   326     print "const SDL_AudioTypeFilters sdl_audio_type_filters[] =\n{\n";
   327     print "#if !NO_CONVERTERS\n";
   328     foreach (@audiotypes) {
   329         my $from = $_;
   330         foreach (@audiotypes) {
   331             my $to = $_;
   332             if ($from ne $to) {
   333                 my $hashid = getTypeConvertHashId($from, $to);
   334                 my $sym = $funcs{$hashid};
   335                 print("    { AUDIO_$from, AUDIO_$to, $sym },\n");
   336             }
   337         }
   338     }
   339     print "#endif  /* !NO_CONVERTERS */\n";
   340 
   341     print("    { 0, 0, NULL }\n");
   342     print "};\n\n\n";
   343 }
   344 
   345 sub getBiggerCtype {
   346     my ($isfloat, $size) = @_;
   347 
   348     if ($isfloat) {
   349         if ($size == 32) {
   350             return 'double';
   351         }
   352         die("bug in script.\n");
   353     }
   354 
   355     if ($size == 8) {
   356         return 'Sint16';
   357     } elsif ($size == 16) {
   358         return 'Sint32'
   359     } elsif ($size == 32) {
   360         return 'Sint64'
   361     }
   362 
   363     die("bug in script.\n");
   364 }
   365 
   366 
   367 # These handle arbitrary resamples...44100Hz to 48000Hz, for example.
   368 # Man, this code is skanky.
   369 sub buildArbitraryResampleFunc {
   370     # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
   371     my ($from, $channels, $upsample) = @_;
   372     my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   373 
   374     my $bigger = getBiggerCtype($ffloat, $fsize);
   375     my $interp = ($ffloat) ? '* 0.5' : '>> 1';
   376 
   377     my $resample = ($upsample) ? 'Upsample' : 'Downsample';
   378     my $hashid = getResamplerHashId($from, $channels, $upsample, 0);
   379     my $sym = "SDL_${resample}_${from}_${channels}c";
   380     $funcs{$hashid} = $sym;
   381     $custom_converters++;
   382 
   383     my $fudge = $fsize * $channels * 2;  # !!! FIXME
   384     my $eps_adjust = ($upsample) ? 'dstsize' : 'srcsize';
   385     my $incr = '';
   386     my $incr2 = '';
   387 
   388 
   389     # !!! FIXME: DEBUG_CONVERT should report frequencies.
   390     print <<EOF;
   391 static void SDLCALL
   392 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   393 {
   394 #if DEBUG_CONVERT
   395     fprintf(stderr, "$resample arbitrary (x%f) AUDIO_${from}, ${channels} channels.\\n", cvt->rate_incr);
   396 #endif
   397 
   398     const int srcsize = cvt->len_cvt - $fudge;
   399     const int dstsize = (int) (((double)cvt->len_cvt) * cvt->rate_incr);
   400     register int eps = 0;
   401 EOF
   402 
   403     # Upsampling (growing the buffer) needs to work backwards, since we
   404     #  overwrite the buffer as we go.
   405     if ($upsample) {
   406         print <<EOF;
   407     $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   408     const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   409     const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   410 EOF
   411     } else {
   412         print <<EOF;
   413     $fctype *dst = ($fctype *) cvt->buf;
   414     const $fctype *src = ($fctype *) cvt->buf;
   415     const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   416 EOF
   417     }
   418 
   419     for (my $i = 0; $i < $channels; $i++) {
   420         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   421         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   422         print <<EOF;
   423     $fctype sample${idx} = $val;
   424 EOF
   425     }
   426 
   427     for (my $i = 0; $i < $channels; $i++) {
   428         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   429         print <<EOF;
   430     $fctype last_sample${idx} = sample${idx};
   431 EOF
   432     }
   433 
   434     print <<EOF;
   435     while (dst != target) {
   436 EOF
   437 
   438     if ($upsample) {
   439         for (my $i = 0; $i < $channels; $i++) {
   440             # !!! FIXME: don't do this swap every write, just when the samples change.
   441             my $idx = (($channels - $i) - 1);
   442             my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}");
   443             print <<EOF;
   444         dst[$idx] = $val;
   445 EOF
   446         }
   447 
   448         $incr = ($channels == 1) ? 'dst--' : "dst -= $channels";
   449         $incr2 = ($channels == 1) ? 'src--' : "src -= $channels";
   450 
   451         print <<EOF;
   452         $incr;
   453         eps += srcsize;
   454         if ((eps << 1) >= dstsize) {
   455             $incr2;
   456 EOF
   457     } else {  # downsample.
   458         $incr = ($channels == 1) ? 'src++' : "src += $channels";
   459         print <<EOF;
   460         $incr;
   461         eps += dstsize;
   462         if ((eps << 1) >= srcsize) {
   463 EOF
   464         for (my $i = 0; $i < $channels; $i++) {
   465             my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}");
   466             print <<EOF;
   467             dst[$i] = $val;
   468 EOF
   469         }
   470 
   471         $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   472         print <<EOF;
   473             $incr;
   474 EOF
   475     }
   476 
   477     for (my $i = 0; $i < $channels; $i++) {
   478         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   479         my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   480         print <<EOF;
   481             sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp);
   482 EOF
   483     }
   484 
   485     for (my $i = 0; $i < $channels; $i++) {
   486         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   487         print <<EOF;
   488             last_sample${idx} = sample${idx};
   489 EOF
   490     }
   491 
   492     print <<EOF;
   493             eps -= $eps_adjust;
   494         }
   495     }
   496 EOF
   497 
   498         print <<EOF;
   499     cvt->len_cvt = dstsize;
   500     if (cvt->filters[++cvt->filter_index]) {
   501         cvt->filters[cvt->filter_index] (cvt, format);
   502     }
   503 }
   504 
   505 EOF
   506 
   507 }
   508 
   509 # These handle clean resamples...doubling and quadrupling the sample rate, etc.
   510 sub buildMultipleResampleFunc {
   511     # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
   512     my ($from, $channels, $upsample, $multiple) = @_;
   513     my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   514 
   515     my $bigger = getBiggerCtype($ffloat, $fsize);
   516     my $interp = ($ffloat) ? '* 0.5' : '>> 1';
   517     my $interp2 = ($ffloat) ? '* 0.25' : '>> 2';
   518     my $mult3 = ($ffloat) ? '3.0' : '3';
   519     my $lencvtop = ($upsample) ? '*' : '/';
   520 
   521     my $resample = ($upsample) ? 'Upsample' : 'Downsample';
   522     my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple);
   523     my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}";
   524     $funcs{$hashid} = $sym;
   525     $custom_converters++;
   526 
   527     # !!! FIXME: DEBUG_CONVERT should report frequencies.
   528     print <<EOF;
   529 static void SDLCALL
   530 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   531 {
   532 #if DEBUG_CONVERT
   533     fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n");
   534 #endif
   535 
   536     const int srcsize = cvt->len_cvt;
   537     const int dstsize = cvt->len_cvt $lencvtop $multiple;
   538 EOF
   539 
   540     my $endcomparison = '!=';
   541 
   542     # Upsampling (growing the buffer) needs to work backwards, since we
   543     #  overwrite the buffer as we go.
   544     if ($upsample) {
   545         $endcomparison = '>';  # dst > target
   546         print <<EOF;
   547     $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   548     const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   549     const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   550 EOF
   551     } else {
   552         $endcomparison = '<';  # dst < target
   553         print <<EOF;
   554     $fctype *dst = ($fctype *) cvt->buf;
   555     const $fctype *src = ($fctype *) cvt->buf;
   556     const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   557 EOF
   558     }
   559 
   560     for (my $i = 0; $i < $channels; $i++) {
   561         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   562         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   563         print <<EOF;
   564     $bigger last_sample${idx} = ($bigger) $val;
   565 EOF
   566     }
   567 
   568     print <<EOF;
   569     while (dst $endcomparison target) {
   570 EOF
   571 
   572     for (my $i = 0; $i < $channels; $i++) {
   573         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   574         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   575         print <<EOF;
   576         const $bigger sample${idx} = ($bigger) $val;
   577 EOF
   578     }
   579 
   580     my $incr = '';
   581     if ($upsample) {
   582         $incr = ($channels == 1) ? 'src--' : "src -= $channels";
   583     } else {
   584         my $amount = $channels * $multiple;
   585         $incr = "src += $amount";  # can't ever be 1, so no "++" version.
   586     }
   587 
   588 
   589     print <<EOF;
   590         $incr;
   591 EOF
   592 
   593     # !!! FIXME: This really begs for some Altivec or SSE, etc.
   594     if ($upsample) {
   595         if ($multiple == 2) {
   596             for (my $i = $channels-1; $i >= 0; $i--) {
   597                 my $dsti = $i + $channels;
   598                 print <<EOF;
   599         dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   600 EOF
   601             }
   602             for (my $i = $channels-1; $i >= 0; $i--) {
   603                 my $dsti = $i;
   604                 print <<EOF;
   605         dst[$dsti] = ($fctype) sample${i};
   606 EOF
   607             }
   608         } elsif ($multiple == 4) {
   609             for (my $i = $channels-1; $i >= 0; $i--) {
   610                 my $dsti = $i + ($channels * 3);
   611                 print <<EOF;
   612         dst[$dsti] = ($fctype) sample${i};
   613 EOF
   614             }
   615 
   616             for (my $i = $channels-1; $i >= 0; $i--) {
   617                 my $dsti = $i + ($channels * 2);
   618                 print <<EOF;
   619         dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2);
   620 EOF
   621             }
   622 
   623             for (my $i = $channels-1; $i >= 0; $i--) {
   624                 my $dsti = $i + ($channels * 1);
   625                 print <<EOF;
   626         dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   627 EOF
   628             }
   629 
   630             for (my $i = $channels-1; $i >= 0; $i--) {
   631                 my $dsti = $i + ($channels * 0);
   632                 print <<EOF;
   633         dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2);
   634 EOF
   635             }
   636         } else {
   637             die('bug in program.');  # we only handle x2 and x4.
   638         }
   639     } else {  # downsample.
   640         if ($multiple == 2) {
   641             for (my $i = 0; $i < $channels; $i++) {
   642                 print <<EOF;
   643         dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   644 EOF
   645             }
   646         } elsif ($multiple == 4) {
   647             # !!! FIXME: interpolate all 4 samples?
   648             for (my $i = 0; $i < $channels; $i++) {
   649                 print <<EOF;
   650         dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   651 EOF
   652             }
   653         } else {
   654             die('bug in program.');  # we only handle x2 and x4.
   655         }
   656     }
   657 
   658     for (my $i = 0; $i < $channels; $i++) {
   659         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   660         print <<EOF;
   661         last_sample${idx} = sample${idx};
   662 EOF
   663     }
   664 
   665     if ($upsample) {
   666         my $amount = $channels * $multiple;
   667         $incr = "dst -= $amount";  # can't ever be 1, so no "--" version.
   668     } else {
   669         $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   670     }
   671 
   672     print <<EOF;
   673         $incr;
   674     }
   675 
   676     cvt->len_cvt = dstsize;
   677     if (cvt->filters[++cvt->filter_index]) {
   678         cvt->filters[cvt->filter_index] (cvt, format);
   679     }
   680 }
   681 
   682 EOF
   683 
   684 }
   685 
   686 sub buildResamplers {
   687     print "#if !NO_RESAMPLERS\n\n";
   688     foreach (@audiotypes) {
   689         my $from = $_;
   690         foreach (@channels) {
   691             my $channel = $_;
   692             buildArbitraryResampleFunc($from, $channel, 1);
   693             buildArbitraryResampleFunc($from, $channel, 0);
   694         }
   695     }
   696 
   697     print "\n#if !LESS_RESAMPLERS\n\n";
   698     foreach (@audiotypes) {
   699         my $from = $_;
   700         foreach (@channels) {
   701             my $channel = $_;
   702             for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
   703                 buildMultipleResampleFunc($from, $channel, 1, $multiple);
   704                 buildMultipleResampleFunc($from, $channel, 0, $multiple);
   705             }
   706         }
   707     }
   708 
   709     print "#endif  /* !LESS_RESAMPLERS */\n";
   710     print "#endif  /* !NO_RESAMPLERS */\n\n\n";
   711 
   712     print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n";
   713     print "#if !NO_RESAMPLERS\n";
   714     foreach (@audiotypes) {
   715         my $from = $_;
   716         foreach (@channels) {
   717             my $channel = $_;
   718             for (my $upsample = 0; $upsample <= 1; $upsample++) {
   719                 my $hashid = getResamplerHashId($from, $channel, $upsample, 0);
   720                 my $sym = $funcs{$hashid};
   721                 print("    { AUDIO_$from, $channel, $upsample, 0, $sym },\n");
   722             }
   723         }
   724     }
   725 
   726     print "#if !LESS_RESAMPLERS\n";
   727     foreach (@audiotypes) {
   728         my $from = $_;
   729         foreach (@channels) {
   730             my $channel = $_;
   731             for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
   732                 for (my $upsample = 0; $upsample <= 1; $upsample++) {
   733                     my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple);
   734                     my $sym = $funcs{$hashid};
   735                     print("    { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n");
   736                 }
   737             }
   738         }
   739     }
   740 
   741     print "#endif  /* !LESS_RESAMPLERS */\n";
   742     print "#endif  /* !NO_RESAMPLERS */\n";
   743     print("    { 0, 0, 0, 0, NULL }\n");
   744     print "};\n\n";
   745 }
   746 
   747 
   748 # mainline ...
   749 
   750 outputHeader();
   751 buildTypeConverters();
   752 buildResamplers();
   753 outputFooter();
   754 
   755 exit 0;
   756 
   757 # end of sdlgenaudiocvt.pl ...
   758