src/audio/sdlgenaudiocvt.pl
author Sam Lantinga
Fri, 11 Feb 2011 22:37:15 -0800
changeset 5262 b530ef003506
parent 4889 8c9cbb623d55
child 5535 96594ac5fd1a
permissions -rwxr-xr-x
Happy 2011! :)
     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-2011 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     my $endcomparison = '!=';
   404 
   405     # Upsampling (growing the buffer) needs to work backwards, since we
   406     #  overwrite the buffer as we go.
   407     if ($upsample) {
   408         $endcomparison = '>';  # dst > target
   409         print <<EOF;
   410     $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   411     const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   412     const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   413 EOF
   414     } else {
   415         $endcomparison = '<';  # dst < target
   416         print <<EOF;
   417     $fctype *dst = ($fctype *) cvt->buf;
   418     const $fctype *src = ($fctype *) cvt->buf;
   419     const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   420 EOF
   421     }
   422 
   423     for (my $i = 0; $i < $channels; $i++) {
   424         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   425         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   426         print <<EOF;
   427     $fctype sample${idx} = $val;
   428 EOF
   429     }
   430 
   431     for (my $i = 0; $i < $channels; $i++) {
   432         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   433         print <<EOF;
   434     $fctype last_sample${idx} = sample${idx};
   435 EOF
   436     }
   437 
   438     print <<EOF;
   439     while (dst $endcomparison target) {
   440 EOF
   441 
   442     if ($upsample) {
   443         for (my $i = 0; $i < $channels; $i++) {
   444             # !!! FIXME: don't do this swap every write, just when the samples change.
   445             my $idx = (($channels - $i) - 1);
   446             my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}");
   447             print <<EOF;
   448         dst[$idx] = $val;
   449 EOF
   450         }
   451 
   452         $incr = ($channels == 1) ? 'dst--' : "dst -= $channels";
   453         $incr2 = ($channels == 1) ? 'src--' : "src -= $channels";
   454 
   455         print <<EOF;
   456         $incr;
   457         eps += srcsize;
   458         if ((eps << 1) >= dstsize) {
   459             $incr2;
   460 EOF
   461     } else {  # downsample.
   462         $incr = ($channels == 1) ? 'src++' : "src += $channels";
   463         print <<EOF;
   464         $incr;
   465         eps += dstsize;
   466         if ((eps << 1) >= srcsize) {
   467 EOF
   468         for (my $i = 0; $i < $channels; $i++) {
   469             my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}");
   470             print <<EOF;
   471             dst[$i] = $val;
   472 EOF
   473         }
   474 
   475         $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   476         print <<EOF;
   477             $incr;
   478 EOF
   479     }
   480 
   481     for (my $i = 0; $i < $channels; $i++) {
   482         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   483         my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   484         print <<EOF;
   485             sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp);
   486 EOF
   487     }
   488 
   489     for (my $i = 0; $i < $channels; $i++) {
   490         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   491         print <<EOF;
   492             last_sample${idx} = sample${idx};
   493 EOF
   494     }
   495 
   496     print <<EOF;
   497             eps -= $eps_adjust;
   498         }
   499     }
   500 EOF
   501 
   502         print <<EOF;
   503     cvt->len_cvt = dstsize;
   504     if (cvt->filters[++cvt->filter_index]) {
   505         cvt->filters[cvt->filter_index] (cvt, format);
   506     }
   507 }
   508 
   509 EOF
   510 
   511 }
   512 
   513 # These handle clean resamples...doubling and quadrupling the sample rate, etc.
   514 sub buildMultipleResampleFunc {
   515     # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
   516     my ($from, $channels, $upsample, $multiple) = @_;
   517     my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   518 
   519     my $bigger = getBiggerCtype($ffloat, $fsize);
   520     my $interp = ($ffloat) ? '* 0.5' : '>> 1';
   521     my $interp2 = ($ffloat) ? '* 0.25' : '>> 2';
   522     my $mult3 = ($ffloat) ? '3.0' : '3';
   523     my $lencvtop = ($upsample) ? '*' : '/';
   524 
   525     my $resample = ($upsample) ? 'Upsample' : 'Downsample';
   526     my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple);
   527     my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}";
   528     $funcs{$hashid} = $sym;
   529     $custom_converters++;
   530 
   531     # !!! FIXME: DEBUG_CONVERT should report frequencies.
   532     print <<EOF;
   533 static void SDLCALL
   534 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   535 {
   536 #if DEBUG_CONVERT
   537     fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n");
   538 #endif
   539 
   540     const int srcsize = cvt->len_cvt;
   541     const int dstsize = cvt->len_cvt $lencvtop $multiple;
   542 EOF
   543 
   544     my $endcomparison = '!=';
   545 
   546     # Upsampling (growing the buffer) needs to work backwards, since we
   547     #  overwrite the buffer as we go.
   548     if ($upsample) {
   549         $endcomparison = '>';  # dst > target
   550         print <<EOF;
   551     $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   552     const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   553     const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   554 EOF
   555     } else {
   556         $endcomparison = '<';  # dst < target
   557         print <<EOF;
   558     $fctype *dst = ($fctype *) cvt->buf;
   559     const $fctype *src = ($fctype *) cvt->buf;
   560     const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   561 EOF
   562     }
   563 
   564     for (my $i = 0; $i < $channels; $i++) {
   565         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   566         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   567         print <<EOF;
   568     $bigger last_sample${idx} = ($bigger) $val;
   569 EOF
   570     }
   571 
   572     print <<EOF;
   573     while (dst $endcomparison target) {
   574 EOF
   575 
   576     for (my $i = 0; $i < $channels; $i++) {
   577         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   578         my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   579         print <<EOF;
   580         const $bigger sample${idx} = ($bigger) $val;
   581 EOF
   582     }
   583 
   584     my $incr = '';
   585     if ($upsample) {
   586         $incr = ($channels == 1) ? 'src--' : "src -= $channels";
   587     } else {
   588         my $amount = $channels * $multiple;
   589         $incr = "src += $amount";  # can't ever be 1, so no "++" version.
   590     }
   591 
   592 
   593     print <<EOF;
   594         $incr;
   595 EOF
   596 
   597     # !!! FIXME: This really begs for some Altivec or SSE, etc.
   598     if ($upsample) {
   599         if ($multiple == 2) {
   600             for (my $i = $channels-1; $i >= 0; $i--) {
   601                 my $dsti = $i + $channels;
   602                 print <<EOF;
   603         dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   604 EOF
   605             }
   606             for (my $i = $channels-1; $i >= 0; $i--) {
   607                 my $dsti = $i;
   608                 print <<EOF;
   609         dst[$dsti] = ($fctype) sample${i};
   610 EOF
   611             }
   612         } elsif ($multiple == 4) {
   613             for (my $i = $channels-1; $i >= 0; $i--) {
   614                 my $dsti = $i + ($channels * 3);
   615                 print <<EOF;
   616         dst[$dsti] = ($fctype) sample${i};
   617 EOF
   618             }
   619 
   620             for (my $i = $channels-1; $i >= 0; $i--) {
   621                 my $dsti = $i + ($channels * 2);
   622                 print <<EOF;
   623         dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2);
   624 EOF
   625             }
   626 
   627             for (my $i = $channels-1; $i >= 0; $i--) {
   628                 my $dsti = $i + ($channels * 1);
   629                 print <<EOF;
   630         dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   631 EOF
   632             }
   633 
   634             for (my $i = $channels-1; $i >= 0; $i--) {
   635                 my $dsti = $i + ($channels * 0);
   636                 print <<EOF;
   637         dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2);
   638 EOF
   639             }
   640         } else {
   641             die('bug in program.');  # we only handle x2 and x4.
   642         }
   643     } else {  # downsample.
   644         if ($multiple == 2) {
   645             for (my $i = 0; $i < $channels; $i++) {
   646                 print <<EOF;
   647         dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   648 EOF
   649             }
   650         } elsif ($multiple == 4) {
   651             # !!! FIXME: interpolate all 4 samples?
   652             for (my $i = 0; $i < $channels; $i++) {
   653                 print <<EOF;
   654         dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   655 EOF
   656             }
   657         } else {
   658             die('bug in program.');  # we only handle x2 and x4.
   659         }
   660     }
   661 
   662     for (my $i = 0; $i < $channels; $i++) {
   663         my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   664         print <<EOF;
   665         last_sample${idx} = sample${idx};
   666 EOF
   667     }
   668 
   669     if ($upsample) {
   670         my $amount = $channels * $multiple;
   671         $incr = "dst -= $amount";  # can't ever be 1, so no "--" version.
   672     } else {
   673         $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   674     }
   675 
   676     print <<EOF;
   677         $incr;
   678     }
   679 
   680     cvt->len_cvt = dstsize;
   681     if (cvt->filters[++cvt->filter_index]) {
   682         cvt->filters[cvt->filter_index] (cvt, format);
   683     }
   684 }
   685 
   686 EOF
   687 
   688 }
   689 
   690 sub buildResamplers {
   691     print "#if !NO_RESAMPLERS\n\n";
   692     foreach (@audiotypes) {
   693         my $from = $_;
   694         foreach (@channels) {
   695             my $channel = $_;
   696             buildArbitraryResampleFunc($from, $channel, 1);
   697             buildArbitraryResampleFunc($from, $channel, 0);
   698         }
   699     }
   700 
   701     print "\n#if !LESS_RESAMPLERS\n\n";
   702     foreach (@audiotypes) {
   703         my $from = $_;
   704         foreach (@channels) {
   705             my $channel = $_;
   706             for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
   707                 buildMultipleResampleFunc($from, $channel, 1, $multiple);
   708                 buildMultipleResampleFunc($from, $channel, 0, $multiple);
   709             }
   710         }
   711     }
   712 
   713     print "#endif  /* !LESS_RESAMPLERS */\n";
   714     print "#endif  /* !NO_RESAMPLERS */\n\n\n";
   715 
   716     print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n";
   717     print "#if !NO_RESAMPLERS\n";
   718     foreach (@audiotypes) {
   719         my $from = $_;
   720         foreach (@channels) {
   721             my $channel = $_;
   722             for (my $upsample = 0; $upsample <= 1; $upsample++) {
   723                 my $hashid = getResamplerHashId($from, $channel, $upsample, 0);
   724                 my $sym = $funcs{$hashid};
   725                 print("    { AUDIO_$from, $channel, $upsample, 0, $sym },\n");
   726             }
   727         }
   728     }
   729 
   730     print "#if !LESS_RESAMPLERS\n";
   731     foreach (@audiotypes) {
   732         my $from = $_;
   733         foreach (@channels) {
   734             my $channel = $_;
   735             for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
   736                 for (my $upsample = 0; $upsample <= 1; $upsample++) {
   737                     my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple);
   738                     my $sym = $funcs{$hashid};
   739                     print("    { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n");
   740                 }
   741             }
   742         }
   743     }
   744 
   745     print "#endif  /* !LESS_RESAMPLERS */\n";
   746     print "#endif  /* !NO_RESAMPLERS */\n";
   747     print("    { 0, 0, 0, 0, NULL }\n");
   748     print "};\n\n";
   749 }
   750 
   751 
   752 # mainline ...
   753 
   754 outputHeader();
   755 buildTypeConverters();
   756 buildResamplers();
   757 outputFooter();
   758 
   759 exit 0;
   760 
   761 # end of sdlgenaudiocvt.pl ...
   762