comparison src/audio/sdlgenaudiocvt.pl @ 3008:786a48f8309c

First shot at autogenerated audio resamplers. Don't check in a new SDL_audiotypecvt.c yet, though.
author Ryan C. Gordon <icculus@icculus.org>
date Fri, 09 Jan 2009 15:41:45 +0000
parents 1210d5a28e16
children dfd23eb79be9
comparison
equal deleted inserted replaced
3007:b30409e106f2 3008:786a48f8309c
14 S32MSB 14 S32MSB
15 F32LSB 15 F32LSB
16 F32MSB 16 F32MSB
17 ); 17 );
18 18
19 my @channels = ( 1, 2, 4, 6, 8 );
19 my %funcs; 20 my %funcs;
20
21 my $custom_converters = 0; 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 }
22 34
23 35
24 sub outputHeader { 36 sub outputHeader {
25 print <<EOF; 37 print <<EOF;
26 /* DO NOT EDIT! This file is generated by sdlgenaudiocvt.pl */ 38 /* DO NOT EDIT! This file is generated by sdlgenaudiocvt.pl */
64 print("\n"); 76 print("\n");
65 } 77 }
66 78
67 sub outputFooter { 79 sub outputFooter {
68 print <<EOF; 80 print <<EOF;
81 /* $custom_converters converters generated. */
82
69 /* *INDENT-ON* */ 83 /* *INDENT-ON* */
70 84
71 /* vi: set ts=4 sw=4 expandtab: */ 85 /* vi: set ts=4 sw=4 expandtab: */
72 EOF 86 EOF
73 } 87 }
160 $diffs++ if ($ffloat != $tfloat); 174 $diffs++ if ($ffloat != $tfloat);
161 $diffs++ if ($fendian ne $tendian); 175 $diffs++ if ($fendian ne $tendian);
162 176
163 return if ($diffs == 0); 177 return if ($diffs == 0);
164 178
165 my $hashid = "$from/$to"; 179 my $hashid = getTypeConvertHashId($from, $to);
166 if (1) { # !!! FIXME: if ($diffs > 1) { 180 if (1) { # !!! FIXME: if ($diffs > 1) {
167 my $sym = "SDL_Convert_${from}_to_${to}"; 181 my $sym = "SDL_Convert_${from}_to_${to}";
168 $funcs{$hashid} = $sym; 182 $funcs{$hashid} = $sym;
169 $custom_converters++; 183 $custom_converters++;
170 184
272 die("error in script.\n"); 286 die("error in script.\n");
273 } 287 }
274 } 288 }
275 } 289 }
276 290
291
292 sub buildTypeConverters {
293 foreach (@audiotypes) {
294 my $from = $_;
295 foreach (@audiotypes) {
296 my $to = $_;
297 buildCvtFunc($from, $to);
298 }
299 }
300
301 print "const SDL_AudioTypeFilters sdl_audio_type_filters[] =\n{\n";
302 foreach (@audiotypes) {
303 my $from = $_;
304 foreach (@audiotypes) {
305 my $to = $_;
306 if ($from ne $to) {
307 my $hashid = getTypeConvertHashId($from, $to);
308 my $sym = $funcs{$hashid};
309 print(" { AUDIO_$from, AUDIO_$to, $sym },\n");
310 }
311 }
312 }
313
314 print "};\n\n\n";
315 }
316
317 sub getBiggerCtype {
318 my ($isfloat, $size) = @_;
319
320 if ($isfloat) {
321 if ($size == 32) {
322 return 'double';
323 }
324 die("bug in script.\n");
325 }
326
327 if ($size == 8) {
328 return 'Sint16';
329 } elsif ($size == 16) {
330 return 'Sint32'
331 } elsif ($size == 32) {
332 return 'Sint64'
333 }
334
335 die("bug in script.\n");
336 }
337
338
339 # These handle arbitrary resamples...44100Hz to 48000Hz, for example.
340 # Man, this code is skanky.
341 sub buildArbitraryResampleFunc {
342 # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
343 my ($from, $channels, $upsample) = @_;
344 my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
345
346 my $bigger = getBiggerCtype($ffloat, $fsize);
347 my $interp = ($ffloat) ? '* 0.5' : '>> 1';
348
349 my $resample = ($upsample) ? 'Upsample' : 'Downsample';
350 my $hashid = getResamplerHashId($from, $channels, $upsample, 0);
351 my $sym = "SDL_${resample}_${from}_${channels}c";
352 $funcs{$hashid} = $sym;
353 $custom_converters++;
354
355 my $fudge = $fsize * $channels * 2; # !!! FIXME
356 my $eps_adjust = ($upsample) ? 'dstsize' : 'srcsize';
357 my $incr = '';
358 my $incr2 = '';
359
360
361 # !!! FIXME: DEBUG_CONVERT should report frequencies.
362 print <<EOF;
363 static void SDLCALL
364 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
365 {
366 #ifdef DEBUG_CONVERT
367 fprintf(stderr, "$resample arbitrary (x%f) AUDIO_${from}, ${channels} channels.\\n", cvt->rate_incr);
368 #endif
369
370 const int srcsize = cvt->len_cvt - $fudge;
371 const int dstsize = (int) (((double)cvt->len_cvt) * cvt->rate_incr);
372 register int eps = 0;
373 EOF
374
375 # Upsampling (growing the buffer) needs to work backwards, since we
376 # overwrite the buffer as we go.
377 if ($upsample) {
378 print <<EOF;
379 $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
380 const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
381 const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
382 EOF
383 } else {
384 print <<EOF;
385 $fctype *dst = ($fctype *) cvt->buf;
386 const $fctype *src = ($fctype *) cvt->buf;
387 const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
388 EOF
389 }
390
391 for (my $i = 0; $i < $channels; $i++) {
392 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
393 my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
394 print <<EOF;
395 $fctype sample${idx} = $val;
396 EOF
397 }
398
399 for (my $i = 0; $i < $channels; $i++) {
400 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
401 print <<EOF;
402 $fctype last_sample${idx} = sample${idx};
403 EOF
404 }
405
406 print <<EOF;
407 while (dst != target) {
408 EOF
409
410 if ($upsample) {
411 for (my $i = 0; $i < $channels; $i++) {
412 # !!! FIXME: don't do this swap every write, just when the samples change.
413 my $idx = (($channels - $i) - 1);
414 my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}");
415 print <<EOF;
416 dst[$idx] = $val;
417 EOF
418 }
419
420 $incr = ($channels == 1) ? 'dst--' : "dst -= $channels";
421 $incr2 = ($channels == 1) ? 'src--' : "src -= $channels";
422
423 print <<EOF;
424 $incr;
425 eps += srcsize;
426 if ((eps << 1) >= dstsize) {
427 $incr2;
428 EOF
429 } else { # downsample.
430 $incr = ($channels == 1) ? 'src++' : "src += $channels";
431 print <<EOF;
432 $incr;
433 eps += dstsize;
434 if ((eps << 1) >= srcsize) {
435 EOF
436 for (my $i = 0; $i < $channels; $i++) {
437 my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}");
438 print <<EOF;
439 dst[$i] = $val;
440 EOF
441 }
442
443 $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
444 print <<EOF;
445 $incr;
446 EOF
447 }
448
449 for (my $i = 0; $i < $channels; $i++) {
450 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
451 my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
452 print <<EOF;
453 sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp);
454 EOF
455 }
456
457 for (my $i = 0; $i < $channels; $i++) {
458 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
459 print <<EOF;
460 last_sample${idx} = sample${idx};
461 EOF
462 }
463
464 print <<EOF;
465 eps -= $eps_adjust;
466 }
467 }
468 EOF
469
470 print <<EOF;
471 cvt->len_cvt = dstsize;
472 if (cvt->filters[++cvt->filter_index]) {
473 cvt->filters[cvt->filter_index] (cvt, format);
474 }
475 }
476
477 EOF
478
479 }
480
481 # These handle clean resamples...doubling and quadrupling the sample rate, etc.
482 sub buildMultipleResampleFunc {
483 # !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
484 my ($from, $channels, $upsample, $multiple) = @_;
485 my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
486
487 my $bigger = getBiggerCtype($ffloat, $fsize);
488 my $interp = ($ffloat) ? '* 0.5' : '>> 1';
489 my $interp2 = ($ffloat) ? '* 0.25' : '>> 2';
490 my $mult3 = ($ffloat) ? '3.0' : '3';
491 my $lencvtop = ($upsample) ? '*' : '/';
492
493 my $resample = ($upsample) ? 'Upsample' : 'Downsample';
494 my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple);
495 my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}";
496 $funcs{$hashid} = $sym;
497 $custom_converters++;
498
499 # !!! FIXME: DEBUG_CONVERT should report frequencies.
500 print <<EOF;
501 static void SDLCALL
502 ${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
503 {
504 #ifdef DEBUG_CONVERT
505 fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n");
506 #endif
507
508 const int srcsize = cvt->len_cvt;
509 const int dstsize = cvt->len_cvt $lencvtop $multiple;
510 EOF
511
512 # Upsampling (growing the buffer) needs to work backwards, since we
513 # overwrite the buffer as we go.
514 if ($upsample) {
515 print <<EOF;
516 $fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
517 const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
518 const $fctype *target = ((const $fctype *) cvt->buf) - $channels;
519 EOF
520 } else {
521 print <<EOF;
522 $fctype *dst = ($fctype *) cvt->buf;
523 const $fctype *src = ($fctype *) cvt->buf;
524 const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
525 EOF
526 }
527
528 for (my $i = 0; $i < $channels; $i++) {
529 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
530 my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
531 print <<EOF;
532 $bigger last_sample${idx} = ($bigger) $val;
533 EOF
534 }
535
536 print <<EOF;
537 while (dst != target) {
538 EOF
539
540 for (my $i = 0; $i < $channels; $i++) {
541 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
542 my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
543 print <<EOF;
544 const $bigger sample${idx} = ($bigger) $val;
545 EOF
546 }
547
548 my $incr = '';
549 if ($upsample) {
550 $incr = ($channels == 1) ? 'src--' : "src -= $channels";
551 } else {
552 my $amount = $channels * $multiple;
553 $incr = "src += $amount"; # can't ever be 1, so no "++" version.
554 }
555
556
557 print <<EOF;
558 $incr;
559 EOF
560
561 # !!! FIXME: This really begs for some Altivec or SSE, etc.
562 if ($upsample) {
563 if ($multiple == 2) {
564 for (my $i = $channels-1; $i >= 0; $i--) {
565 my $dsti = $i + $channels;
566 print <<EOF;
567 dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
568 EOF
569 }
570 for (my $i = $channels-1; $i >= 0; $i--) {
571 my $dsti = $i;
572 print <<EOF;
573 dst[$dsti] = ($fctype) sample${i};
574 EOF
575 }
576 } elsif ($multiple == 4) {
577 for (my $i = $channels-1; $i >= 0; $i--) {
578 my $dsti = $i + ($channels * 3);
579 print <<EOF;
580 dst[$dsti] = ($fctype) sample${i};
581 EOF
582 }
583
584 for (my $i = $channels-1; $i >= 0; $i--) {
585 my $dsti = $i + ($channels * 2);
586 print <<EOF;
587 dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2);
588 EOF
589 }
590
591 for (my $i = $channels-1; $i >= 0; $i--) {
592 my $dsti = $i + ($channels * 1);
593 print <<EOF;
594 dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
595 EOF
596 }
597
598 for (my $i = $channels-1; $i >= 0; $i--) {
599 my $dsti = $i + ($channels * 0);
600 print <<EOF;
601 dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2);
602 EOF
603 }
604 } else {
605 die('bug in program.'); # we only handle x2 and x4.
606 }
607 } else { # downsample.
608 if ($multiple == 2) {
609 for (my $i = 0; $i < $channels; $i++) {
610 print <<EOF;
611 dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
612 EOF
613 }
614 } elsif ($multiple == 4) {
615 # !!! FIXME: interpolate all 4 samples?
616 for (my $i = 0; $i < $channels; $i++) {
617 print <<EOF;
618 dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
619 EOF
620 }
621 } else {
622 die('bug in program.'); # we only handle x2 and x4.
623 }
624 }
625
626 for (my $i = 0; $i < $channels; $i++) {
627 my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
628 print <<EOF;
629 last_sample${idx} = sample${idx};
630 EOF
631 }
632
633 if ($upsample) {
634 my $amount = $channels * $multiple;
635 $incr = "dst -= $amount"; # can't ever be 1, so no "--" version.
636 } else {
637 $incr = ($channels == 1) ? 'dst++' : "dst += $channels";
638 }
639
640 print <<EOF;
641 $incr;
642 }
643
644 cvt->len_cvt = dstsize;
645 if (cvt->filters[++cvt->filter_index]) {
646 cvt->filters[cvt->filter_index] (cvt, format);
647 }
648 }
649
650 EOF
651
652 }
653
654 sub buildResamplers {
655 foreach (@audiotypes) {
656 my $from = $_;
657 foreach (@channels) {
658 my $channel = $_;
659 for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
660 buildMultipleResampleFunc($from, $channel, 1, $multiple);
661 buildMultipleResampleFunc($from, $channel, 0, $multiple);
662 }
663 buildArbitraryResampleFunc($from, $channel, 1);
664 buildArbitraryResampleFunc($from, $channel, 0);
665 }
666 }
667
668 print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n";
669 foreach (@audiotypes) {
670 my $from = $_;
671 foreach (@channels) {
672 my $channel = $_;
673 for (my $multiple = 0; $multiple <= 4; $multiple += 2) {
674 for (my $upsample = 0; $upsample <= 1; $upsample++) {
675 my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple);
676 my $sym = $funcs{$hashid};
677 print(" { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n");
678 }
679 }
680 }
681 }
682
683 print "};\n\n";
684 }
685
686
687 # mainline ...
688
277 outputHeader(); 689 outputHeader();
278 690 buildTypeConverters();
279 foreach (@audiotypes) { 691 buildResamplers();
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 outputFooter(); 692 outputFooter();
311 693
312 exit 0; 694 exit 0;
313 695
314 # end of sdlgenaudiocvt.pl ... 696 # end of sdlgenaudiocvt.pl ...