Mercurial > sdl-ios-xcode
view src/audio/sdlgenaudiocvt.pl @ 3668:6952d2b783e6
Friendly warning comment.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 13 Jan 2010 16:21:26 +0000 |
parents | bfa8d34ce03a |
children | 8c9cbb623d55 |
line wrap: on
line source
#!/usr/bin/perl -w use warnings; use strict; my @audiotypes = qw( U8 S8 U16LSB S16LSB U16MSB S16MSB S32LSB S32MSB F32LSB F32MSB ); my @channels = ( 1, 2, 4, 6, 8 ); my %funcs; my $custom_converters = 0; sub getTypeConvertHashId { my ($from, $to) = @_; return "TYPECONVERTER $from/$to"; } sub getResamplerHashId { my ($from, $channels, $upsample, $multiple) = @_; return "RESAMPLER $from/$channels/$upsample/$multiple"; } sub outputHeader { print <<EOF; /* DO NOT EDIT! This file is generated by sdlgenaudiocvt.pl */ /* SDL - Simple DirectMedia Layer Copyright (C) 1997-2009 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken\@libsdl.org */ #include "SDL_config.h" #include "SDL_audio.h" #include "SDL_audio_c.h" #ifndef DEBUG_CONVERT #define DEBUG_CONVERT 0 #endif /* If you can guarantee your data and need space, you can eliminate code... */ /* Just build the arbitrary resamplers if you're saving code space. */ #ifndef LESS_RESAMPLERS #define LESS_RESAMPLERS 0 #endif /* Don't build any resamplers if you're REALLY saving code space. */ #ifndef NO_RESAMPLERS #define NO_RESAMPLERS 0 #endif /* Don't build any type converters if you're saving code space. */ #ifndef NO_CONVERTERS #define NO_CONVERTERS 0 #endif /* *INDENT-OFF* */ EOF my @vals = ( 127, 32767, 2147483647 ); foreach (@vals) { my $val = $_; my $fval = 1.0 / $val; print("#define DIVBY${val} ${fval}f\n"); } print("\n"); } sub outputFooter { print <<EOF; /* $custom_converters converters generated. */ /* *INDENT-ON* */ /* vi: set ts=4 sw=4 expandtab: */ EOF } sub splittype { my $t = shift; my ($signed, $size, $endian) = $t =~ /([USF])(\d+)([LM]SB|)/; my $float = ($signed eq 'F') ? 1 : 0; $signed = (($float) or ($signed eq 'S')) ? 1 : 0; $endian = 'NONE' if ($endian eq ''); my $ctype = ''; if ($float) { $ctype = (($size == 32) ? 'float' : 'double'); } else { $ctype = (($signed) ? 'S' : 'U') . "int${size}"; } return ($signed, $float, $size, $endian, $ctype); } sub getSwapFunc { my ($size, $signed, $float, $endian, $val) = @_; my $BEorLE = (($endian eq 'MSB') ? 'BE' : 'LE'); my $code = ''; if ($float) { $code = "SDL_SwapFloat${BEorLE}($val)"; } else { if ($size > 8) { $code = "SDL_Swap${BEorLE}${size}($val)"; } else { $code = $val; } if (($signed) and (!$float)) { $code = "((Sint${size}) $code)"; } } return "${code}"; } sub maxIntVal { my $size = shift; if ($size == 8) { return 0x7F; } elsif ($size == 16) { return 0x7FFF; } elsif ($size == 32) { return 0x7FFFFFFF; } die("bug in script.\n"); } sub getFloatToIntMult { my $size = shift; my $val = maxIntVal($size) . '.0'; $val .= 'f' if ($size < 32); return $val; } sub getIntToFloatDivBy { my $size = shift; return 'DIVBY' . maxIntVal($size); } sub getSignFlipVal { my $size = shift; if ($size == 8) { return '0x80'; } elsif ($size == 16) { return '0x8000'; } elsif ($size == 32) { return '0x80000000'; } die("bug in script.\n"); } sub buildCvtFunc { my ($from, $to) = @_; my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from); my ($tsigned, $tfloat, $tsize, $tendian, $tctype) = splittype($to); my $diffs = 0; $diffs++ if ($fsize != $tsize); $diffs++ if ($fsigned != $tsigned); $diffs++ if ($ffloat != $tfloat); $diffs++ if ($fendian ne $tendian); return if ($diffs == 0); my $hashid = getTypeConvertHashId($from, $to); if (1) { # !!! FIXME: if ($diffs > 1) { my $sym = "SDL_Convert_${from}_to_${to}"; $funcs{$hashid} = $sym; $custom_converters++; # Always unsigned for ints, for possible byteswaps. my $srctype = (($ffloat) ? 'float' : "Uint${fsize}"); print <<EOF; static void SDLCALL ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; const $srctype *src; $tctype *dst; #if DEBUG_CONVERT fprintf(stderr, "Converting AUDIO_${from} to AUDIO_${to}.\\n"); #endif EOF if ($fsize < $tsize) { my $mult = $tsize / $fsize; print <<EOF; src = ((const $srctype *) (cvt->buf + cvt->len_cvt)) - 1; dst = (($tctype *) (cvt->buf + cvt->len_cvt * $mult)) - 1; for (i = cvt->len_cvt / sizeof ($srctype); i; --i, --src, --dst) { EOF } else { print <<EOF; src = (const $srctype *) cvt->buf; dst = ($tctype *) cvt->buf; for (i = cvt->len_cvt / sizeof ($srctype); i; --i, ++src, ++dst) { EOF } # Have to convert to/from float/int. # !!! FIXME: cast through double for int32<->float? my $code = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, '*src'); if ($ffloat != $tfloat) { if ($ffloat) { my $mult = getFloatToIntMult($tsize); if (!$tsigned) { # bump from -1.0f/1.0f to 0.0f/2.0f $code = "($code + 1.0f)"; } $code = "(($tctype) ($code * $mult))"; } else { # $divby will be the reciprocal, to avoid pipeline stalls # from floating point division...so multiply it. my $divby = getIntToFloatDivBy($fsize); $code = "(((float) $code) * $divby)"; if (!$fsigned) { # bump from 0.0f/2.0f to -1.0f/1.0f. $code = "($code - 1.0f)"; } } } else { # All integer conversions here. if ($fsigned != $tsigned) { my $signflipval = getSignFlipVal($fsize); $code = "(($code) ^ $signflipval)"; } my $shiftval = abs($fsize - $tsize); if ($fsize < $tsize) { $code = "((($tctype) $code) << $shiftval)"; } elsif ($fsize > $tsize) { $code = "(($tctype) ($code >> $shiftval))"; } } my $swap = getSwapFunc($tsize, $tsigned, $tfloat, $tendian, 'val'); print <<EOF; const $tctype val = $code; *dst = ${swap}; } EOF if ($fsize > $tsize) { my $divby = $fsize / $tsize; print(" cvt->len_cvt /= $divby;\n"); } elsif ($fsize < $tsize) { my $mult = $tsize / $fsize; print(" cvt->len_cvt *= $mult;\n"); } print <<EOF; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, AUDIO_$to); } } EOF } else { if ($fsigned != $tsigned) { $funcs{$hashid} = 'SDL_ConvertSigned'; } elsif ($ffloat != $tfloat) { $funcs{$hashid} = 'SDL_ConvertFloat'; } elsif ($fsize != $tsize) { $funcs{$hashid} = 'SDL_ConvertSize'; } elsif ($fendian ne $tendian) { $funcs{$hashid} = 'SDL_ConvertEndian'; } else { die("error in script.\n"); } } } sub buildTypeConverters { print "#if !NO_CONVERTERS\n\n"; foreach (@audiotypes) { my $from = $_; foreach (@audiotypes) { my $to = $_; buildCvtFunc($from, $to); } } print "#endif /* !NO_CONVERTERS */\n\n\n"; print "const SDL_AudioTypeFilters sdl_audio_type_filters[] =\n{\n"; print "#if !NO_CONVERTERS\n"; foreach (@audiotypes) { my $from = $_; foreach (@audiotypes) { my $to = $_; if ($from ne $to) { my $hashid = getTypeConvertHashId($from, $to); my $sym = $funcs{$hashid}; print(" { AUDIO_$from, AUDIO_$to, $sym },\n"); } } } print "#endif /* !NO_CONVERTERS */\n"; print(" { 0, 0, NULL }\n"); print "};\n\n\n"; } sub getBiggerCtype { my ($isfloat, $size) = @_; if ($isfloat) { if ($size == 32) { return 'double'; } die("bug in script.\n"); } if ($size == 8) { return 'Sint16'; } elsif ($size == 16) { return 'Sint32' } elsif ($size == 32) { return 'Sint64' } die("bug in script.\n"); } # These handle arbitrary resamples...44100Hz to 48000Hz, for example. # Man, this code is skanky. sub buildArbitraryResampleFunc { # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc(). my ($from, $channels, $upsample) = @_; my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from); my $bigger = getBiggerCtype($ffloat, $fsize); my $interp = ($ffloat) ? '* 0.5' : '>> 1'; my $resample = ($upsample) ? 'Upsample' : 'Downsample'; my $hashid = getResamplerHashId($from, $channels, $upsample, 0); my $sym = "SDL_${resample}_${from}_${channels}c"; $funcs{$hashid} = $sym; $custom_converters++; my $fudge = $fsize * $channels * 2; # !!! FIXME my $eps_adjust = ($upsample) ? 'dstsize' : 'srcsize'; my $incr = ''; my $incr2 = ''; # !!! FIXME: DEBUG_CONVERT should report frequencies. print <<EOF; static void SDLCALL ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format) { #if DEBUG_CONVERT fprintf(stderr, "$resample arbitrary (x%f) AUDIO_${from}, ${channels} channels.\\n", cvt->rate_incr); #endif const int srcsize = cvt->len_cvt - $fudge; const int dstsize = (int) (((double)cvt->len_cvt) * cvt->rate_incr); register int eps = 0; EOF # Upsampling (growing the buffer) needs to work backwards, since we # overwrite the buffer as we go. if ($upsample) { print <<EOF; $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels; const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels; const $fctype *target = ((const $fctype *) cvt->buf) - $channels; EOF } else { print <<EOF; $fctype *dst = ($fctype *) cvt->buf; const $fctype *src = ($fctype *) cvt->buf; const $fctype *target = (const $fctype *) (cvt->buf + dstsize); EOF } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]"); print <<EOF; $fctype sample${idx} = $val; EOF } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; print <<EOF; $fctype last_sample${idx} = sample${idx}; EOF } print <<EOF; while (dst != target) { EOF if ($upsample) { for (my $i = 0; $i < $channels; $i++) { # !!! FIXME: don't do this swap every write, just when the samples change. my $idx = (($channels - $i) - 1); my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}"); print <<EOF; dst[$idx] = $val; EOF } $incr = ($channels == 1) ? 'dst--' : "dst -= $channels"; $incr2 = ($channels == 1) ? 'src--' : "src -= $channels"; print <<EOF; $incr; eps += srcsize; if ((eps << 1) >= dstsize) { $incr2; EOF } else { # downsample. $incr = ($channels == 1) ? 'src++' : "src += $channels"; print <<EOF; $incr; eps += dstsize; if ((eps << 1) >= srcsize) { EOF for (my $i = 0; $i < $channels; $i++) { my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}"); print <<EOF; dst[$i] = $val; EOF } $incr = ($channels == 1) ? 'dst++' : "dst += $channels"; print <<EOF; $incr; EOF } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]"); print <<EOF; sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp); EOF } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; print <<EOF; last_sample${idx} = sample${idx}; EOF } print <<EOF; eps -= $eps_adjust; } } EOF print <<EOF; cvt->len_cvt = dstsize; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } EOF } # These handle clean resamples...doubling and quadrupling the sample rate, etc. sub buildMultipleResampleFunc { # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc(). my ($from, $channels, $upsample, $multiple) = @_; my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from); my $bigger = getBiggerCtype($ffloat, $fsize); my $interp = ($ffloat) ? '* 0.5' : '>> 1'; my $interp2 = ($ffloat) ? '* 0.25' : '>> 2'; my $mult3 = ($ffloat) ? '3.0' : '3'; my $lencvtop = ($upsample) ? '*' : '/'; my $resample = ($upsample) ? 'Upsample' : 'Downsample'; my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple); my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}"; $funcs{$hashid} = $sym; $custom_converters++; # !!! FIXME: DEBUG_CONVERT should report frequencies. print <<EOF; static void SDLCALL ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format) { #if DEBUG_CONVERT fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n"); #endif const int srcsize = cvt->len_cvt; const int dstsize = cvt->len_cvt $lencvtop $multiple; EOF my $endcomparison = '!='; # Upsampling (growing the buffer) needs to work backwards, since we # overwrite the buffer as we go. if ($upsample) { $endcomparison = '>'; # dst > target print <<EOF; $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels; const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels; const $fctype *target = ((const $fctype *) cvt->buf) - $channels; EOF } else { $endcomparison = '<'; # dst < target print <<EOF; $fctype *dst = ($fctype *) cvt->buf; const $fctype *src = ($fctype *) cvt->buf; const $fctype *target = (const $fctype *) (cvt->buf + dstsize); EOF } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]"); print <<EOF; $bigger last_sample${idx} = ($bigger) $val; EOF } print <<EOF; while (dst $endcomparison target) { EOF for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]"); print <<EOF; const $bigger sample${idx} = ($bigger) $val; EOF } my $incr = ''; if ($upsample) { $incr = ($channels == 1) ? 'src--' : "src -= $channels"; } else { my $amount = $channels * $multiple; $incr = "src += $amount"; # can't ever be 1, so no "++" version. } print <<EOF; $incr; EOF # !!! FIXME: This really begs for some Altivec or SSE, etc. if ($upsample) { if ($multiple == 2) { for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i + $channels; print <<EOF; dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp); EOF } for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i; print <<EOF; dst[$dsti] = ($fctype) sample${i}; EOF } } elsif ($multiple == 4) { for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i + ($channels * 3); print <<EOF; dst[$dsti] = ($fctype) sample${i}; EOF } for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i + ($channels * 2); print <<EOF; dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2); EOF } for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i + ($channels * 1); print <<EOF; dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp); EOF } for (my $i = $channels-1; $i >= 0; $i--) { my $dsti = $i + ($channels * 0); print <<EOF; dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2); EOF } } else { die('bug in program.'); # we only handle x2 and x4. } } else { # downsample. if ($multiple == 2) { for (my $i = 0; $i < $channels; $i++) { print <<EOF; dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp); EOF } } elsif ($multiple == 4) { # !!! FIXME: interpolate all 4 samples? for (my $i = 0; $i < $channels; $i++) { print <<EOF; dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp); EOF } } else { die('bug in program.'); # we only handle x2 and x4. } } for (my $i = 0; $i < $channels; $i++) { my $idx = ($upsample) ? (($channels - $i) - 1) : $i; print <<EOF; last_sample${idx} = sample${idx}; EOF } if ($upsample) { my $amount = $channels * $multiple; $incr = "dst -= $amount"; # can't ever be 1, so no "--" version. } else { $incr = ($channels == 1) ? 'dst++' : "dst += $channels"; } print <<EOF; $incr; } cvt->len_cvt = dstsize; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } EOF } sub buildResamplers { print "#if !NO_RESAMPLERS\n\n"; foreach (@audiotypes) { my $from = $_; foreach (@channels) { my $channel = $_; buildArbitraryResampleFunc($from, $channel, 1); buildArbitraryResampleFunc($from, $channel, 0); } } print "\n#if !LESS_RESAMPLERS\n\n"; foreach (@audiotypes) { my $from = $_; foreach (@channels) { my $channel = $_; for (my $multiple = 2; $multiple <= 4; $multiple += 2) { buildMultipleResampleFunc($from, $channel, 1, $multiple); buildMultipleResampleFunc($from, $channel, 0, $multiple); } } } print "#endif /* !LESS_RESAMPLERS */\n"; print "#endif /* !NO_RESAMPLERS */\n\n\n"; print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n"; print "#if !NO_RESAMPLERS\n"; foreach (@audiotypes) { my $from = $_; foreach (@channels) { my $channel = $_; for (my $upsample = 0; $upsample <= 1; $upsample++) { my $hashid = getResamplerHashId($from, $channel, $upsample, 0); my $sym = $funcs{$hashid}; print(" { AUDIO_$from, $channel, $upsample, 0, $sym },\n"); } } } print "#if !LESS_RESAMPLERS\n"; foreach (@audiotypes) { my $from = $_; foreach (@channels) { my $channel = $_; for (my $multiple = 2; $multiple <= 4; $multiple += 2) { for (my $upsample = 0; $upsample <= 1; $upsample++) { my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple); my $sym = $funcs{$hashid}; print(" { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n"); } } } } print "#endif /* !LESS_RESAMPLERS */\n"; print "#endif /* !NO_RESAMPLERS */\n"; print(" { 0, 0, 0, 0, NULL }\n"); print "};\n\n"; } # mainline ... outputHeader(); buildTypeConverters(); buildResamplers(); outputFooter(); exit 0; # end of sdlgenaudiocvt.pl ...