
|
|
|
Robot Karaoke
Who says people get to have all the fun? With
this hack, you can let your computer do a little singing, by scraping
the LyricsFreak.com web site and sending the results to a
text-to-speech translator
The Code
[Discuss (1) | Link to this hack] |
There are things that are text-only and
things that are multimedia. Then there's this hack,
which turns boring old text into multimedia—specifically, a
.wav file.
This hack, as it stands, is actually pretty silly. It searches the
lyric collections at
LyricsFreak.com
for the keywords you specify, then sends the matching lyrics to yet
another site (http://naturalvoices.com)
that turns them into a .wav file. If
you're running a Win32 system, the code will then
automatically play the .wav file
("sing" the lyrics, for some narrow
definition of sing) via the
Win32::Sound module (http://search.cpan.org/author/ACALPINI/Win32-Sound/).
WARNING
Listening to your computer's rendition of the
Spider-Man theme song can be detrimental to
your health.
As you're playing with this code, you might want to
think of more sublime and less ridiculous implementations. Do you
have a site read by low-vision people? Are
there short bits of text, such as a local weather forecast, that
would be useful for them to have read aloud? Would it be helpful to
have a button that would convert a story summary to a
.wav file for later download?
Running the Hack
Invoke the script on the command line, passing it the phrase
you're interested in; the script will search for
that phrase in the titles of pages on LyrisFreak.com. If it
doesn't find the phase, it'll just
stop:
% perl robotkaroake.pl "fish heads"
No LyricsFreak matches were found for 'fish heads'.
If it does find the phrase, it'll download the
lyrics and generate the .wav file:
% perl robotkaroake.pl "born never asked"
Downloading lyrics from:
http://www.lyricsfreak.com/l/laurie-anderson/81556.html
Connection to LyricsFreak was successful.
Playing downloaded wav files...
Now Playing: 7a0c0093f2f531ac98691152d1f74367.wav
The previous example shows the output of a rather short entry. Longer
songs will result in more .wav files saved to
the current directory, each representing a small chunk (a single
chunk representing one request to the TTS server):
% perl robotkaroake.pl "under the moon"
Downloading lyrics from:
http://www.lyricsfreak.com/i/insane-clown-posse/67657.html
Connection to LyricsFreak was successful.
Playing downloaded wav files...
Now Playing: fe34e081ab8a3abaeecdb1e50b030209.wav
Now Playing: 80709499765f9bfe75d3c7234c435a79.wav
Now Playing: f1ca99233f9cdc6a78f311db887914f1.wav
Now Playing: fd6b61421f3fc56510cf4b9e0d3a0e12.wav
Now Playing: b954f58f906d53ec312bbcc6579ebe12.wav
Now Playing: 407415e685260754174cf45338ba4d10.wav
Now Playing: 8a2ade6e7f8fe950ddcb58747d241694.wav
Now Playing: 22ed038190b9ed0fb4e3077655503422.wav
The CodeOne of the modules used with this code,
Win32::Sound, is for Win32 machines only. Since
it's used to play back the generated
.wav file, you will not get a
"singing" robot if
you're on a non-Win32 machine;
you'll just get a .wav file,
suitable for playing through your preferred music player. Save this script as robotkaroake.pl: #!/usr/bin/perl -w
use strict;
use LWP::Simple;
use URI::Escape;
use Win32::Sound;
use SOAP::Lite;
# use your own Google API key here!
my $google_key = "your Google key here";
my $google_wdsl = "GoogleSearch.wsdl";
# load in our lyrics phrase from the command line.
my $lyrics_phrase = shift or die "Usage: robot-karaoke.pl <phrase>\n";
# and perform the search on Google.
my $google_search_term = "intitle:\"$lyrics_phrase\" site:lyricsfreak.com";
my $googleSearch = SOAP::Lite->service("file:$google_wdsl");
my $result = $googleSearch->doGoogleSearch(
$google_key, $google_search_term,
0, 10, "false", "", "false",
"", "", "");
# if there are no matches, then say so and die.
die "No LyricsFreak matches were found for '$lyrics_phrase'.\n"
if $result->{estimatedTotalResultsCount} == 0;
# and take the first Google result as
# the most likely location on LyricsFreak.com.
my @results = @{$result->{'resultElements'}};
my $first_result = $results[0];
my $lyricsfreak_url = $first_result->{'URL'};
print "Downloading lyrics from:\n $lyricsfreak_url\n";
# and download the data from LyricsFreak.com.
my $content = get($lyricsfreak_url) or die $!;
print "Connection to LyricsFreak was successful.\n";
# we have the data, so let's parse it.
# all lyrics are stored in a pre tag,
# so we delete everything before and after.
$content =~ s/.*<pre><b>.*<\/b><br>//mgis;
$content =~ s/<\/pre>.*//mgis;
my @lyrics_lines = split("\x0d", $content);
# AT&T's demo TTS service takes a maximum of 30 words,
# so we'll create a mini chunk of the lyrics to send off.
# each of these chunks will be sent to the TTS server
# then saved seperately as multiple mini-wav files.
my (@lyrics_chunks, $current_lyrics_chunk); my $line_counter = 0;
for (my $i = 0; $i <= scalar(@lyrics_lines) - 1; ++$i) {
next if $lyrics_lines[$i] =~ /^\s*$/;
$current_lyrics_chunk .= $lyrics_lines[$i] . "\n";
if (($line_counter == 5) || ($i == scalar(@lyrics_lines) - 1) ) {
push(@lyrics_chunks, $current_lyrics_chunk);
$current_lyrics_chunk = ''; $line_counter = 0;
} $line_counter++;
}
# now, we'll go through each chunk,
# and send it off to our TTS server.
my @temporary_wav_files;
foreach my $lyrics_chunk (@lyrics_chunks) {
# and download the data.
my $url = 'http://morrissey.naturalvoices.com/tts/cgi-bin/nph-talk';
my $req = HTTP::Request->new('POST', $url); # almost there!
$req->content('txt=' . uri_escape($lyrics_chunk) .
'&voice=crystal&speakButton=SPEAK');
$req->content_type('application/x-www-form-urlencoded');
my $res = LWP::UserAgent->new->simple_request($req);
# incorrect server response? then die.
unless ($res->is_success || $res->code == 301) {
die "Error connecting to TTS server: " . $res->status_line . ".\n"; }
# didn't get the response we wanted? die.
if ($res->content !~ /can be found <A HREF=([^>]*)>here<\/A>/i) {
die "Response from TTS server not understood. Odd.\n"; }
# side effect of error checking above is to set $1 to
# the actual wav file that was generated. this is good.
my $wav_url = "http://morrissey.naturalvoices.com$1";
my $wav_file = $1; # for use in saving to disk.
$wav_file =~ s/.*?\/(\w+.wav)/$1/;
getstore($wav_url, "$wav_file") or
die "Download of $wav_file failed: $!";
push(@temporary_wav_files, $wav_file);
}
# with all our files downloaded, play them in
# order with the Win32::Sound module. else, they
# just sit there in hopes of the user playing them.
print "Playing downloaded wav files...\n";
foreach my $temporary_wav_file (@temporary_wav_files) {
print " Now Playing: $temporary_wav_file\n";
Win32::Sound::Play("$temporary_wav_file");
}
Showing messages 1 through 1 of 1.
-
wrong text
2004-01-19 19:00:26
agomez
[View]
|
Showing messages 1 through 1 of 1.
|
|
O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website:
| Customer Service:
| Book issues:
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
|
|
Thanks.