src/audio/sdlgenaudiocvt.pl
author Ryan C. Gordon
Thu, 24 Aug 2006 12:10:46 +0000
changeset 1982 3b4ce57c6215
child 1985 8055185ae4ed
permissions -rwxr-xr-x
First shot at new audio data types (int32 and float32).

Notable changes:
- Converters between types are autogenerated. Instead of making multiple
passes over the data with seperate filters for endianess, size, signedness,
etc, converting between data types is always one specialized filter. This
simplifies SDL_BuildAudioCVT(), which otherwise had a million edge cases
with the new types, and makes the actually conversions more CPU cache
friendly. Left a stub for adding specific optimized versions of these
routines (SSE/MMX/Altivec, assembler, etc)
- Autogenerated converters are built by SDL/src/audio/sdlgenaudiocvt.pl. This
does not need to be run unless tweaking the code, and thus doesn't need
integration into the build system.
- Went through all the drivers and tried to weed out all the "Uint16"
references that are better specified with the new SDL_AudioFormat typedef.
- Cleaned out a bunch of hardcoded bitwise magic numbers and replaced them
with new SDL_AUDIO_* macros.
- Added initial float32 and int32 support code. Theoretically, existing
drivers will push these through converters to get the data they want to
feed to the hardware.

Still TODO:
- Optimize and debug new converters.
- Update the CoreAudio backend to accept float32 data directly.
- Other backends, too?
- SDL_LoadWAV() needs to be updated to support int32 and float32 .wav files
(both of which exist and can be generated by 'sox' for testing purposes).
- Update the mixer to handle new datatypes.
- Optionally update SDL_sound and SDL_mixer, etc.
     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 %funcs;
    20 
    21 my $custom_converters = 0;
    22 
    23 
    24 sub outputHeader {
    25     print <<EOF;
    26 /* DO NOT EDIT THIS FILE! It is generated code. */
    27 /* Please modify SDL/src/audio/sdlgenaudiocvt.pl instead. */
    28 
    29 /*
    30     SDL - Simple DirectMedia Layer
    31     Copyright (C) 1997-2006 Sam Lantinga
    32 
    33     This library is free software; you can redistribute it and/or
    34     modify it under the terms of the GNU Lesser General Public
    35     License as published by the Free Software Foundation; either
    36     version 2.1 of the License, or (at your option) any later version.
    37 
    38     This library is distributed in the hope that it will be useful,
    39     but WITHOUT ANY WARRANTY; without even the implied warranty of
    40     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    41     Lesser General Public License for more details.
    42 
    43     You should have received a copy of the GNU Lesser General Public
    44     License along with this library; if not, write to the Free Software
    45     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    46 
    47     Sam Lantinga
    48     slouken\@libsdl.org
    49 */
    50 
    51 #include "SDL_config.h"
    52 #include "SDL_audio.h"
    53 #include "SDL_audio_c.h"
    54 
    55 /* Now the generated code... */
    56 
    57 EOF
    58 
    59     my @vals = ( 127, 255, 32767, 65535, 2147483647 );
    60     foreach (@vals) {
    61         my $val = $_;
    62         my $fval = 1.0 / $val;
    63         print("#define DIVBY${val} ${fval}f\n");
    64     }
    65 
    66     print("\n");
    67 }
    68 
    69 
    70 sub splittype {
    71     my $t = shift;
    72     my ($signed, $size, $endian) = $t =~ /([USF])(\d+)([LM]SB|)/;
    73     my $float = ($signed eq 'F') ? 1 : 0;
    74     $signed = (($float) or ($signed eq 'S')) ? 1 : 0;
    75     $endian = 'NONE' if ($endian eq '');
    76 
    77     my $ctype = '';
    78     if ($float) {
    79         $ctype = (($size == 32) ? 'float' : 'double');
    80     } else {
    81         $ctype = (($signed) ? 'S' : 'U') . "int${size}";
    82     }
    83 
    84     return ($signed, $float, $size, $endian, $ctype);
    85 }
    86 
    87 sub getSwapFunc {
    88     my ($size, $signed, $float, $endian, $val) = @_;
    89     my $BEorLE = (($endian eq 'MSB') ? 'BE' : 'LE');
    90     my $code = '';
    91 
    92     if ($float) {
    93         $code = "SDL_SwapFloat${BEorLE}($val)";
    94     } else {
    95         if ($size > 8) {
    96             $code = "SDL_Swap${BEorLE}${size}($val)";
    97         } else {
    98             $code = $val;
    99         }
   100 
   101         if (($signed) and (!$float)) {
   102             $code = "((Sint${size}) $code)";
   103         }
   104     }
   105 
   106     return "${code}";
   107 }
   108 
   109 
   110 sub maxIntVal {
   111     my ($signed, $size) = @_;
   112     if ($signed) {
   113         if ($size == 8) {
   114             return 0x7F;
   115         } elsif ($size == 16) {
   116             return 0x7FFF;
   117         } elsif ($size == 32) {
   118             return 0x7FFFFFFF;
   119         }
   120     } else {
   121         if ($size == 8) {
   122             return 0xFF;
   123         } elsif ($size == 16) {
   124             return 0xFFFF;
   125         } elsif ($size == 32) {
   126             return 0xFFFFFFFF;
   127         }
   128     }
   129 
   130     die("bug in script.\n");
   131 }
   132 
   133 sub getFloatToIntMult {
   134     my ($signed, $size) = @_;
   135     my $val = maxIntVal($signed, $size) . '.0';
   136     $val .= 'f' if ($size < 32);
   137     return $val;
   138 }
   139 
   140 sub getIntToFloatDivBy {
   141     my ($signed, $size) = @_;
   142     return 'DIVBY' . maxIntVal($signed, $size);
   143 }
   144 
   145 sub getSignFlipVal {
   146     my $size = shift;
   147     if ($size == 8) {
   148         return '0x80';
   149     } elsif ($size == 16) {
   150         return '0x8000';
   151     } elsif ($size == 32) {
   152         return '0x80000000';
   153     }
   154 
   155     die("bug in script.\n");
   156 }
   157 
   158 sub buildCvtFunc {
   159     my ($from, $to) = @_;
   160     my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   161     my ($tsigned, $tfloat, $tsize, $tendian, $tctype) = splittype($to);
   162     my $diffs = 0;
   163     $diffs++ if ($fsize != $tsize);
   164     $diffs++ if ($fsigned != $tsigned);
   165     $diffs++ if ($ffloat != $tfloat);
   166     $diffs++ if ($fendian ne $tendian);
   167 
   168     return if ($diffs == 0);
   169 
   170     my $hashid = "$from/$to";
   171     if (1) { # !!! FIXME: if ($diffs > 1) {
   172         my $sym = "SDL_Convert_${from}_to_${to}";
   173         $funcs{$hashid} = $sym;
   174         $custom_converters++;
   175 
   176         # Always unsigned for ints, for possible byteswaps.
   177         my $srctype = (($ffloat) ? 'float' : "Uint${fsize}");
   178 
   179         print <<EOF;
   180 static void SDLCALL
   181 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   182 {
   183     int i;
   184     const $srctype *src;
   185     $tctype *dst;
   186 
   187 #ifdef DEBUG_CONVERT
   188     fprintf(stderr, "Converting AUDIO_${from} to AUDIO_${to}.\\n");
   189 #endif
   190 
   191 EOF
   192 
   193         if ($fsize < $tsize) {
   194             my $mult = $tsize / $fsize;
   195             print <<EOF;
   196     src = (const $srctype *) (cvt->buf + cvt->len_cvt);
   197     dst = ($tctype *) (cvt->buf + cvt->len_cvt * $mult);
   198     for (i = cvt->len_cvt / sizeof ($srctype); i; --i, --src, --dst) {
   199 EOF
   200         } else {
   201             print <<EOF;
   202     src = (const $srctype *) cvt->buf;
   203     dst = ($tctype *) cvt->buf;
   204     for (i = cvt->len_cvt / sizeof ($srctype); i; --i, ++src, ++dst) {
   205 EOF
   206         }
   207 
   208         # Have to convert to/from float/int.
   209         # !!! FIXME: cast through double for int32<->float?
   210         my $code = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, '*src');
   211         if ($ffloat != $tfloat) {
   212             if ($ffloat) {
   213                 my $mult = getFloatToIntMult($tsigned, $tsize);
   214                 $code = "(($tctype) ($code * $mult))";
   215             } else {
   216                 # $divby will be the reciprocal, to avoid pipeline stalls
   217                 #  from floating point division...so multiply it.
   218                 my $divby = getIntToFloatDivBy($fsigned, $fsize);
   219                 $code = "(((float) $code) * $divby)";
   220             }
   221         } else {
   222             # All integer conversions here.
   223             if ($fsigned != $tsigned) {
   224                 my $signflipval = getSignFlipVal($fsize);
   225                 $code = "(($code) ^ $signflipval)";
   226             }
   227 
   228             my $shiftval = abs($fsize - $tsize);
   229             if ($fsize < $tsize) {
   230                 $code = "((($tctype) $code) << $shiftval)";
   231             } elsif ($fsize > $tsize) {
   232                 $code = "(($tctype) ($code >> $shiftval))";
   233             }
   234         }
   235 
   236         my $swap = getSwapFunc($tsize, $tsigned, $tfloat, $tendian, 'val');
   237 
   238         print <<EOF;
   239         const $tctype val = $code;
   240         *dst = ${swap};
   241     }
   242 
   243 EOF
   244 
   245         if ($fsize > $tsize) {
   246             my $divby = $fsize / $tsize;
   247             print("    cvt->len_cvt /= $divby;\n");
   248         } elsif ($fsize < $tsize) {
   249             my $mult = $tsize / $fsize;
   250             print("    cvt->len_cvt *= $mult;\n");
   251         }
   252 
   253         print <<EOF;
   254     format = AUDIO_$to;
   255     if (cvt->filters[++cvt->filter_index]) {
   256         cvt->filters[cvt->filter_index] (cvt, format);
   257     }
   258 }
   259 
   260 EOF
   261 
   262     } else {
   263         if ($fsigned != $tsigned) {
   264             $funcs{$hashid} = 'SDL_ConvertSigned';
   265         } elsif ($ffloat != $tfloat) {
   266             $funcs{$hashid} = 'SDL_ConvertFloat';
   267         } elsif ($fsize != $tsize) {
   268             $funcs{$hashid} = 'SDL_ConvertSize';
   269         } elsif ($fendian ne $tendian) {
   270             $funcs{$hashid} = 'SDL_ConvertEndian';
   271         } else {
   272             die("error in script.\n");
   273         }
   274     }
   275 }
   276 
   277 outputHeader();
   278 
   279 foreach (@audiotypes) {
   280     my $from = $_;
   281     foreach (@audiotypes) {
   282         my $to = $_;
   283         buildCvtFunc($from, $to);
   284     }
   285 }
   286 
   287 print <<EOF;
   288 const SDL_AudioTypeFilters sdl_audio_type_filters[] =
   289 {
   290 EOF
   291 
   292 foreach (@audiotypes) {
   293     my $from = $_;
   294     foreach (@audiotypes) {
   295         my $to = $_;
   296         if ($from ne $to) {
   297             my $hashid = "$from/$to";
   298             my $sym = $funcs{$hashid};
   299             print("    { AUDIO_$from, AUDIO_$to, $sym },\n");
   300         }
   301     }
   302 }
   303 
   304 print <<EOF;
   305 };
   306 
   307 
   308 EOF
   309 
   310 exit 0;
   311 
   312 # end of sdlaudiocvt.pl ...
   313