##
#!/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;