Hack 29 Make IRC Talk


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 endlessif 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/downloadl.
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_needl.
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
"connected\n"; from
Speech/Festival/Synthesiser.pm and
"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 Code
So 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 Hack
Now you can venture to add speak.pl to your
~/.irssi/scripts directory and type:
/script load speakNow you need to adjust your settings:
speech_language should be set to one of those
listed when you type:
/speech languagesspeech_channels controls which channels (separated
by spaces) should trigger a speech output (~
stands for private messages).
4.14.3 Hacking the Hack
The 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 repositorythe
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