use strict; use Win32::OLE qw(EVENTS); use Win32::OLE::Const; use Data::Dumper; #Win32::OLE::Const is terrible, the tree like interface ( Win32::OLE::TypeInfo class ) #is explictly undocumented my $constHash = Win32::OLE::Const->Load('Microsoft Speech Object Library'); #print Dumper($constHash); my $vox = Win32::OLE->new ('SAPI.SpVoice') || die "Unable to create SAPI object\n"; sub Event { my %hash; #foreach (@_) { # print " ".$_." "; #} #print "\n"; #system("pause"); #stack trace from the system pause above # ntdll.dll!_KiFastSystemCallRet@0() # ntdll.dll!_ZwWaitForMultipleObjects@20() + 0xc # kernel32.dll!_WaitForMultipleObjectsEx@20() - 0x48 # user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xd9 # user32.dll!_MsgWaitForMultipleObjects@20() + 0x1f #> perl512.dll!win32_msgwait(interpreter * my_perl=0x003940dc, unsigned long count=0x00000001, void * * handles=0x0006f500, unsigned long timeout=0xffffffff, unsigned long * resultp=0x00000000) Line 2181 + 0x19 C # perl512.dll!win32_spawnvp(int mode=0x00000000, const char * cmdname=0x00a11604, const char * const * argv=0x00a1abfc) Line 4249 + 0x19 C # perl512.dll!do_spawn2(interpreter * my_perl=0x003940dc, const char * cmd=0x0083a3f4, int exectype=0x00000002) Line 784 + 0x11 C # perl512.dll!Perl_do_spawn(interpreter * my_perl=0x003940dc, char * cmd=0x0083a3f4) Line 820 + 0xf C # perl512.dll!Perl_pp_system(interpreter * my_perl=0x003940dc) Line 4277 + 0x5e C # perl512.dll!Perl_runops_debug(interpreter * my_perl=0x003940dc) Line 2049 + 0xd C # perl512.dll!Perl_call_sv(interpreter * my_perl=0x003940dc, sv * sv=0x00a042ec, volatile long flags=0x00000006) Line 2590 + 0x36 C # OLE.dll!EventSink::Invoke(long dispidMember=0x00000003, const _GUID & riid={...}, unsigned long lcid=0x00000400, unsigned short wFlags=0x0001, tagDISPPARAMS * pdispparams=0x0006f7d8, tagVARIANT * pvarResult=0x00000000, tagEXCEPINFO * pexcepinfo=0x00000000, unsigned int * puArgErr=0x00000000) Line 2161 + 0xf C++ # sapi.dll!CProxy_ISpeechVoiceEvents::Fire_VoiceChange() + 0xb8 # sapi.dll!CSpVoice::NotifyCallback() + 0x11d # sapi.dll!CSpNotify::WndProc() + 0x60 # user32.dll!_InternalCallWinProc@20() + 0x28 # user32.dll!_UserCallWinProcCheckWow@32() + 0xb7 # user32.dll!_DispatchMessageWorker@8() + 0xdc # user32.dll!_DispatchMessageA@4() + 0xf # OLE.dll!XS_Win32__OLE_Initialize(interpreter * my_perl=0x003940dc, cv * cv=0x008d0634) Line 3549 C++ # perl512.dll!Perl_pp_entersub(interpreter * my_perl=0x003940dc) Line 2882 + 0x10 C # perl512.dll!Perl_runops_debug(interpreter * my_perl=0x003940dc) Line 2049 + 0xd C # perl512.dll!S_run_body(interpreter * my_perl=0x003940dc, long oldscope=0x00000001) Line 2308 + 0xd C # perl512.dll!perl_run(interpreter * my_perl=0x003940dc) Line 2233 + 0xd C # perl512.dll!RunPerl(int argc=0x00000002, char * * argv=0x00282510, char * * env=0x002828f0) Line 270 + 0x9 C++ # perl.exe!main(int argc=0x00000002, char * * argv=0x00282510, char * * env=0x00282d40) Line 23 + 0x12 C # perl.exe!mainCRTStartup() Line 398 + 0xe C # kernel32.dll!_BaseProcessStart@4() + 0x23 $hash{'eventName'} = $_[1]; if($hash{'eventName'} eq 'AudioLevel'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; $hash{'AudioLevel'} = $_[4]; } elsif($hash{'eventName'} eq 'Phoneme'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; $hash{'Duration'} = $_[4]; $hash{'NextPhoneId'} = $_[5]; $hash{'Feature'} = DecodeSpeechVisemeFeature($_[6]); $hash{'CurrentPhoneId'} = $_[7]; } elsif($hash{'eventName'} eq 'Word'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; $hash{'CharacterPosition'} = $_[4]; $hash{'Length'} = $_[5]; } elsif($hash{'eventName'} eq 'StartStream'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; } elsif($hash{'eventName'} eq 'VoiceChange'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; #Dumping the obj is pagefuls of info $hash{'VoiceObjectTokenDesc'} = $_[4]->GetDescription(); } elsif($hash{'eventName'} eq 'Sentence'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; $hash{'CharacterPosition'} = $_[4]; $hash{'Length'} = $_[5]; } elsif($hash{'eventName'} eq 'Viseme'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; $hash{'Duration'} = $_[4]; $hash{'NextVisemeId'} = DecodeSpeechVisemeType($_[5]); $hash{'Feature'} = DecodeSpeechVisemeFeature($_[6]); $hash{'CurrentVisemeId'} = DecodeSpeechVisemeType($_[7]); } elsif($hash{'eventName'} eq 'EndStream'){ $hash{'StreamNumber'} = $_[2]; $hash{'StreamPosition'} = $_[3]; } else {die "unknown event $hash{'eventName'}";} print Dumper(\%hash); if($hash{'eventName'} eq 'EndStream'){ Win32::OLE->QuitMessageLoop(); } } my $res = Win32::OLE->WithEvents ($vox, \&Event #'_' bug in Win32 OLE, must use UUID if not fixed #https://rt.cpan.org/Public/Bug/Display.html?id=43574 #, '{A372ACD1-3BEF-4BBD-8FFB-CB3E2B416AF8}' ,'_ISpeechVoiceEvents' ); print " OLE last error ".Win32::OLE->LastError()."\n"; #print Dumper($res); die "bad const" if $constHash->{'SVEAllEvents'} != 33790; $vox->{'EventInterests'} = $constHash->{'SVEAllEvents'}; $res = $vox->{'EventInterests'}; print "EventInterests res ".Dumper($res); my $text = "I Love Perl"; die "bad const" if $constHash->{'SVSFlagsAsync'} != 1; my $res = $vox->Speak ($text, $constHash->{'SVSFlagsAsync'}); print Dumper($res); print " ".Win32::OLE->LastError()."\n"; print "WFNE ".Dumper($res); #potential ideas, investigate using Perl's alarm win msg loop Win32::OLE->MessageLoop(); { my @table; $table[0] = 'silence'; $table[1] = 'ae ax ah'; $table[2] = 'aa'; $table[3] = 'ao'; $table[4] = 'ey eh uh'; $table[5] = 'er'; $table[6] = 'y iy ih ix'; $table[7] = 'w uw'; $table[8] = 'ow'; $table[9] = 'aw'; $table[10] = 'oy'; $table[11] = 'ay'; $table[12] = 'h'; $table[13] = 'r'; $table[14] = 'l'; $table[15] = 's z'; $table[16] = 'sh ch jh zh'; $table[17] = 'th dh'; $table[18] = 'f v'; $table[19] = 'd t n'; $table[20] = 'k g ng'; $table[21] = 'p b m'; sub DecodeSpeechVisemeType { return $table[$_[0]]; } } sub DecodeSpeechVisemeFeature { if($_[0] == 0){return 'None';} elsif($_[0] == 1){return 'Stressed';} elsif($_[0] == 2){return 'Emphasis';} else{die "unknown SpeechVisemeFeature";} }