I like the band Radiohead.
I also enjoy a Markov Matrix now and then (in the past I have written a Poe generator - reads in all of his works, and then will generate text that reads like him... kind of. I have also written scripts that will read in from the math and religion newsgroups and then post to them - it makes for an entertaining time to see it start fights. I've also automated it to post to a few chatboards as well - again, it seems to lead to people fighting with it - which to me is endless amusement.).
As a result (or a side effect?) I decided to combine the two for fun.

I made up an xml file (http://www.cardboardutopia.com/radiohead/radiohead.xml) of all of their song lyrics (taken from Greenplastic.com) and then read that in.
I then iterate over it and build some hashes (the markov matrix). Then I choose a random starting point and then iterate through for an arbitrary length (for now I think it is around the length of one of their songs "Fake Plastic Trees" and then stops.
As a result of that, it sometimes cuts off abruptly and doesn't end pleasantly - sometimes mid sentence. But in some ways I like that - I tend to talk a lot like that sometimes.

There is currently a lot of code in there that is redundant, and there are functions that aren't used at all. This is because I want to add to this to allow any XML file to be created for any band (although technically doesn't need to be bands even), and then output based on that.
I also want it to eventually generate song titles and album titles as well - but those won't work as well since there is a small amount of data to "learn" on.

This will work best with bands that have a large number of albums released (assuming that can be correlated to lots of lyrics as well).

It would also be interesting to have this figure out the notes of a song file as well, and then do the same operation to that - you could then create music this way as well... but I personally don't think I'd want to do audio signal processing in Perl.

I have also ported this over to PHP as well and have both of them on my site. ( I also wrote a script that takes in all of the lyrics via the XML file and then feeds them into the ThousandWords module and creates a web page of that - I used the "Meeting People Is Easy" graphic for that)


An example of what the Perl version outputs, followed by what the PHP version outputs. Right now the PHP version is a little faster and the Perl version tends to die the first time it is run and I don't know why (I don't tend to program Perl to output to a web page, this is my first).

Perl:
Faith, yeah Everyone is so much, regular exercise oh no, pop one final friends surrogate and while no-one car Where was better, I get out of the last time Here it but you go When I'm not like a change Sometimes I can see my real farm my reflection It's such a house and somehow Yester, i'm not idealise I didn't cope if you A little birds, the future I do? If I don't wants you never leave me hills explode ray, but the kids in half Into yourself a local make love I get old Remind me crazy, maybe I want you can try the think you're in a week, get offered and there, I lost m

PHP:
Comes like a bug in a fast Germarket Just don't die the furniture I'm on antibiotics. I wish, I wish the ceiling, starting I doing that I'm gone Remind me that keep moving Be a while And the YEN We're talk slowerless Woh i can is what the really fed us on Let don't leave me In the best you? Been the lost myself. I'll always runs from here? Hey man, slow down though For a ride tonight, ever had, you'll not coming to my head of itself I'll drown Tie your eyes There's a danger Mobiles skwrking But never cheek Talking pills Just don't know what might as well comes i think now why I feel. My Belisha beacon on politics and when you wants to be crisps I'll be in a child, pearly



Here is the code below - it isn't likely optimal - but feel free to use it or post up improvement suggestions.
I'm interested in eventually making it a module so that, like I said, you can feed it any band XML that is formatted in the way that it expects, and then it will output songs.

#!/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 th +e 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 r +andom based on the real lengths. #$maxLines = 43*5;#for some reason this needs to be multiplied by vagu +ely 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 n +odes $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->getAttri +buteNode("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 n +odes $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->g +etNodeValue(); } } } } } 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 st +rings #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 cha +rted } $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 st +rings #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].$allLyri +csArray[$x+2].$allLyricsArray[$x+3]; $pos2 = $allLyricsArray[$x+4].$allLyricsArray[$x+5].$allLy +ricsArray[$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 st +rings #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].$allLyri +csArray[$x+2].$allLyricsArray[$x+3].$allLyricsArray[$x+4]; $pos2 = $allLyricsArray[$x+5].$allLyricsArray[$x+6].$allLy +ricsArray[$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 st +rings #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].$allLyri +csArray[$x+2]; $pos2 = $allLyricsArray[$x+3].$allLyricsArray[$x+4].$allLy +ricsArray[$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 st +rings #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 t +hat 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 inaccura +cies $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 '<html><body style=\'font-family:arial;font-size:9pt;\'><pre wid +th="500">'; #create the frequency hash #%songsHash = createSongHashMono('radiohead');#not very good %songsHash = createSongHashQuad('radiohead');#very good #%songsHash = createSongHashPent('radiohead');#good, but tends to repr +oduce 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 lett +er 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 '</pre></body></html>'; ########################### # Avoid memory leaks - cleanup circular references for garbage collect +ion $doc->dispose;

In reply to Radiohead song generator by AssFace

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.