#!/Perl
use strict;
use warnings;
use Win32::OLE;
use Data::Dumper;

$| = 1;
# just for LOG FILE ouput ...
my ($LOG);
my $write_log = 0;
my $verb = 1; # increase to 9 to see more output
my $outfile = "temp.$0.txt"; # note name of perl file used as base
if ( open( $LOG, ">$outfile" ) ) {
    $write_log = 1; # we have a LOG file
} else {
    $write_log = 0;
    prt( "WARNING: Unable to open $outfile LOG ...\n" );
}

# from : http://public.activestate.com/authors/jandubois/Perl/TPC3/fun.html
# found these tit-bits ... and later used some of the ideas indicated ...
# like the DESTROY, I used to build a 'cycle()' service, letting the speech continue
#package Speech;
#sub TIEHANDLE {
#      my $voice = Win32::OLE->new('Speech.VoiceText');
#      $voice->Register('', 'Perl');
#      bless \$voice => shift;}
#sub PRINT {
#      my $voice = shift;
#      my $text = join('', @_);
#      print STDOUT $text, "\n";
#      $$voice->Speak($text, 0); }
#sub DESTROY {
#      my $voice = shift;
#      sleep(1) while $$voice->IsSpeaking;}
#package main;
#tie *SPEECH, 'Speech'; END {untie *SPEECH}
#print SPEECH "It is now ", scalar localtime;
#
# prerequisits -
# downloaded from : http://www.microsoft.com/msagent/downloads/user.asp#tts
# dtemp\msspeech - actcnc.exe lhttseng.exe MSagent.exe spchapi.exe
# ran all of these ... not sure exactly what was installed ...
# now I can at least get the voice engine ...
# more from : http://www.microsoft.com/downloads/details.aspx?FamilyId=5E86EC97-40A7-453F-B0EE-6583171B4530&displaylang=en#filelist
# downloaded and installed the SpeechSDK511.exe
# which points to more at : http://www.microsoft.com/downloads/details.aspx?familyid=1194ED95-7A23-46A0-BBBC-06EF009C053A&displaylang=en
# I loaded, converted to MSVC8 (Microsoft Visual C/C++ NET 2005 Express) the 'SimpleTTS.vb' sample,
# and it ran fine ... saw SpeechLib.SpVoice in this VB application sample, and tried it -
#my $voice = Win32::OLE->new('SpeechLib.SpVoice') or die "Unable to get voice engine ...\n". Win32::OLE->LastError() . "\n";
# BUT IT FAILED ...
my $use_voice_text = 0;
my ($voice);
if ($use_voice_text) {
    # 2011-01-03 - Now FAILS???
    $voice = Win32::OLE->new('Speech.VoiceText') 
        or die "Unable to get voice engine ...\n". Win32::OLE->LastError() . "\n";
} else {
    # 2011-01-03 - This worls fine in XP
    $voice = Win32::OLE->new('SAPI.SpVoice')
        or die "Unable to get voice engine ...\n[". Win32::OLE->LastError() . "]\n";
}

# tried all sorts of things like ...
#$voice->{Enabled} = 1;
#$voice->{IsSpeaking} = 0;
#cycle($voice);
# read again that you need to do Register first ...
$voice->Register('', 'Perl');
##bless \$voice => shift;

prt( "Got voice $voice ". Dumper( $voice ) . "\n" );
# NOW Dumper shows -
if ($use_voice_text) {
    # Got voice Win32::OLE=HASH(0x18361d0) $VAR1 = bless( {
    #                 'Speed' => 150,
    #                 'Enabled' => 1,
    #                 'IsSpeaking' => 0
    #               }, 'Win32::OLE' );
    # where as previously it showed each item as 'undef' ...
    # **** IT IS WORKING FINE ****
    ###$voice->Speak("It is now ". scalar localtime, 0);
    my $orgspd = 150;
    # the speech engine REMEMBERS changes to this speed parameter - it started at 150, so
    my $spd = $voice->{Speed};
    $voice->{Speed} = $orgspd;
    vprt( $voice, "Speed: ".$voice->{Speed}.". It is now ", scalar localtime );
    prt( "Played $voice ". Dumper( $voice ) . "\n" );
    prt( "Try some 'Speed' changes ...\n" );
    $voice->{Speed} = 100; # slower = TOO SLOW
    vprt( $voice, "Speed: ".$voice->{Speed}.". It is now ", scalar localtime );
    prt( "That made it SLOWER, and I already thought it was TOO SLOW, so ...\n" );
    $voice->{Speed} = 250; # fast = TOO FAST
    vprt( $voice, "Speed: ".$voice->{Speed}.". It is now ", scalar localtime );
    $voice->{Speed} = 200; # I like about this ...
    vprt( $voice, "Speed: ".$voice->{Speed}.". It is now ", scalar localtime );
    vprt( $voice, "Now headed for exit ...");
} else {
    vprt( $voice, "It is now ".(scalar localtime)."\n" );
    vprt( $voice, "Now headed for exit ...\n");
    SAPItest();
}
exit;

