use warnings; use strict; # Make sure we hear about problems my $sample_rate = 44100; # CD Audio sample rate my $bits_sample = 16; # CD Audio bit depth my $channels = 2; # Stereo = 2 channels my $outfile = 'testout.wav'; # file name to generate my $binaud = ''; # start with an empty sample string my $block_align = $channels * $bits_sample / 8; # how many bytes per block my $bytes_sec = $sample_rate * $block_align; # how many bytes per second my $fmt_length = 16; # 16 bytes in format block my $encoding = 1; # PCM encoding (uncompressed) # call the subroutine to add sine wave samples at specified frequency and duration &add_sine(261.626, 0.5 ); # .5 second of middle C &add_sine(293.665, 0.5 ); # .5 second of D &add_sine(329.628, 0.5 ); # .5 second of E &add_sine(349.228, 0.5 ); # .5 second of F &add_sine(391.995, 0.5 ); # .5 second of G &add_sine(440.000, 0.5 ); # .5 second of A &add_sine(493.883, 0.5 ); # .5 second of B &add_sine(523.251, 0.5 ); # .5 second of C &add_sine( 15 , 5.0 ); # 5 seconds of 15Hz = 65 cycles &add_sine( 20 , 1.0 ); # 1 second of 20Hz &add_sine( 30 , 1.0 ); # 1 second of 30Hz &add_sine( 40 , 1.0 ); # 1 second of 40Hz &add_sine( 50 , 1.0 ); # 1 second of 50Hz &add_sine( 60 , 1.0 ); # 1 second of 60Hz &add_sine( 70 , 1.0 ); # 1 second of 70Hz &add_sine( 80 , 1.0 ); # 1 second of 80Hz &add_sine( 90 , 1.0 ); # 1 second of 90Hz &add_sine(100 , 1.0 ); # 1 second of 100Hz &add_spiro(0.75,0.25,0.24,30,100); # "outer ring size", "inner ring size", "pen offset", "Speed", "Duration" # create the WAV file according to the settings &write_file(); # subroutines to do the work sub write_file { open FILE, ">", $outfile or die $!; binmode FILE; # tell perl not to worry about unicode my $binlength = length($binaud); # size of the samples block print FILE "RIFF" ; # 52 49 46 46 RIFF file header # pack() uses the local machine endian order for l(ong, 32) and s(hort,16) print FILE pack("l",($binlength + 36)); # xx xx xx xx Remaining file size print FILE "WAVE" ; # 57 41 56 45 Content Flag print FILE "fmt " ; # 66 6D 74 20 begin Format chunk print FILE pack("l",$fmt_length) ; # 10 00 00 00 always 16 bytes print FILE pack("s",$encoding) ; # 01 00 1 = PCM print FILE pack("s",$channels) ; # 02 00 2 = Stereo print FILE pack("l",$sample_rate) ; # 44 AC 00 00 44100 (CD) print FILE pack("l",$bytes_sec) ; # 10 B1 02 00 44100 * 4 bytes print FILE pack("s",$block_align) ; # 04 00 4 bytes for two 16 bit samples print FILE pack("s",$bits_sample) ; # 10 00 16 bits/sample print FILE "data" ; # 64 61 74 61 begin Data chunk print FILE pack("l",$binlength) ; # xx xx xx xx length of sample data print FILE $binaud ; # xx xx xx xx sample data... close FILE; } sub add_sine { my $hz = shift; my $length = shift; my $pi = ( 22 / 7 ) * 2; $length *= $sample_rate; my $max_no = (( 2 ** $bits_sample ) / 2) - 1; my $time = 0; for my $pos ( 0 .. $length ) { $time = $pos / $sample_rate; $time *= $hz; my $valx = sin $pi * $time; my $sampx = $valx * $max_no; my $valy = cos $pi * $time; my $sampy = $valy * $max_no; $binaud .= pack("s2",$sampx,$sampy); } } sub add_spiro { # "outer ring size", "inner ring size", "pen offset", "Speed", "Duration" my $outerR = shift; # primary gear my $innerR = shift; # secondary or roller gear my $rmax = ((( 2 ** $bits_sample ) / 2) - 1); my $r1 = $outerR * $rmax; # keep outerR between 0 and +- 1 my $r2 = $innerR * $rmax; # keep innerR between 0 and +-(1-outerR) my $penhole = shift; # keep penhole less than innerR for best results, my $rp = $penhole * $rmax;# penhole + outerR <= 1 to avoid clipping my $hz = shift; my $length = shift; my $pi = ( 22 / 7 ) * 2; $length *= $sample_rate; my $time = 0; for my $pos ( 0 .. $length ) { $time = $pos / $sample_rate * $hz; my $sampx = $r1 * sin ($pi * $time) + $rp * sin($pi * $time * ($r1/$r2)); my $sampy = $r1 * cos ($pi * $time) + $rp * cos($pi * $time * ($r1/$r2)); $binaud .= pack("s2",$sampx,$sampy); } }