153 lines
5.4 KiB
Perl
153 lines
5.4 KiB
Perl
# TTYverse Text-to-Speech Extension
|
|
# By Storm Dragon: https://stormux.org/
|
|
# Support Storm Dragon's work: https://patreon.com/stormux
|
|
# Published under the Floodgap Free Software License: http://www.floodgap.com/software/ffsl/
|
|
|
|
# What is it?
|
|
# This extension uses text-to-speech to read incoming fediverse posts.
|
|
# Multiple synthesizer engines are supported for accessibility.
|
|
|
|
# Usage:
|
|
# Toggle TTS on/off: /tts
|
|
# Get help: /tts help
|
|
# Load extension when starting TTYverse or add to configuration
|
|
|
|
$addaction = sub {
|
|
my $command = shift;
|
|
my $speak = "";
|
|
|
|
# Get current TTS state from config
|
|
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
|
|
if (-e $tts_config) {
|
|
open TTSSETTINGS, $tts_config;
|
|
$speak = <TTSSETTINGS>;
|
|
close TTSSETTINGS;
|
|
chomp($speak) if defined($speak);
|
|
} else {
|
|
$speak = 0;
|
|
}
|
|
|
|
if ($command eq '/tts') {
|
|
if ((!defined($speak)) || ($speak eq 1)) {
|
|
$speak = 0;
|
|
print $streamout "-- Text-to-speech disabled\n";
|
|
} else {
|
|
$speak = 1;
|
|
print $streamout "-- Text-to-speech enabled\n";
|
|
}
|
|
|
|
# Save new state
|
|
open TTSSETTINGS, ">$tts_config";
|
|
print TTSSETTINGS "$speak";
|
|
close TTSSETTINGS;
|
|
return 1;
|
|
}
|
|
|
|
if ($command eq "/tts help") {
|
|
my $ttsHelp = "TTYverse Text-to-Speech Extension\n\n" .
|
|
"Commands:\n" .
|
|
" /tts - Toggle speech on/off\n" .
|
|
" /tts help - Show this help\n\n" .
|
|
"Configuration (add to TTYverse config):\n\n" .
|
|
"extpref_tts_synthesizer=synthName\n" .
|
|
" Available: espeak (default), festival, pico, cepstral, mac\n\n" .
|
|
"extpref_tts_language=languageCode\n" .
|
|
" Default: en-US (supported by espeak and pico)\n\n" .
|
|
"extpref_tts_rate=number\n" .
|
|
" Speech rate, default: 175 (espeak only)\n\n" .
|
|
"extpref_tts_variant=name\n" .
|
|
" Voice variant/name (espeak variants, cepstral/mac voices)\n";
|
|
print $streamout "$ttsHelp";
|
|
return 1;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
$handle = sub {
|
|
my $ref = shift;
|
|
my $class = shift;
|
|
|
|
# Skip TTS during initial timeline load
|
|
if ($last_id eq 0) {
|
|
&defaulthandle($ref);
|
|
return 1;
|
|
}
|
|
|
|
my $speak = "";
|
|
# Get TTS state from config
|
|
my $tts_config = "$ENV{'HOME'}/.config/ttyverse/tts_enabled";
|
|
if (-e $tts_config) {
|
|
open TTSSETTINGS, $tts_config;
|
|
$speak = <TTSSETTINGS>;
|
|
close TTSSETTINGS;
|
|
chomp($speak) if defined($speak);
|
|
} else {
|
|
$speak = 0;
|
|
}
|
|
|
|
# Initialize TTS configuration
|
|
my $soundCommand = "paplay"; # Default for modern Linux/PulseAudio
|
|
my $synthesizer = $extpref_tts_synthesizer || "espeak";
|
|
my $language = $extpref_tts_language || "en-US";
|
|
my $variant = $extpref_tts_variant || "";
|
|
my $rate = $extpref_tts_rate || "";
|
|
|
|
# Override sound command if configured
|
|
if ($extpref_sound_command) {
|
|
$soundCommand = $extpref_sound_command;
|
|
}
|
|
|
|
# Extract post content for TTS
|
|
my $postText = &descape($ref->{'account'}->{'display_name'} || $ref->{'account'}->{'username'}) .
|
|
" posted: " . &descape($ref->{'content'});
|
|
|
|
# Make speech more accessible
|
|
$postText =~ s/\"/ /g; # Remove quotes
|
|
$postText =~ s/ \#/ hashtag /g; # Make hashtags readable
|
|
$postText =~ s/https?:\/\/\S+/ link /g; # Simplify links
|
|
$postText =~ s/www\./ /g; # Remove www
|
|
$postText =~ s/\// forward slash /g; # Make slashes readable
|
|
$postText =~ s/\\/ back slash /g; # Make backslashes readable
|
|
$postText =~ s/\blol\b/ laughs out loud /gi; # Expand common abbreviations
|
|
$postText =~ s/\brofl\b/ rolling on the floor laughing /gi;
|
|
$postText =~ s/<[^>]*>//g; # Remove any remaining HTML
|
|
|
|
my $speechCommand = "";
|
|
|
|
# Configure synthesizer command
|
|
if ($synthesizer eq "cepstral") {
|
|
$speechCommand = "aoss swift ";
|
|
if ($variant ne "") {
|
|
$speechCommand .= "-n $variant ";
|
|
}
|
|
$speechCommand .= "\"$postText\"";
|
|
} elsif ($synthesizer eq "espeak") {
|
|
if ($rate eq "") {
|
|
$rate = "175";
|
|
}
|
|
$speechCommand = "$synthesizer -v " . lc($language);
|
|
if ($variant ne "") {
|
|
$speechCommand .= "+$variant";
|
|
}
|
|
$speechCommand .= " -s $rate \"$postText\"";
|
|
} elsif ($synthesizer eq "festival") {
|
|
$speechCommand = "echo \"$postText\" | festival --tts";
|
|
} elsif ($synthesizer eq "mac") {
|
|
$speechCommand = "say ";
|
|
if ($variant ne "") {
|
|
$speechCommand .= "-v $variant ";
|
|
}
|
|
$speechCommand .= "\"$postText\"";
|
|
} elsif ($synthesizer eq "pico") {
|
|
$speechCommand = "pico2wave -l $language -w /tmp/ttyverse.wav \"$postText\" && $soundCommand /tmp/ttyverse.wav";
|
|
}
|
|
|
|
# Speak the post if TTS is enabled
|
|
if ($speak eq 1) {
|
|
system("$speechCommand");
|
|
}
|
|
|
|
# Allow TTYverse to display the post normally
|
|
&defaulthandle($ref);
|
|
return 1;
|
|
}; |