< Day Day Up > |
Hack 29 Make IRC Talk
Even when you can't keep your IRC client in view, keep track of IRC by making your IRC client talk to you. Ever wondered if you could touch the future just a little? What about getting your computer to read out what's happening on a channel or at least just your private messages? You would be able to do something else while listening to the cheery activity of IRC, perhaps even establish a bot that tells you fairy tales while you drift off to sleep. The possibilities are endless—if only your IRC client could speak! It is not impossible, but because this is still very much a cutting edge and not a mass technology, it will not be easy to set up either. You will be hacking in Perl, this time to create a script for the popular irssi IRC client. At the backend, you can employ the Festival speech engine. The first step is to install a speech synthesizer, and then you have to find a Perl interface for it. Neither of these tasks is particularly easy. For the speech synthesizer, we will be using Festival. Its main advantage is that it is a universal engine, so you can teach it to speak virtually any language (from English to Spanish to Czech). It is perhaps not as effective as a native speech synthesizer would be, but it will be enough for our purposes. You can get Festival from http://www.cstr.ed.ac.uk/projects/festival/download.html. However, if you are not sure which tarballs to grab, you can try reading http://www.cstr.ed.ac.uk/cgi-bin/lists.cgi?config=festival_faq&entry=installing_festival/what_do_i_need.html. It comes with support for English, but chances are someone has implemented rulesets for other languages as well. After you have set up Festival, start up the festival-server, as you are going to use it to process requests from your script. Now you need to grab the Perl Festival interface, Speech::Festival. You can download it through the popular CPAN interface, but make test will fail. You will need to run make install manually, although you may notice the modules will install to the wrong location. To fix this, you must move to the directory where the modules were installed (for example, /usr/lib/perl/site_perl/5.6.1/Speech), move the Audio subdirectory to its parent directory, and move the contents of the Speech subdirectory to the current directory. Next, you will probably want to remove print "connected\n"; from Speech/Festival/Synthesiser.pm and print "disconnected\n"; from Speech/Festival.pm. By now, you should hopefully have an idea how important it is to do some basic quality assurance before releasing a module to the world. Of course, the Speech::Festival maintainer might already spare you of this valuable lesson by the time you read this hack. 4.14.1 The CodeSo you don't block irssi while pushing data to the Festival server, you must spawn a helper in a child process to take care of things. You can give it everything through a pipe and close it if the child dies (you probably want to restart it in case the settings change). # Talking irssi gadget (c) Petr Baudis <pasky@ucw.cz>, BSD licence. use strict; use vars qw($forked $wh $type $lang); use Irssi; use Irssi::Irc; use Speech::Synthesiser; # Spawn a helper which will feed our Festival backend, so that we do not # block the main irssi process while pushing data all around. sub fork_me { my ($rh, $pid); pipe($rh, $wh); $forked = 1; $pid = fork( ); if ($pid > 0) { # The main irssi process close $rh; # This makes sure we do not get a zombie Irssi::pidwait_add($pid); return; } else { # The helper child close($wh); my $synth = new Speech::Synthesiser(-type => $type); start $synth; if ($lang) { voice $synth $lang; } while (my $in = <$rh>) { chomp $in; speak $synth $in; } stop $synth; close($rh); POSIX::_exit(0); } } # The incoming message event handler. sub event_privmsg { my ($server, $data, $nick, $address) = @_; my ($msgtarget, $text) = split(/ :/, $data, 2); my (@channels) = split(/\s+/, Irssi::settings_get_str('speech_channels')); # The ~ substitution return unless (grep {s/^~$/$server->{nick}/x; $_ eq $msgtarget} @channels); # Restart the backend if something changed. my ($otype, $olang) = ($type, $lang); $type = Irssi::settings_get_str('speech_backend'); $lang = Irssi::settings_get_str('speech_language'); if ($forked and ($type ne $otype or $lang ne $olang)) { print $wh "\n"; close($wh); $forked = 0; } if (!$forked) { fork_me( ); } # Some emoticon replacements (e.g. ":-)"->"hehe!" # add your own if you need more! $text =~ s/:.?\)/hehe!/g; $text =~ s/:.?\(/sniff/g; # The exclamation point helps to get the right intonation. print $wh "$nick! $text\n"; } # Our command interface. sub cmd_speech { my ($cmd) = @_; if ($cmd =~ /^languages/i) { my $synth = new Speech::Synthesiser( -type => Irssi::settings_get_str('speech_backend')); start $synth; my @voices = voice_list $synth; Irssi::print("These languages are supported: @voices"); stop $synth; } } Irssi::command_bind('speech', \&cmd_speech); Irssi::signal_add("event privmsg", "event_privmsg"); Irssi::settings_add_str('speech', 'speech_backend', 'Festival'); Irssi::settings_add_str('speech', 'speech_language', ''); Irssi::settings_add_str('speech', 'speech_channels', '~ #irchacks'); 4.14.2 Running the HackNow you can venture to add speak.pl to your ~/.irssi/scripts directory and type: /script load speak Now you need to adjust your settings: speech_language should be set to one of those listed when you type: /speech languages speech_channels controls which channels (separated by spaces) should trigger a speech output (~ stands for private messages). 4.14.3 Hacking the HackThe preceding code is no more than a skeleton script, which could of course be extended and polished in so many ways. A good irssi script should follow some basic conventions, which can also help it to make it into the http://scripts.irssi.org repository—the script should ideally provide an %IRSSI hash containing some basic information about itself (author, version, description, license, required modules). While you're adding those features, you should extend the /SPEECH command interface so that it provides some status information and perhaps could even send some commands to the Festival server. I also recommend that the script print a short announcement when it is loaded. Another area of improvement would be the relevant text transformations. The script substitutes only the most frequent smileys now (:), :-), :o), etc.), but all of this should be configurable so that people can expand this list and adjust it to their language preference. Speaking of languages, another interesting direction of expansion could be to make the language settings channel-specific. Non-English people frequently idle in both English and foreign channels, therefore irssi should speak in a different language in each channel. Yes, Festival is great and cool, but for most languages, the reality is that better speech synthesizers exist, being optimized specifically for the given language. Therefore, a larger project would involve hacking Perl interfaces for other synthesizers as well, perhaps also fixing some of the problems in the Festival interface as described earlier. —Petr Baudis |
< Day Day Up > |