# I note from : http://cpan.uwinnipeg.ca/htdocs/Win32-SAPI4/Win32/SAPI4.pm.html
# that there is another module for 'speech' ...
# use Win32::SAPI4;
#    my $vt   = Win32::SAPI4::VoiceText->new();
#    my $dss  = Win32::SAPI4::DirectSpeechSynthesis->new();
#    my $dsr  = Win32::SAPI4::DirectSpeechRecognition->new();
#    my $vtel = Win32::SAPI4::VoiceTelephony->new();    
#    my $vd   = Win32::SAPI4::VoiceDictation->new();
#    my $vc   = Win32::SAPI4::VoiceCommand->new();
# PREREQUISITES
# The Microsoft Speech API 4.0. It can be downloaded for free from http://www.microsoft.com/speech
# (go to the 'old versions' and find the 4.0 version)
# and at : http://cpan.uwinnipeg.ca/htdocs/Win32-SAPI5/Win32/SAPI5.html
#use Win32::SAPI5;
#    my $object = Win32::SAPI5::SpVoice->new();
#    my $object = Win32::SAPI5::SpNotifyTranslator->new();
#    my $object = Win32::SAPI5::SpObjectTokenCategory->new();
#    my $object = Win32::SAPI5::SpObjectToken->new();
#    my $object = Win32::SAPI5::SpResourceManager->new();
#    my $object = Win32::SAPI5::SpStreamFormatConverter->new();
#    my $object = Win32::SAPI5::SpMMAudioEnum->new();
#    my $object = Win32::SAPI5::SpMMAudioIn->new();
#    my $object = Win32::SAPI5::SpMMAudioOut->new();
#    my $object = Win32::SAPI5::SpRecPlayAudio->new();
#    my $object = Win32::SAPI5::SpStream->new();
#    my $object = Win32::SAPI5::SpSharedRecoContext->new();
#    my $object = Win32::SAPI5::SpInprocRecognizer->new();
#    my $object = Win32::SAPI5::SpSharedRecognizer->new();
#    my $object = Win32::SAPI5::SpLexicon->new();
#    my $object = Win32::SAPI5::SpUnCompressedLexicon->new();
#    my $object = Win32::SAPI5::SpCompressedLexicon->new();
#    my $object = Win32::SAPI5::SpPhoneConverter->new();
#    my $object = Win32::SAPI5::SpNullPhoneConverter->new();
#    my $object = Win32::SAPI5::SpTextSelectionInformation->new();
#    my $object = Win32::SAPI5::SpPhraseInfoBuilder->new();
#    my $object = Win32::SAPI5::SpAudioFormat->new();
#    my $object = Win32::SAPI5::SpWaveFormatEx->new();
#    my $object = Win32::SAPI5::SpInProcRecoContext->new();
#    my $object = Win32::SAPI5::SpCustomStream->new();
#    my $object = Win32::SAPI5::SpFileStream->new();
#    my $object = Win32::SAPI5::SpMemoryStream->new();
#######################################################
#
# Partial OLE Class List use Win32::OLE; use Win32::OLE qw(in valof with OVERLOAD EVENTS);
# use Win32::OLE::Const 'Microsoft DAO';
# use Win32::OLE::Const 'Microsoft ActiveX Data Objects';
# my $Access = Win32::OLE->new('Access.Application', 'Quit');
# my $Conn = Win32::OLE->new('ADODB.Connection'); # creates a connection object
# my $RS = Win32::OLE->new('ADODB.Recordset');    # creates a recordset object
# use Win32::OLE::Variant;
# use Win32::OLE::Const 'Microsoft Excel';
# use Win32::OLE::NLS qw(:DEFAULT :LANG :SUBLANG);
# my $Excel = Win32::OLE->new('Excel.Application', 'Quit');
# my $IE  = Win32::OLE->new("InternetExplorer.Application")
# Win32::OLE->WithEvents($IE,\&Event,"DWebBrowserEvents2");
# Win32::OLE->MessageLoop(); Win32::OLE->QuitMessageLoop();
# Win32::OLE->SpinMessageLoop;
# my $Count = Win32::OLE->EnumAllObjects(sub { });
# my $Class = Win32::OLE->QueryObjectType($Object);
# my $voice = Win32::OLE->new('Speech.VoiceText') 
# use Win32::OLE::Const 'Microsoft Word';
# my $Word = Win32::OLE->new('Word.Application', 'Quit');
# my $wd = Win32::OLE::Const->Load("Microsoft Word 8.0 Object Library");
# $wd = Win32::OLE::Const->Load("Microsoft Word 9.0 Object Library");
# $wd = Win32::OLE::Const->Load("Microsoft Word 10.0 Object Library");
# $enumerate = new Win32::OLE::Enum($paragraphs);
# Collections - Characters Words Sentences Paragraphs Sections HeadersFooters 
# my $xmlDoc = Win32::OLE->new('MSXML2.DOMDocument.3.0') or mydie( "new() failed" );
# All the 'classes' appear to be enumerated in the registry, under HKEY_CLASSES_ROOT
#
##########################################################################################
#### SUBS BELOW
###############

