First shot at autogenerated audio resamplers.
authorRyan C. Gordon <icculus@icculus.org>
Fri, 09 Jan 2009 15:41:45 +0000
changeset 3008786a48f8309c
parent 3007 b30409e106f2
child 3009 546c022a9ae5
First shot at autogenerated audio resamplers.

Don't check in a new SDL_audiotypecvt.c yet, though.
src/audio/sdlgenaudiocvt.pl
     1.1 --- a/src/audio/sdlgenaudiocvt.pl	Fri Jan 09 13:58:28 2009 +0000
     1.2 +++ b/src/audio/sdlgenaudiocvt.pl	Fri Jan 09 15:41:45 2009 +0000
     1.3 @@ -16,9 +16,21 @@
     1.4      F32MSB
     1.5  );
     1.6  
     1.7 +my @channels = ( 1, 2, 4, 6, 8 );
     1.8  my %funcs;
     1.9 +my $custom_converters = 0;
    1.10  
    1.11 -my $custom_converters = 0;
    1.12 +
    1.13 +sub getTypeConvertHashId {
    1.14 +    my ($from, $to) = @_;
    1.15 +    return "TYPECONVERTER $from/$to";
    1.16 +}
    1.17 +
    1.18 +
    1.19 +sub getResamplerHashId {
    1.20 +    my ($from, $channels, $upsample, $multiple) = @_;
    1.21 +    return "RESAMPLER $from/$channels/$upsample/$multiple";
    1.22 +}
    1.23  
    1.24  
    1.25  sub outputHeader {
    1.26 @@ -66,6 +78,8 @@
    1.27  
    1.28  sub outputFooter {
    1.29      print <<EOF;
    1.30 +/* $custom_converters converters generated. */
    1.31 +
    1.32  /* *INDENT-ON* */
    1.33  
    1.34  /* vi: set ts=4 sw=4 expandtab: */
    1.35 @@ -162,7 +176,7 @@
    1.36  
    1.37      return if ($diffs == 0);
    1.38  
    1.39 -    my $hashid = "$from/$to";
    1.40 +    my $hashid = getTypeConvertHashId($from, $to);
    1.41      if (1) { # !!! FIXME: if ($diffs > 1) {
    1.42          my $sym = "SDL_Convert_${from}_to_${to}";
    1.43          $funcs{$hashid} = $sym;
    1.44 @@ -274,39 +288,407 @@
    1.45      }
    1.46  }
    1.47  
    1.48 -outputHeader();
    1.49  
    1.50 -foreach (@audiotypes) {
    1.51 -    my $from = $_;
    1.52 +sub buildTypeConverters {
    1.53      foreach (@audiotypes) {
    1.54 -        my $to = $_;
    1.55 -        buildCvtFunc($from, $to);
    1.56 +        my $from = $_;
    1.57 +        foreach (@audiotypes) {
    1.58 +            my $to = $_;
    1.59 +            buildCvtFunc($from, $to);
    1.60 +        }
    1.61 +    }
    1.62 +
    1.63 +    print "const SDL_AudioTypeFilters sdl_audio_type_filters[] =\n{\n";
    1.64 +    foreach (@audiotypes) {
    1.65 +        my $from = $_;
    1.66 +        foreach (@audiotypes) {
    1.67 +            my $to = $_;
    1.68 +            if ($from ne $to) {
    1.69 +                my $hashid = getTypeConvertHashId($from, $to);
    1.70 +                my $sym = $funcs{$hashid};
    1.71 +                print("    { AUDIO_$from, AUDIO_$to, $sym },\n");
    1.72 +            }
    1.73 +        }
    1.74 +    }
    1.75 +
    1.76 +    print "};\n\n\n";
    1.77 +}
    1.78 +
    1.79 +sub getBiggerCtype {
    1.80 +    my ($isfloat, $size) = @_;
    1.81 +
    1.82 +    if ($isfloat) {
    1.83 +        if ($size == 32) {
    1.84 +            return 'double';
    1.85 +        }
    1.86 +        die("bug in script.\n");
    1.87 +    }
    1.88 +
    1.89 +    if ($size == 8) {
    1.90 +        return 'Sint16';
    1.91 +    } elsif ($size == 16) {
    1.92 +        return 'Sint32'
    1.93 +    } elsif ($size == 32) {
    1.94 +        return 'Sint64'
    1.95 +    }
    1.96 +
    1.97 +    die("bug in script.\n");
    1.98 +}
    1.99 +
   1.100 +
   1.101 +# These handle arbitrary resamples...44100Hz to 48000Hz, for example.
   1.102 +# Man, this code is skanky.
   1.103 +sub buildArbitraryResampleFunc {
   1.104 +    # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
   1.105 +    my ($from, $channels, $upsample) = @_;
   1.106 +    my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   1.107 +
   1.108 +    my $bigger = getBiggerCtype($ffloat, $fsize);
   1.109 +    my $interp = ($ffloat) ? '* 0.5' : '>> 1';
   1.110 +
   1.111 +    my $resample = ($upsample) ? 'Upsample' : 'Downsample';
   1.112 +    my $hashid = getResamplerHashId($from, $channels, $upsample, 0);
   1.113 +    my $sym = "SDL_${resample}_${from}_${channels}c";
   1.114 +    $funcs{$hashid} = $sym;
   1.115 +    $custom_converters++;
   1.116 +
   1.117 +    my $fudge = $fsize * $channels * 2;  # !!! FIXME
   1.118 +    my $eps_adjust = ($upsample) ? 'dstsize' : 'srcsize';
   1.119 +    my $incr = '';
   1.120 +    my $incr2 = '';
   1.121 +
   1.122 +
   1.123 +    # !!! FIXME: DEBUG_CONVERT should report frequencies.
   1.124 +    print <<EOF;
   1.125 +static void SDLCALL
   1.126 +${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   1.127 +{
   1.128 +#ifdef DEBUG_CONVERT
   1.129 +    fprintf(stderr, "$resample arbitrary (x%f) AUDIO_${from}, ${channels} channels.\\n", cvt->rate_incr);
   1.130 +#endif
   1.131 +
   1.132 +    const int srcsize = cvt->len_cvt - $fudge;
   1.133 +    const int dstsize = (int) (((double)cvt->len_cvt) * cvt->rate_incr);
   1.134 +    register int eps = 0;
   1.135 +EOF
   1.136 +
   1.137 +    # Upsampling (growing the buffer) needs to work backwards, since we
   1.138 +    #  overwrite the buffer as we go.
   1.139 +    if ($upsample) {
   1.140 +        print <<EOF;
   1.141 +    $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   1.142 +    const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   1.143 +    const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   1.144 +EOF
   1.145 +    } else {
   1.146 +        print <<EOF;
   1.147 +    $fctype *dst = ($fctype *) cvt->buf;
   1.148 +    const $fctype *src = ($fctype *) cvt->buf;
   1.149 +    const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   1.150 +EOF
   1.151 +    }
   1.152 +
   1.153 +    for (my $i = 0; $i < $channels; $i++) {
   1.154 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.155 +        my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   1.156 +        print <<EOF;
   1.157 +    $fctype sample${idx} = $val;
   1.158 +EOF
   1.159 +    }
   1.160 +
   1.161 +    for (my $i = 0; $i < $channels; $i++) {
   1.162 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.163 +        print <<EOF;
   1.164 +    $fctype last_sample${idx} = sample${idx};
   1.165 +EOF
   1.166 +    }
   1.167 +
   1.168 +    print <<EOF;
   1.169 +    while (dst != target) {
   1.170 +EOF
   1.171 +
   1.172 +    if ($upsample) {
   1.173 +        for (my $i = 0; $i < $channels; $i++) {
   1.174 +            # !!! FIXME: don't do this swap every write, just when the samples change.
   1.175 +            my $idx = (($channels - $i) - 1);
   1.176 +            my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}");
   1.177 +            print <<EOF;
   1.178 +        dst[$idx] = $val;
   1.179 +EOF
   1.180 +        }
   1.181 +
   1.182 +        $incr = ($channels == 1) ? 'dst--' : "dst -= $channels";
   1.183 +        $incr2 = ($channels == 1) ? 'src--' : "src -= $channels";
   1.184 +
   1.185 +        print <<EOF;
   1.186 +        $incr;
   1.187 +        eps += srcsize;
   1.188 +        if ((eps << 1) >= dstsize) {
   1.189 +            $incr2;
   1.190 +EOF
   1.191 +    } else {  # downsample.
   1.192 +        $incr = ($channels == 1) ? 'src++' : "src += $channels";
   1.193 +        print <<EOF;
   1.194 +        $incr;
   1.195 +        eps += dstsize;
   1.196 +        if ((eps << 1) >= srcsize) {
   1.197 +EOF
   1.198 +        for (my $i = 0; $i < $channels; $i++) {
   1.199 +            my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}");
   1.200 +            print <<EOF;
   1.201 +            dst[$i] = $val;
   1.202 +EOF
   1.203 +        }
   1.204 +
   1.205 +        $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   1.206 +        print <<EOF;
   1.207 +            $incr;
   1.208 +EOF
   1.209 +    }
   1.210 +
   1.211 +    for (my $i = 0; $i < $channels; $i++) {
   1.212 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.213 +        my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   1.214 +        print <<EOF;
   1.215 +            sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp);
   1.216 +EOF
   1.217 +    }
   1.218 +
   1.219 +    for (my $i = 0; $i < $channels; $i++) {
   1.220 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.221 +        print <<EOF;
   1.222 +            last_sample${idx} = sample${idx};
   1.223 +EOF
   1.224 +    }
   1.225 +
   1.226 +    print <<EOF;
   1.227 +            eps -= $eps_adjust;
   1.228 +        }
   1.229 +    }
   1.230 +EOF
   1.231 +
   1.232 +        print <<EOF;
   1.233 +    cvt->len_cvt = dstsize;
   1.234 +    if (cvt->filters[++cvt->filter_index]) {
   1.235 +        cvt->filters[cvt->filter_index] (cvt, format);
   1.236      }
   1.237  }
   1.238  
   1.239 -print <<EOF;
   1.240 -const SDL_AudioTypeFilters sdl_audio_type_filters[] =
   1.241 -{
   1.242  EOF
   1.243  
   1.244 -foreach (@audiotypes) {
   1.245 -    my $from = $_;
   1.246 -    foreach (@audiotypes) {
   1.247 -        my $to = $_;
   1.248 -        if ($from ne $to) {
   1.249 -            my $hashid = "$from/$to";
   1.250 -            my $sym = $funcs{$hashid};
   1.251 -            print("    { AUDIO_$from, AUDIO_$to, $sym },\n");
   1.252 +}
   1.253 +
   1.254 +# These handle clean resamples...doubling and quadrupling the sample rate, etc.
   1.255 +sub buildMultipleResampleFunc {
   1.256 +    # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
   1.257 +    my ($from, $channels, $upsample, $multiple) = @_;
   1.258 +    my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
   1.259 +
   1.260 +    my $bigger = getBiggerCtype($ffloat, $fsize);
   1.261 +    my $interp = ($ffloat) ? '* 0.5' : '>> 1';
   1.262 +    my $interp2 = ($ffloat) ? '* 0.25' : '>> 2';
   1.263 +    my $mult3 = ($ffloat) ? '3.0' : '3';
   1.264 +    my $lencvtop = ($upsample) ? '*' : '/';
   1.265 +
   1.266 +    my $resample = ($upsample) ? 'Upsample' : 'Downsample';
   1.267 +    my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple);
   1.268 +    my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}";
   1.269 +    $funcs{$hashid} = $sym;
   1.270 +    $custom_converters++;
   1.271 +
   1.272 +    # !!! FIXME: DEBUG_CONVERT should report frequencies.
   1.273 +    print <<EOF;
   1.274 +static void SDLCALL
   1.275 +${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
   1.276 +{
   1.277 +#ifdef DEBUG_CONVERT
   1.278 +    fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n");
   1.279 +#endif
   1.280 +
   1.281 +    const int srcsize = cvt->len_cvt;
   1.282 +    const int dstsize = cvt->len_cvt $lencvtop $multiple;
   1.283 +EOF
   1.284 +
   1.285 +    # Upsampling (growing the buffer) needs to work backwards, since we
   1.286 +    #  overwrite the buffer as we go.
   1.287 +    if ($upsample) {
   1.288 +        print <<EOF;
   1.289 +    $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
   1.290 +    const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
   1.291 +    const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
   1.292 +EOF
   1.293 +    } else {
   1.294 +        print <<EOF;
   1.295 +    $fctype *dst = ($fctype *) cvt->buf;
   1.296 +    const $fctype *src = ($fctype *) cvt->buf;
   1.297 +    const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
   1.298 +EOF
   1.299 +    }
   1.300 +
   1.301 +    for (my $i = 0; $i < $channels; $i++) {
   1.302 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.303 +        my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   1.304 +        print <<EOF;
   1.305 +    $bigger last_sample${idx} = ($bigger) $val;
   1.306 +EOF
   1.307 +    }
   1.308 +
   1.309 +    print <<EOF;
   1.310 +    while (dst != target) {
   1.311 +EOF
   1.312 +
   1.313 +    for (my $i = 0; $i < $channels; $i++) {
   1.314 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.315 +        my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
   1.316 +        print <<EOF;
   1.317 +        const $bigger sample${idx} = ($bigger) $val;
   1.318 +EOF
   1.319 +    }
   1.320 +
   1.321 +    my $incr = '';
   1.322 +    if ($upsample) {
   1.323 +        $incr = ($channels == 1) ? 'src--' : "src -= $channels";
   1.324 +    } else {
   1.325 +        my $amount = $channels * $multiple;
   1.326 +        $incr = "src += $amount";  # can't ever be 1, so no "++" version.
   1.327 +    }
   1.328 +
   1.329 +
   1.330 +    print <<EOF;
   1.331 +        $incr;
   1.332 +EOF
   1.333 +
   1.334 +    # !!! FIXME: This really begs for some Altivec or SSE, etc.
   1.335 +    if ($upsample) {
   1.336 +        if ($multiple == 2) {
   1.337 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.338 +                my $dsti = $i + $channels;
   1.339 +                print <<EOF;
   1.340 +        dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   1.341 +EOF
   1.342 +            }
   1.343 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.344 +                my $dsti = $i;
   1.345 +                print <<EOF;
   1.346 +        dst[$dsti] = ($fctype) sample${i};
   1.347 +EOF
   1.348 +            }
   1.349 +        } elsif ($multiple == 4) {
   1.350 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.351 +                my $dsti = $i + ($channels * 3);
   1.352 +                print <<EOF;
   1.353 +        dst[$dsti] = ($fctype) sample${i};
   1.354 +EOF
   1.355 +            }
   1.356 +
   1.357 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.358 +                my $dsti = $i + ($channels * 2);
   1.359 +                print <<EOF;
   1.360 +        dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2);
   1.361 +EOF
   1.362 +            }
   1.363 +
   1.364 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.365 +                my $dsti = $i + ($channels * 1);
   1.366 +                print <<EOF;
   1.367 +        dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   1.368 +EOF
   1.369 +            }
   1.370 +
   1.371 +            for (my $i = $channels-1; $i >= 0; $i--) {
   1.372 +                my $dsti = $i + ($channels * 0);
   1.373 +                print <<EOF;
   1.374 +        dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2);
   1.375 +EOF
   1.376 +            }
   1.377 +        } else {
   1.378 +            die('bug in program.');  # we only handle x2 and x4.
   1.379          }
   1.380 +    } else {  # downsample.
   1.381 +        if ($multiple == 2) {
   1.382 +            for (my $i = 0; $i < $channels; $i++) {
   1.383 +                print <<EOF;
   1.384 +        dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   1.385 +EOF
   1.386 +            }
   1.387 +        } elsif ($multiple == 4) {
   1.388 +            # !!! FIXME: interpolate all 4 samples?
   1.389 +            for (my $i = 0; $i < $channels; $i++) {
   1.390 +                print <<EOF;
   1.391 +        dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
   1.392 +EOF
   1.393 +            }
   1.394 +        } else {
   1.395 +            die('bug in program.');  # we only handle x2 and x4.
   1.396 +        }
   1.397 +    }
   1.398 +
   1.399 +    for (my $i = 0; $i < $channels; $i++) {
   1.400 +        my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
   1.401 +        print <<EOF;
   1.402 +        last_sample${idx} = sample${idx};
   1.403 +EOF
   1.404 +    }
   1.405 +
   1.406 +    if ($upsample) {
   1.407 +        my $amount = $channels * $multiple;
   1.408 +        $incr = "dst -= $amount";  # can't ever be 1, so no "--" version.
   1.409 +    } else {
   1.410 +        $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
   1.411 +    }
   1.412 +
   1.413 +    print <<EOF;
   1.414 +        $incr;
   1.415 +    }
   1.416 +
   1.417 +    cvt->len_cvt = dstsize;
   1.418 +    if (cvt->filters[++cvt->filter_index]) {
   1.419 +        cvt->filters[cvt->filter_index] (cvt, format);
   1.420      }
   1.421  }
   1.422  
   1.423 -print <<EOF;
   1.424 -};
   1.425 -
   1.426 -
   1.427  EOF
   1.428  
   1.429 +}
   1.430 +
   1.431 +sub buildResamplers {
   1.432 +    foreach (@audiotypes) {
   1.433 +        my $from = $_;
   1.434 +        foreach (@channels) {
   1.435 +            my $channel = $_;
   1.436 +            for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
   1.437 +                buildMultipleResampleFunc($from, $channel, 1, $multiple);
   1.438 +                buildMultipleResampleFunc($from, $channel, 0, $multiple);
   1.439 +            }
   1.440 +            buildArbitraryResampleFunc($from, $channel, 1);
   1.441 +            buildArbitraryResampleFunc($from, $channel, 0);
   1.442 +        }
   1.443 +    }
   1.444 +
   1.445 +    print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n";
   1.446 +    foreach (@audiotypes) {
   1.447 +        my $from = $_;
   1.448 +        foreach (@channels) {
   1.449 +            my $channel = $_;
   1.450 +            for (my $multiple = 0; $multiple <= 4; $multiple += 2) {
   1.451 +                for (my $upsample = 0; $upsample <= 1; $upsample++) {
   1.452 +                    my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple);
   1.453 +                    my $sym = $funcs{$hashid};
   1.454 +                    print("    { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n");
   1.455 +                }
   1.456 +            }
   1.457 +        }
   1.458 +    }
   1.459 +
   1.460 +    print "};\n\n";
   1.461 +}
   1.462 +
   1.463 +
   1.464 +# mainline ...
   1.465 +
   1.466 +outputHeader();
   1.467 +buildTypeConverters();
   1.468 +buildResamplers();
   1.469  outputFooter();
   1.470  
   1.471  exit 0;