blob: 83132fbae075519bbfde137b28b597cf394cdc92 [file] [log] [blame]
Josh Coalson7617cac2008-09-09 07:24:23 +00001#!/usr/bin/perl -w
2
3use strict;
4
5require Math::BigInt;
6
7my $usage = "
8$0 <format> <bps> <channels> <sample-rate> <#samples> <sample-type>
9
10 <format> is one of aiff,wave,rf64
11 <bps> is 8,16,24,32
12 <channels> is 1-8
13<sample-rate> is any 32-bit value
14 <#samples> is 0-2^64-1
15<sample-type> is one of zero,rand
16
17";
18
19die $usage unless @ARGV == 6;
20
21my %formats = ( 'aiff'=>1, 'wave'=>1, 'rf64'=>1 );
22my %sampletypes = ( 'zero'=>1, 'rand'=>1 );
23my @channelmask = ( 0, 1, 3, 7, 0x33, 0x607, 0x60f, 0, 0 ); #@@@@@@ need proper masks for 7,8
24
25my ($format, $bps, $channels, $samplerate, $samples, $sampletype) = @ARGV;
26my $bigsamples = new Math::BigInt $samples;
27
28die $usage unless defined $formats{$format};
29die $usage unless $bps == 8 || $bps == 16 || $bps == 24 || $bps == 32;
30die $usage unless $channels >= 1 && $channels <= 8;
31die $usage unless $samplerate >= 0 && $samplerate <= 4294967295;
32die $usage unless defined $sampletypes{$sampletype};
33
34# convert bits-per-sample to bytes-per-sample
35$bps /= 8;
36
37my $datasize = $samples * $bps * $channels;
38my $bigdatasize = $bigsamples * $bps * $channels;
39
40my $padding = int($bigdatasize & 1? 1 : 0);
41my $wavx = ($format eq 'wave' || $format eq 'rf64') && ($channels > 2);
42
43# write header
44
45if ($format eq 'aiff') {
46 die "sample data too big for format\n" if 46 + $datasize + $padding > 4294967295;
47 # header
48 print "FORM";
49 print pack('N', 46 + $datasize + $padding);
50 print "AIFF";
51 # COMM chunk
52 print "COMM";
53 print pack('N', 18); # chunk size = 18
54 print pack('n', $channels);
55 print pack('N', $samples);
56 print pack('n', $bps * 8);
57 print pack_sane_extended($samplerate);
58 # SSND header
59 print "SSND";
60 print pack('N', $datasize + 8); # chunk size
61 print pack('N', 0); # ssnd_offset_size
62 print pack('N', 0); # blocksize
63}
64elsif ($format eq 'wave' || $format eq 'rf64') {
65 die "sample data too big for format\n" if $format eq 'wave' && ($wavx?60:36) + $datasize + $padding > 4294967295;
66 # header
67 if ($format eq 'wave') {
68 print "RIFF";
69 print pack('V', ($wavx?60:36) + $datasize + $padding);
70 print "WAVE";
71 }
72 else {
73 print "RF64";
74 print pack('V', 0xffffffff);
75 print "WAVE";
76 # ds64 chunk
77 print "ds64";
78 print pack('V', 28); # chunk size
79 my $bigriffsize = $bigdatasize + ($wavx?60:36) + (8+28) + $padding;
80 print pack_64('V', $bigriffsize);
81 print pack_64('V', $bigdatasize);
82 print pack_64('V', $bigsamples);
83 print pack('V', 0); # table size
84 }
85 # fmt chunk
86 print "fmt ";
87 print pack('V', $wavx?40:16); # chunk size
88 print pack('v', $wavx?65534:1); # compression code
89 print pack('v', $channels);
90 print pack('V', $samplerate);
91 print pack('V', $samplerate * $channels * $bps);
92 print pack('v', $bps); # block align = channels*((bps+7)/8)
93 print pack('v', $bps * 8); # bits per sample = ((bps+7)/8)*8
94 if ($wavx) {
95 print pack('v', 22); # cbSize
96 print pack('v', $bps * 8); # validBitsPerSample
97 print pack('V', $channelmask[$channels]);
98 # GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}
99 print "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71";
100 }
101 # data header
102 print "data";
103 print pack('V', $format eq 'wave'? $datasize : 0xffffffff);
104}
105else {
106 die;
107}
108
109# write sample data
110
111if ($sampletype eq 'zero') {
112 my $chunk = 4096;
113 my $buf = pack("x[".($channels*$bps*$chunk)."]");
114 for (my $s = $samples; $s > 0; $s -= $chunk) {
115 if ($s < $chunk) {
116 print substr($buf, 0, $channels*$bps*$s);
117 }
118 else {
119 print $buf;
120 }
121 }
122}
123elsif ($sampletype eq 'rand') {
124 for (my $s = 0; $s < $samples; $s++) {
125 for (my $c = 0; $c < $channels; $c++) {
126 for (my $b = 0; $b < $bps; $b++) {
127 print pack('C', int(rand(256)));
128 }
129 }
130 }
131}
132else {
133 die;
134}
135print "\x00" if $padding;
136
137exit 0;
138
139sub pack_sane_extended
140{
141 my $val = shift;
142 die unless $val > 0;
143 my $shift;
144 for ($shift = 0; ($val>>(31-$shift)) == 0; ++$shift) {
145 }
146 $val <<= $shift;
147 my $exponent = 63 - ($shift + 32);
148 return pack('nNN', $exponent + 16383, $val, 0);
149}
150
151sub pack_64
152{
153 my $c = shift;
154 my $v1 = shift;
155 my $v2 = $v1->copy();
156 if ($c eq 'V') {
157 $v1->band(0xffffffff);
158 $v2->brsft(32);
159 }
160 elsif ($c eq 'C') {
161 $v2->band(0xffffffff);
162 $v1->brsft(32);
163 }
164 else {
165 die;
166 }
167 return pack("$c$c", 0+$v1->bstr(), 0+$v2->bstr());
168}