Created
May 7, 2011 06:17
-
-
Save nfreear/960257 to your computer and use it in GitHub Desktop.
getsound.pl: CGI script to bridge eSpeak TTS to the Web - WebAnywhere
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# | |
# CGI script to bridge eSpeak TTS to the Web - WebAnywhere. | |
# | |
# @copyright (c)2008 University of Washington. | |
# @license http://www.opensource.org/licenses/bsd-license.php | |
# @source http://code.google.com/p/webanywhere/#trunk/tts/espeak/getsound.pl#r264 | |
# Modifications, Nick Freear {http://freear.org.uk} | |
# | |
use strict; | |
use warnings; | |
use utf8; | |
use CGI qw(:standard unescape); | |
use Digest::MD5; | |
use File::Path; | |
use Encode; | |
# Paths - edit me. | |
my $lame = '/home/hgneng/bin/lame'; #'/opt/local/bin/lame'; #NDF. | |
my $espeak = '/home/hgneng/bin/espeak/speak'; #'/Applications/espeak/speak'; #NDF. Added. | |
my $log_dir= '/var/log/ekho'; #'/Applications/MAMP/logs'; | |
my $request_log= "$log_dir/espeak_request.log"; #'/var/log/ekho/espeak.request'; | |
my $error_log = "$log_dir/espeak_error.log"; #'/var/log/ekho/espeak.error'; | |
my $cache_dir = '/var/cache/sounds'; #'/Applications/MAMP/tmp/cache_sounds'; | |
my $ext = 'wav'; #NDF. | |
if ($lame) { | |
$ext = 'mp3'; | |
} | |
#festival/getsound.pl - See %letters search-replace. | |
my $voice = 'en'; | |
my $cache = 1; | |
my $text = param('text'); | |
#error("Woops, parameter 'text=X' is required.") if !$text; #NDF. Added. | |
$text = "Error. Parameter text is required." if !$text; | |
$voice = param('lang') if (defined param('lang')); | |
$cache = param('cache') if (defined param('cache')); | |
$voice =~ s/\|/+/; #NDF. en%2Bf1. | |
$text = unescape($text); | |
# handle encoding | |
#`echo "raw: $text" >> /tmp/espeak.log`; | |
# decode MS %u infamous non-standard encoding in URL | |
while ($text =~ /([^%]*)%u(....)(.*)/) { | |
$text = $1 . chr(hex("0x$2")) . $3; | |
} | |
$text =~ s/^\s+//; # delete leading spaces | |
$text =~ s/\s+$//; # delete tailing spaces | |
$text =~ s/\"//g; | |
#`echo "speaking $text" >> /tmp/espeak.log`; | |
logRequest($text, $voice); | |
sendFileToClient(getMp3File($text, $voice, $ext), $text, $voice, $ext); | |
##### END OF MAIN ##### | |
sub getMp3File { | |
my ($text, $voice, $ext) = @_; | |
# Constructs a filename based on the MD5 of the text. | |
my $md5 = Digest::MD5->new; | |
$md5->add(encode_utf8($text)); | |
my $filename = $md5->b64digest; | |
$filename =~ s/[\/+\s]/_/g; | |
my $lc_filename = lc($filename); | |
my $first_dir = substr($lc_filename, 0, 1); | |
my $second_dir = substr($lc_filename, 1, 1); | |
my $third_dir = substr($lc_filename, 2, 1); | |
#my $enc_voice = $voice; #NDF. Encode '+m3' etc. | |
#$enc_voice =~ s/\+/_/; | |
my $final_dir = "$cache_dir/espeak-$voice/$first_dir/$second_dir/$third_dir"; | |
my $final_filename = "$final_dir/$filename.$ext"; #NDF. Was .mp3. | |
# Ensure that the final directory actually exists. | |
mkpath $final_dir; | |
if((!(-e $final_filename)) || $cache == 0) { | |
if ($ext eq 'wav') { #NDF. | |
system("$espeak -v$voice -w $final_filename \"$text\" "); | |
} else { | |
system("$espeak -v$voice \"$text\" --stdout | $lame --preset voice -q 9 --vbr-new - $final_filename"); | |
#Try to get 'duration' info! | |
#system("$espeak -v$voice \"$text\" --stdout | $lame --verbose --preset voice -q 9 --vbr-new - $final_filename >> /Applications/MAMP/logs/lame.log"); | |
} | |
# | |
} | |
return $final_filename; | |
} | |
# output mp3 audio stream | |
sub sendFileToClient { | |
#my $file = shift; | |
my ($file, $text, $voice, $ext) = @_; | |
if (-e $file) { | |
my $size = -s $file; | |
my $filename = $file; | |
$filename =~ s#.*?(tmp|cache)#\$$1#; #NDF. Security. | |
if ($size) { | |
my $buff; | |
if ($ext eq 'wav') { #NDF. | |
print "Content-Type: audio/wav\n"; | |
} else { | |
print "Content-Type: audio/mpeg\n"; | |
} | |
print "Content-Length: $size\n"; | |
print "Final-name: $filename\n"; | |
print "X-Text: $text\n"; | |
print "X-Voice: $voice\n"; #NDF. | |
##print "Expires: Tue, 12 Mar 2012 04:00:25 GMT\n\n"; | |
print "\n"; | |
open(FILE, $file); | |
while(read(FILE, $buff, 4096)) { | |
print $buff; | |
} | |
close(FILE); | |
} else { | |
error("Zero size audio file. Something wrong with eSpeak?"); | |
} | |
} else { | |
error("Fail to generate audio file. Permission deny?"); | |
} | |
} | |
# global variable: $request_log | |
sub logRequest { | |
my ($text, $voice) = @_; | |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); | |
if (-s $request_log > 1000000) { | |
for (my $i = 4; $i > 0; --$i) { | |
if (-e "$request_log.$i") { | |
rename("$request_log.$i", "$request_log." . ($i + 1)); | |
} | |
} | |
rename($request_log, "$request_log.1"); | |
} | |
open(REQUEST_LOG, '>>', $request_log); | |
printf REQUEST_LOG "[%4d%02d%02d-%02d:%02d:%02d] [%s] %s\n", | |
1900 + $year, $mon, $mday, $hour, $min, $sec, $voice, $text; | |
close(REQUEST_LOG); | |
} | |
sub error() { | |
my $error = shift; | |
open(ERROR_LOG, '>>', "$error_log"); | |
print ERROR_LOG "$error\n"; | |
close(ERROR_LOG); | |
print "Status: 400 Bad Request\n"; | |
#print "Status: 500 Internal Server Error\n"; #NDF.? | |
print "Content-type: text/html\n\n"; | |
print "ERROR: " . $error; | |
exit(0); | |
} | |
1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment