#!/usr/bin/perl -w print "Content-type: text/html\r\n\r\n"; #radiohead song generator #has all the songs, but not the Demo Songs nor the covers (not sure the demo songs belong in there, and the #covers really don't seem to belong there) #includes use XML::DOM; #rough estimate of line numbers in Fake Plastic Trees #can later figure out the average on the fly, or generate a weighted random based on the real lengths. #$maxLines = 43*5;#for some reason this needs to be multiplied by vaguely the number of letters in the wordlength you choose $maxLines = 137; #setup parser, load songlist and turn into an XML document $parser = new XML::DOM::Parser; $doc = $parser->parsefile ("radiohead.xml"); ########################### #pass in album name and then get all the songs names for that album #return one long string of them ########################### sub getSongNames{ my $inputAlbumName = shift(@_); my $albumNodes = $doc->getElementsByTagName ("album"); my $n = $albumNodes->getLength; my $albumNode; my $albumName; my $songNodes; my $nn; my $x; for (my $i = 0; $i < $n; $i++) { $albumNode = $albumNodes->item ($i); if(lc($albumNode->getNodeName()) eq "album"){ $albumName = $albumNode->getAttributeNode("name"); $albumName = $albumName->getValue; #if the name matches what was passed in if(lc($albumName) eq lc($inputAlbumName)){ #we now match what was passed in - now find all the song nodes $songNodes = $albumNode->getChildNodes(); $nn = $songNodes->getLength(); for($x = 0;$x < $nn; $x++){ $songNode = $songNodes->item($x); if(lc($songNode->getNodeName()) eq 'song'){ $songNameArray[@songNameArray]=$songNode->getAttributeNode("name")->getValue(); } } } } } return join(' ', @songNameArray); } ########################### ########################### #pass in album name and then get all the songs for that album #return one long string of them ########################### sub getSongs{ my $inputAlbumName = shift(@_); my $albumNodes = $doc->getElementsByTagName ("album"); my $n = $albumNodes->getLength; my $i = 0; my $x = 0; my $albumNode; my $albumName; my $songNodes; my $nn = 0; for ($i = 0; $i < $n; $i++) { $albumNode = $albumNodes->item ($i); if(lc($albumNode->getNodeName()) eq "album"){ $albumName = $albumNode->getAttributeNode("name"); $albumName = $albumName->getValue; #if the name matches what was passed in if(lc($albumName) eq lc($inputAlbumName)){ #we now match what was passed in - now find all the song nodes $songNodes = $albumNode->getChildNodes(); $nn = $songNodes->getLength(); for($x = 0;$x < $nn; $x++){ $songNode = $songNodes->item($x); if(lc($songNode->getNodeName()) eq 'song'){ $songArray[@songArray]=$songNode->getFirstChild->getNodeValue(); } } } } } return join(' ', @songArray); } ########################### ########################### #pass in band name and then get all the albums for that band #take each album and pass it into getSongs and collect the returned strings #looks at 'words' of size 1 at a time (a single char) ########################### sub createSongHashMono{ my $inputBandName = shift(@_); my $bandNodes = $doc->getElementsByTagName ("band"); my $n = $bandNodes->getLength; my $i; my $bandName; my $albumNodes; my $nn = 0; my $x; my $albumName; for ($i = 0; $i < $n; $i++) { my $bandNode = $bandNodes->item ($i); if(lc($bandNode->getNodeName()) eq "band"){ $bandName = $bandNode->getAttributeNode("name"); $bandName = $bandName->getValue; #if the name matches what was passed in if(lc($bandName) eq lc($inputBandName)){ #we now match what was passed in - now find all the album nodes $albumNodes = $bandNode->getChildNodes(); $nn = $albumNodes->getLength(); for($x = 0;$x < $nn; $x++){ $albumNode = $albumNodes->item($x); if(lc($albumNode->getNodeName()) eq 'album'){ $albumName = $albumNode->getAttributeNode("name")->getValue(); $allLyrics .= ' '.getSongs($albumName); } } } } } #now take $allLyrics and split it into an array @allLyricsArray = split('', $allLyrics); #iterate the array and fill a hash, looking at the $_ and the one to the right for($x = 0; $x < $#allLyricsArray; $x++){ #make sure that the $pos2 isn't gonna be out of the array if($x+1 < $#allLyricsArray){ $pos1 = $allLyricsArray[$x]; $pos2 = $allLyricsArray[$x+1]; } else{ $pos1 = $allLyricsArray[$x]; $pos2 = '**NULL**';#end of array, so this one won't be charted } $mainHash{$pos1}{$pos2}++; } return %mainHash; } ########################### ########################### #pass in band name and then get all the albums for that band #take each album and pass it into getSongs and collect the returned strings #looks at 'words' of size 4 at a time ########################### sub createSongHashQuad{ my $inputBandName = shift(@_); my $bandNodes = $doc->getElementsByTagName ("band"); my $n = $bandNodes->getLength; my $i; my $bandName; my $albumNodes; my $nn; my $x; my $albumName; for ($i = 0; $i < $n; $i++) { $bandNode = $bandNodes->item ($i); if(lc($bandNode->getNodeName()) eq "band"){ my $bandName = $bandNode->getAttributeNode("name"); $bandName = $bandName->getValue; #if the name matches what was passed in if(lc($bandName) eq lc($inputBandName)){ #we now match what was passed in - now find all the album nodes $albumNodes = $bandNode->getChildNodes(); $nn = $albumNodes->getLength(); for($x = 0;$x < $nn; $x++){ $albumNode = $albumNodes->item($x); if(lc($albumNode->getNodeName()) eq 'album'){ $albumName = $albumNode->getAttributeNode("name")->getValue(); $allLyrics .= ' '.getSongs($albumName); } } } } } $allLyrics =~ tr/[]()\"//d; #now take $allLyrics and split it into an array @allLyricsArray = split('', $allLyrics); #iterate the array and fill a hash, looking at the $_ and the one to the right for($x = 0; $x < $#allLyricsArray; $x++){ #make sure that the $pos2 isn't gonna be out of the array if($x+7 <= $#allLyricsArray){ $pos1 = $allLyricsArray[$x].$allLyricsArray[$x+1].$allLyricsArray[$x+2].$allLyricsArray[$x+3]; $pos2 = $allLyricsArray[$x+4].$allLyricsArray[$x+5].$allLyricsArray[$x+6].$allLyricsArray[$x+7]; $mainHash{$pos1}{$pos2}++; } } return %mainHash; } ########################### ########################### #pass in band name and then get all the albums for that band #take each album and pass it into getSongs and collect the returned strings #looks at 'words' of size 5 at a time ########################### sub createSongHashPent{ my $inputBandName = shift(@_); my $bandNodes = $doc->getElementsByTagName ("band"); my $n = $bandNodes->getLength; my $i; my $bandNode; my $bandName; my $albumNodes; my $nn = 0; my $x = 0; my $albumName; for ($i = 0; $i < $n; $i++) { $bandNode = $bandNodes->item ($i); if(lc($bandNode->getNodeName()) eq "band"){ $bandName = $bandNode->getAttributeNode("name"); $bandName = $bandName->getValue; #if the name matches what was passed in if(lc($bandName) eq lc($inputBandName)){ #we now match what was passed in - now find all the album nodes $albumNodes = $bandNode->getChildNodes(); $nn = $albumNodes->getLength(); for($x = 0;$x < $nn; $x++){ $albumNode = $albumNodes->item($x); if(lc($albumNode->getNodeName()) eq 'album'){ $albumName = $albumNode->getAttributeNode("name")->getValue(); $allLyrics .= ' '.getSongs($albumName); } } } } } $allLyrics =~ tr/[]()\"//d; #now take $allLyrics and split it into an array @allLyricsArray = split('', $allLyrics); #iterate the array and fill a hash, looking at the $_ and the one to the right for($x = 0; $x < $#allLyricsArray; $x++){ #make sure that the $pos2 isn't gonna be out of the array if($x+9 <= $#allLyricsArray){ $pos1 = $allLyricsArray[$x].$allLyricsArray[$x+1].$allLyricsArray[$x+2].$allLyricsArray[$x+3].$allLyricsArray[$x+4]; $pos2 = $allLyricsArray[$x+5].$allLyricsArray[$x+6].$allLyricsArray[$x+7].$allLyricsArray[$x+8].$allLyricsArray[$x+9]; $mainHash{$pos1}{$pos2}++; } } return %mainHash; } ########################### ########################### #pass in band name and then get all the albums for that band #take each album and pass it into getSongs and collect the returned strings #looks at 'words' of size 3 at a time ########################### sub createSongHashTri{ my $inputBandName = shift(@_); my $bandNodes = $doc->getElementsByTagName ("band"); my $n = $bandNodes->getLength; my $i = 0; my $bandName; my $albumNodes; my $nn = 0; my $x = 0; my $albumName; for ($i = 0; $i < $n; $i++) { my $bandNode = $bandNodes->item ($i); if(lc($bandNode->getNodeName()) eq "band"){ $bandName = $bandNode->getAttributeNode("name"); $bandName = $bandName->getValue; #if the name matches what was passed in if(lc($bandName) eq lc($inputBandName)){ #we now match what was passed in - now find all the album nodes $albumNodes = $bandNode->getChildNodes(); $nn = $albumNodes->getLength(); for($x = 0;$x < $nn; $x++){ $albumNode = $albumNodes->item($x); if(lc($albumNode->getNodeName()) eq 'album'){ $albumName = $albumNode->getAttributeNode("name")->getValue(); $allLyrics .= ' '.getSongs($albumName); } } } } } $allLyrics =~ tr/[]()\"//d; #now take $allLyrics and split it into an array @allLyricsArray = split('', $allLyrics); #iterate the array and fill a hash, looking at the $_ and the one to the right for($x = 0; $x < $#allLyricsArray; $x++){ #make sure that the $pos2 isn't gonna be out of the array if($x+5 <= $#allLyricsArray){ $pos1 = $allLyricsArray[$x].$allLyricsArray[$x+1].$allLyricsArray[$x+2]; $pos2 = $allLyricsArray[$x+3].$allLyricsArray[$x+4].$allLyricsArray[$x+5]; $mainHash{$pos1}{$pos2}++; } } return %mainHash; } ########################### ########################### #pass in band name and then get all the albums for that band #take each album and pass it into getSongs and collect the returned strings #looks at 'words' of size 2 at a time ########################### sub createSongHashDuo{ my $inputBandName = shift(@_); my $bandNodes = $doc->getElementsByTagName ("band"); my $n = $bandNodes->getLength; my $i = 0; my $bandNode; my $bandName; my $albumNodes; my $nn = 0; my $x = 0; my $albumName; for ($i = 0; $i < $n; $i++) { $bandNode = $bandNodes->item ($i); if(lc($bandNode->getNodeName()) eq "band"){ $bandName = $bandNode->getAttributeNode("name"); $bandName = $bandName->getValue; #if the name matches what was passed in if(lc($bandName) eq lc($inputBandName)){ #we now match what was passed in - now find all the album nodes $albumNodes = $bandNode->getChildNodes(); $nn = $albumNodes->getLength(); for($x = 0;$x < $nn; $x++){ $albumNode = $albumNodes->item($x); if(lc($albumNode->getNodeName()) eq 'album'){ $albumName = $albumNode->getAttributeNode("name")->getValue(); $allLyrics .= ' '.getSongs($albumName); } } } } } #now take $allLyrics and split it into an array @allLyricsArray = split('', $allLyrics); #iterate the array and fill a hash, looking at the $_ and the one to the right for($x = 0; $x < $#allLyricsArray; $x++){ #make sure that the $pos2 isn't gonna be out of the array if($x+3 < $#allLyricsArray){ $pos1 = $allLyricsArray[$x].$allLyricsArray[$x+1]; $pos2 = $allLyricsArray[$x+2].$allLyricsArray[$x+3]; $mainHash{$pos1}{$pos2}++; } } return %mainHash; } ########################### ########################### #from Perl Cookbook 2.10 #take in a hash - convert the values to percentages, and then return that hash ########################### sub weight_to_dist { my %weights = @_; my %dist = (); my $total = 0; my ($key, $weight); local $_; foreach (values %weights) { $total += $_; } while ( ($key, $weight) = each %weights ) { $dist{$key} = $weight/$total; } return %dist; } ########################### ########################### #from Perl Cookbook 2.10 #take in a hash and via a weighted random, pick one of the keys based on the value and return it ########################### sub weighted_rand { my %dist = @_; my ($key, $weight); my $rand; while (1) { # to avoid floating point inaccuracies $rand = rand; while ( ($key, $weight) = each %dist ) { return $key if ($rand -= $weight) < 0; } } } ########################### ########################### #randomly pick and return a starting letter ########################### sub startLetter{ my $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; my $startLetter = substr($alphabet, int(rand(26)), 1); return $startLetter; } ########################### ########################### #randomly pick a key from the songsHash ########################### sub randomKeyFromFirstHash{ return ((keys %songsHash)[int rand keys %songsHash]); } ########################### ########################### #main ########################### print '
';
#create the frequency hash
#%songsHash = createSongHashMono('radiohead');#not very good
%songsHash = createSongHashQuad('radiohead');#very good
#%songsHash = createSongHashPent('radiohead');#good, but tends to reproduce exact lyrics from songs
#%songsHash = createSongHashTri('radiohead');#okay, but not so great
#%songsHash = createSongHashDuo('radiohead');#not very good
#randomly pick a spot in the hash, then check to see if the first letter is a cap letter
#if it is, then carry on, if not, pick again
$fail = 0;
while($fail < 1){
$start = randomKeyFromFirstHash();
#if the first position is a capital letter, then cool,
#try to ignore symbols, specifically '[' and ']' since I know they are in there.
#newline is okay
if(ord($start) > 64 && ord($start) < 91){
$fail = 1;
}
}
#take the start letter and get rolling...
print $s = $start;
$lineCount = 0;
#count the newilnes ('\n') and for each one
while($lineCount < $maxLines){
%tempHash = weight_to_dist(%{$songsHash{$s}});
print $s = weighted_rand(%tempHash);
#if it contains a newline, then increment the count
if(index($s, "\n")){
#if($s eq '\n'){
$lineCount++;
# print '*';
}
}
print '';
###########################
# Avoid memory leaks - cleanup circular references for garbage collection
$doc->dispose;