I always post questions here, really annoying questions. Today, i get to post an answer, yippee! Monks are always helping me out, i hope this is a return.
Win32 SAPI voice speech with perl. Namely changing voices used. Since Win2000 i've never been able to change the default voice for the SAPI with perl. Sure there's a command for it (Select) but it never worked for me. Luckily i've read others had the same problem so i starting thinking maybe Select doesn't work. I am still under that assumption today.
This listing works in that it speaks in Win32, but the voice will not select properly...
use Win32::OLE qw( EVENTS );
my $vt = Win32::OLE->new('{EEE78591-FE22-11D0-8BEF-0060081841DE}') ||
+die "Can't start VoiceText";
my $use_id = $vt->Find( "Male Whisper" );
$vt->Select($use_id);
$vt->Speak("Hello World");
while( $vt->{Speaking} ){
Win32::OLE->SpinMessageLoop();
Win32::Sleep( 100 );
}
In fact, whatever voice is currently selected will be the talker, you can even change the voice with one of the SAPI example c++ applications, come back to perl and the new voice you choose via the exe will be selected.
Ah, but to do it in perl....
I found that my registry gets altered whenever the SAPI exe changes the voice. Hence, we let perl change the same registry key and voila, perl changed the default SAPI4 voice. Works great for me, i hope it does for you as well.
Here's the working code which changes the voice to RoboSoft Six...
use Win32::OLE qw( EVENTS );
use Win32::TieRegistry;
my $vt = Win32::OLE->new('{EEE78591-FE22-11D0-8BEF-0060081841DE}') ||
+die "Can't start VoiceText";
my $ttl = $vt->CountEngines();
my $look_for = "RoboSoft Six";
for(my $x=1;$x <= $ttl;$x++){
my $mode_name = $vt->ModeName($x);
my $mode_id = $vt->ModeID($x);
if ($mode_name eq $look_for){
my($h1,$h2,$h3,$h4,$h5) = split(/\-/,$mode_id);
$h1 = &Flip($h1);
$h2 = &Flip($h2);
$h3 = &Flip($h3);
&ChangeVoiceRegKey("$h1$h2$h3$h4$h5");
}
}
$vt->Speak("Hello World");
while( $vt->{Speaking} ){
Win32::OLE->SpinMessageLoop();
Win32::Sleep( 100 );
}
#########################################################
#########################################################
#########################################################
sub ChangeVoiceRegKey{
my $hsh = $_[0];
my $rf = $Registry->{"HKEY_CURRENT_USER\\Software\\Voice\\VoiceText\\L
+ocal PC"}= {
"Mode", => [ pack("H*","$hsh"), "REG_BINARY" ]
};
}################################## end ChangeVoiceRegKey
sub Flip{
my $vle = $_[0];
$vle =~ s/(..)/$1\|/g;
$vle =~ s/\|$//;
my @all = split(/\|/,$vle);
my $new_all;
foreach my $li(reverse @all){
$new_all .= $li;
}
return $new_all;
}################################## end Flip
Notice that we call the CountEngines() function built-in to SAPI4 (thanks Jouke). Then we iterate through the list of all the voices installed on Windows (I've got 37 of em). When we find our match ("RoboSoft Six" above), we take the ModeID value sent to us by SAPI4 and pack that accordingly and set our registry to the new value.
Most smart people i'm sure will think my "Flip" subroutine is absurd and pointless. I'm sure there's a way to do it without the subroutine but i don't know it yet. I tested this code on Win2000Pro and WinXPPro with no issues.