1*600f14f4SXin Li#!/usr/bin/perl -w 2*600f14f4SXin Li 3*600f14f4SXin Liuse strict; 4*600f14f4SXin Li 5*600f14f4SXin Lirequire Math::BigInt; 6*600f14f4SXin Li 7*600f14f4SXin Limy $usage = " 8*600f14f4SXin Li$0 <format> <bps> <channels> <sample-rate> <#samples> <sample-type> 9*600f14f4SXin Li 10*600f14f4SXin Li <format> is one of aiff,wave,wave64,rf64 11*600f14f4SXin Li <bps> is 8,16,24,32 12*600f14f4SXin Li <channels> is 1-8 13*600f14f4SXin Li<sample-rate> is any 32-bit value 14*600f14f4SXin Li <#samples> is 0-2^64-1 15*600f14f4SXin Li<sample-type> is one of zero,rand 16*600f14f4SXin Li 17*600f14f4SXin Li"; 18*600f14f4SXin Li 19*600f14f4SXin Lidie $usage unless @ARGV == 6; 20*600f14f4SXin Li 21*600f14f4SXin Limy %formats = ( 'aiff'=>1, 'wave'=>1, 'wave64'=>1, 'rf64'=>1 ); 22*600f14f4SXin Limy %sampletypes = ( 'zero'=>1, 'rand'=>1 ); 23*600f14f4SXin Limy @channelmask = ( 0, 1, 3, 7, 0x33, 0x607, 0x60f, 0, 0 ); #@@@@@@ need proper masks for 7,8 24*600f14f4SXin Li 25*600f14f4SXin Limy ($format, $bps, $channels, $samplerate, $samples, $sampletype) = @ARGV; 26*600f14f4SXin Limy $bigsamples = new Math::BigInt $samples; 27*600f14f4SXin Li 28*600f14f4SXin Lidie $usage unless defined $formats{$format}; 29*600f14f4SXin Lidie $usage unless $bps == 8 || $bps == 16 || $bps == 24 || $bps == 32; 30*600f14f4SXin Lidie $usage unless $channels >= 1 && $channels <= 8; 31*600f14f4SXin Lidie $usage unless $samplerate >= 0 && $samplerate <= 4294967295; 32*600f14f4SXin Lidie $usage unless defined $sampletypes{$sampletype}; 33*600f14f4SXin Li 34*600f14f4SXin Li# convert bits-per-sample to bytes-per-sample 35*600f14f4SXin Li$bps /= 8; 36*600f14f4SXin Li 37*600f14f4SXin Limy $datasize = $samples * $bps * $channels; 38*600f14f4SXin Limy $bigdatasize = $bigsamples * $bps * $channels; 39*600f14f4SXin Li 40*600f14f4SXin Limy $padding = int($bigdatasize & 1); # for aiff/wave/rf64 chunk alignment 41*600f14f4SXin Limy $padding8 = 8 - int($bigdatasize & 7); $padding8 = 0 if $padding8 == 8; # for wave64 alignment 42*600f14f4SXin Li# wave-ish file needs to be WAVEFORMATEXTENSIBLE? 43*600f14f4SXin Limy $wavx = ($format eq 'wave' || $format eq 'wave64' || $format eq 'rf64') && ($channels > 2 || ($bps != 8 && $bps != 16)); 44*600f14f4SXin Li 45*600f14f4SXin Li# write header 46*600f14f4SXin Li 47*600f14f4SXin Liif ($format eq 'aiff') { 48*600f14f4SXin Li die "sample data too big for format\n" if 46 + $datasize + $padding > 4294967295; 49*600f14f4SXin Li # header 50*600f14f4SXin Li print "FORM"; 51*600f14f4SXin Li print pack('N', 46 + $datasize + $padding); 52*600f14f4SXin Li print "AIFF"; 53*600f14f4SXin Li # COMM chunk 54*600f14f4SXin Li print "COMM"; 55*600f14f4SXin Li print pack('N', 18); # chunk size = 18 56*600f14f4SXin Li print pack('n', $channels); 57*600f14f4SXin Li print pack('N', $samples); 58*600f14f4SXin Li print pack('n', $bps * 8); 59*600f14f4SXin Li print pack_sane_extended($samplerate); 60*600f14f4SXin Li # SSND header 61*600f14f4SXin Li print "SSND"; 62*600f14f4SXin Li print pack('N', $datasize + 8); # chunk size 63*600f14f4SXin Li print pack('N', 0); # ssnd_offset_size 64*600f14f4SXin Li print pack('N', 0); # blocksize 65*600f14f4SXin Li} 66*600f14f4SXin Lielsif ($format eq 'wave' || $format eq 'wave64' || $format eq 'rf64') { 67*600f14f4SXin Li die "sample data too big for format\n" if $format eq 'wave' && ($wavx?60:36) + $datasize + $padding > 4294967295; 68*600f14f4SXin Li # header 69*600f14f4SXin Li if ($format eq 'wave') { 70*600f14f4SXin Li print "RIFF"; 71*600f14f4SXin Li # +4 for WAVE 72*600f14f4SXin Li # +8+{40,16} for fmt chunk 73*600f14f4SXin Li # +8 for data chunk header 74*600f14f4SXin Li print pack('V', 4 + 8+($wavx?40:16) + 8 + $datasize + $padding); 75*600f14f4SXin Li print "WAVE"; 76*600f14f4SXin Li } 77*600f14f4SXin Li elsif ($format eq 'wave64') { 78*600f14f4SXin Li # RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000 79*600f14f4SXin Li print "\x72\x69\x66\x66\x2E\x91\xCF\x11\xD6\xA5\x28\xDB\x04\xC1\x00\x00"; 80*600f14f4SXin Li # +(16+8) for RIFF GUID + size 81*600f14f4SXin Li # +16 for WAVE GUID 82*600f14f4SXin Li # +16+8+{40,16} for fmt chunk 83*600f14f4SXin Li # +16+8 for data chunk header 84*600f14f4SXin Li my $bigriffsize = $bigdatasize + (16+8) + 16 + 16+8+($wavx?40:16) + (16+8) + $padding8; 85*600f14f4SXin Li print pack_64('V', $bigriffsize); 86*600f14f4SXin Li # WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A 87*600f14f4SXin Li print "\x77\x61\x76\x65\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A"; 88*600f14f4SXin Li } 89*600f14f4SXin Li else { 90*600f14f4SXin Li print "RF64"; 91*600f14f4SXin Li print pack('V', 0xffffffff); 92*600f14f4SXin Li print "WAVE"; 93*600f14f4SXin Li # ds64 chunk 94*600f14f4SXin Li print "ds64"; 95*600f14f4SXin Li print pack('V', 28); # chunk size 96*600f14f4SXin Li # +4 for WAVE 97*600f14f4SXin Li # +(8+28) for ds64 chunk 98*600f14f4SXin Li # +8+{40,16} for fmt chunk 99*600f14f4SXin Li # +8 for data chunk header 100*600f14f4SXin Li my $bigriffsize = $bigdatasize + 4 + (8+28) + 8+($wavx?40:16) + 8 + $padding; 101*600f14f4SXin Li print pack_64('V', $bigriffsize); 102*600f14f4SXin Li print pack_64('V', $bigdatasize); 103*600f14f4SXin Li print pack_64('V', $bigsamples); 104*600f14f4SXin Li print pack('V', 0); # table size 105*600f14f4SXin Li } 106*600f14f4SXin Li # fmt chunk 107*600f14f4SXin Li if ($format ne 'wave64') { 108*600f14f4SXin Li print "fmt "; 109*600f14f4SXin Li print pack('V', $wavx?40:16); # chunk size 110*600f14f4SXin Li } 111*600f14f4SXin Li else { # wave64 112*600f14f4SXin Li # fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A 113*600f14f4SXin Li print "\x66\x6D\x74\x20\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A"; 114*600f14f4SXin Li print pack('V', 16+8+($wavx?40:16)); # chunk size (+16+8 for GUID and size fields) 115*600f14f4SXin Li print pack('V', 0); # ...is 8 bytes for wave64 116*600f14f4SXin Li } 117*600f14f4SXin Li print pack('v', $wavx?65534:1); # compression code 118*600f14f4SXin Li print pack('v', $channels); 119*600f14f4SXin Li print pack('V', $samplerate); 120*600f14f4SXin Li print pack('V', $samplerate * $channels * $bps); 121*600f14f4SXin Li print pack('v', $channels * $bps); # block align = channels*((bps+7)/8) 122*600f14f4SXin Li print pack('v', $bps * 8); # bits per sample = ((bps+7)/8)*8 123*600f14f4SXin Li if ($wavx) { 124*600f14f4SXin Li print pack('v', 22); # cbSize 125*600f14f4SXin Li print pack('v', $bps * 8); # validBitsPerSample 126*600f14f4SXin Li print pack('V', $channelmask[$channels]); 127*600f14f4SXin Li # GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} 128*600f14f4SXin Li print "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"; 129*600f14f4SXin Li } 130*600f14f4SXin Li # data header 131*600f14f4SXin Li if ($format ne 'wave64') { 132*600f14f4SXin Li print "data"; 133*600f14f4SXin Li print pack('V', $format eq 'wave'? $datasize : 0xffffffff); 134*600f14f4SXin Li } 135*600f14f4SXin Li else { # wave64 136*600f14f4SXin Li # data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A 137*600f14f4SXin Li print "\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A"; 138*600f14f4SXin Li print pack_64('V', $bigdatasize+16+8); # +16+8 for GUID and size fields 139*600f14f4SXin Li } 140*600f14f4SXin Li} 141*600f14f4SXin Lielse { 142*600f14f4SXin Li die; 143*600f14f4SXin Li} 144*600f14f4SXin Li 145*600f14f4SXin Li# write sample data 146*600f14f4SXin Li 147*600f14f4SXin Liif ($sampletype eq 'zero') { 148*600f14f4SXin Li my $chunk = 4096; 149*600f14f4SXin Li my $buf = pack("x[".($channels*$bps*$chunk)."]"); 150*600f14f4SXin Li for (my $s = $samples; $s > 0; $s -= $chunk) { 151*600f14f4SXin Li if ($s < $chunk) { 152*600f14f4SXin Li print substr($buf, 0, $channels*$bps*$s); 153*600f14f4SXin Li } 154*600f14f4SXin Li else { 155*600f14f4SXin Li print $buf; 156*600f14f4SXin Li } 157*600f14f4SXin Li } 158*600f14f4SXin Li} 159*600f14f4SXin Lielsif ($sampletype eq 'rand') { 160*600f14f4SXin Li for (my $s = 0; $s < $samples; $s++) { 161*600f14f4SXin Li for (my $c = 0; $c < $channels; $c++) { 162*600f14f4SXin Li for (my $b = 0; $b < $bps; $b++) { 163*600f14f4SXin Li print pack('C', int(rand(256))); 164*600f14f4SXin Li } 165*600f14f4SXin Li } 166*600f14f4SXin Li } 167*600f14f4SXin Li} 168*600f14f4SXin Lielse { 169*600f14f4SXin Li die; 170*600f14f4SXin Li} 171*600f14f4SXin Li 172*600f14f4SXin Li# write padding 173*600f14f4SXin Liif ($format eq 'wave64') { 174*600f14f4SXin Li print pack("x[$padding8]") if $padding8; 175*600f14f4SXin Li} 176*600f14f4SXin Lielse { 177*600f14f4SXin Li print "\x00" if $padding; 178*600f14f4SXin Li} 179*600f14f4SXin Li 180*600f14f4SXin Liexit 0; 181*600f14f4SXin Li 182*600f14f4SXin Lisub pack_sane_extended 183*600f14f4SXin Li{ 184*600f14f4SXin Li my $val = shift; 185*600f14f4SXin Li die unless $val > 0; 186*600f14f4SXin Li my $shift; 187*600f14f4SXin Li for ($shift = 0; ($val>>(31-$shift)) == 0; ++$shift) { 188*600f14f4SXin Li } 189*600f14f4SXin Li $val <<= $shift; 190*600f14f4SXin Li my $exponent = 63 - ($shift + 32); 191*600f14f4SXin Li return pack('nNN', $exponent + 16383, $val, 0); 192*600f14f4SXin Li} 193*600f14f4SXin Li 194*600f14f4SXin Lisub pack_64 195*600f14f4SXin Li{ 196*600f14f4SXin Li my $c = shift; # 'N' for big-endian, 'V' for little-endian, ala pack() 197*600f14f4SXin Li my $v1 = shift; # value, must be Math::BigInt 198*600f14f4SXin Li my $v2 = $v1->copy(); 199*600f14f4SXin Li if ($c eq 'V') { 200*600f14f4SXin Li $v1->band(0xffffffff); 201*600f14f4SXin Li $v2->brsft(32); 202*600f14f4SXin Li } 203*600f14f4SXin Li elsif ($c eq 'N') { 204*600f14f4SXin Li $v2->band(0xffffffff); 205*600f14f4SXin Li $v1->brsft(32); 206*600f14f4SXin Li } 207*600f14f4SXin Li else { 208*600f14f4SXin Li die; 209*600f14f4SXin Li } 210*600f14f4SXin Li return pack("$c$c", 0+$v1->bstr(), 0+$v2->bstr()); 211*600f14f4SXin Li} 212