sub cycle {
      my $v = shift;
      ###sleep(1) while $$v->IsSpeaking;
      sleep(1) while $v->IsSpeaking;
}

sub vprt {
      my $v = shift;
      my $t = join('', @_);
      # print STDOUT $t, "\n";
      prt( "$t\n" ); # show the text being SPOKEN ...
      $v->Speak($t, 0); # SPEAK THE TEXT
      ###$$v->Speak($t, 0);
	  cycle($v);
}

#############################
### from : 
sub SAPIgetVoices
{
    #####################################################################
    # SAPIgetVoices
    #-------------------------------------------------------------------#
    # Returns all SAPI voices via a hash
    #####################################################################
    use Win32::OLE;

    my $tts = Win32::OLE->new("Sapi.SpVoice") or die "Sapi.SpVoice failed";
    my %VOICES;
    for(my $VoiceCnt=0;$VoiceCnt < $tts->GetVoices->Count();$VoiceCnt++)
    {
        my $desc = $tts->GetVoices->Item($VoiceCnt)->GetDescription;

        $VOICES{"$desc"} = $VoiceCnt;
    }
    return %VOICES;
}

sub SAPItalk
{
    #####################################################################
    # SAPItalk
    #-------------------------------------------------------------------#
    # Speaks the text in the voice number specified
    #####################################################################
    use Win32::OLE;

    my ($text,$voice) = @_;

    my $tts = Win32::OLE->new("Sapi.SpVoice") or die "Sapi.SpVoice failed";
    $tts->{Voice} = $tts->GetVoices->Item($voice);

    print "[ $text ]\n";
    $tts->Speak("$text", 1);

    $tts->WaitUntilDone(-1);
}

sub SAPIwave
{
    #####################################################################
    # SAPIwave
    #-------------------------------------------------------------------#
    # Creates a wave file, worksjust like SAPItalk
    #####################################################################

    my ($text,$voice,$wave) = @_;
    use Win32::OLE;

    my $type = Win32::OLE->new("SAPI.SpAudioFormat");

    # stereo = add 1
    # 16-bit = add 2
    # 8KHz = 4
    # 11KHz = 8
    # 12KHz = 12
    # 16KHz = 16
    # 22KHz = 20
    # 24KHz = 24
    # 32KHz = 28
    # 44KHz = 32
    # 48KHz = 36
    $type->{Type} = 30;

    my $stream = Win32::OLE->new("SAPI.SpFileStream");
    $stream->{Format} = $type;

    $stream->Open("$wave",3,undef);

    my $tts=Win32::OLE->new("SAPI.SpVoice");
    $tts->{AudioOutputStream}=$stream;
    $tts->Speak($text,1);

    $tts->WaitUntilDone(-1);

    $stream->Close(  );
}

sub SAPItest
{
    #####################################################################
    # SAPItest
    #-------------------------------------------------------------------#
    # Speaks each voice installed in the SAPI environment.
    #####################################################################
    use Win32::OLE;

    my $tts = Win32::OLE->new("Sapi.SpVoice") or die "Sapi.SpVoice failed";

    for(my $VoiceCnt=0;$VoiceCnt < $tts->GetVoices->Count();$VoiceCnt++)
    {
        $tts->{Voice} = $tts->GetVoices->Item($VoiceCnt);
        my $desc = $tts->GetVoices->Item($VoiceCnt)->GetDescription;
        
        my $text = "This is $desc, voice number $VoiceCnt";
        print "[ $text ]\n";
        $tts->Speak("$text", 1);

        $tts->WaitUntilDone(-1);
    }
}

################################
### output and log file
sub wlog {
	my $ml = shift;
	print $LOG $ml;
}

sub prt {
	my $m = shift;
	if ($write_log) {
		wlog($m);
	}
	print $m;
}

sub prtv9 {
	my $ms = shift;
	if ($verb > 8) {
		prt($ms);
	}
}

sub mydie {
	my $msg = shift;
	if ($write_log) {
		wlog($msg);
	}
	die $msg;
}

# eof - autosp01.pl

