Page 5 of 5

Re: SAPI : change Voice

PostPosted: Tue Feb 13, 2024 4:38 am
by Danielmaximiliano
Hola Jimmy :
me explicarias como se disparan los eventos al utilizar
Code: Select all  Expand view
oSpVoice:__hSink := __axRegisterHandler( oSpVoice:__hObj, { | a, b, c, d, e | OnEvent( a, b, c, d, e ) } )

al hacer Play en el form
Code: Select all  Expand view
DEFINE BUTTON oBtnPlay
         ROW 160
         COL 460
         WIDTH 90
         HEIGHT 28
         ACTION( oSpVoice:Speak( cTextToRead, ( SVSFlagsAsync + SVSFPurgeBeforeSpeak ) ) )


Code: Select all  Expand view
FUNCTION OnEvent()

LOCAL iMax  := PCOUNT()
LOCAL i
LOCAL aPara := ARRAY( 5 )

   FOR i := 1 TO iMax
      aPara[ i ] := PValue( i )
   NEXT

   DO CASE
      CASE aPara[ 1 ] = 2
         oSpVoice_Word( aPara[ 2 ], aPara[ 3 ], 0, 0 )
      CASE aPara[ 1 ] = 5
         oSpVoice_Word( aPara[ 2 ], aPara[ 3 ], aPara[ 4 ], aPara[ 5 ] )
   ENDCASE
RETURN NIL


Code: Select all  Expand view
PROCEDURE oSpVoice_Word( StreamNumber, StreamPosition, CharacterPosition, Length )

   //  In order to show this selection,
   //  the Text1.HideSelection property must be False!

   SetProperty( "SPEAK", "oGetText", "HideSelection", .F. )

   SetProperty( "SPEAK", "oGetText", "SelectRange", { CharacterPosition, CharacterPosition + Length } )
   Domethod( "SPEAK", "oGetText", "SetFocus" )
   //   Domethod("SPEAK","oGetText","ScrolltoCaret")
   DO EVENTS
RETURN

Re: SAPI : change Voice

PostPosted: Wed Feb 14, 2024 8:22 am
by Jimmy
hi Daniel,

i have try to Debug Events (see last Page) and have ask "what does they mean"
Antonio found Solution that 1st Parameter are the Event "Number"

Events and meanings:
1 --> Start
3 --> An OLE object is received, probably self OLE object ?
5 --> Word, 4th param is position, 5th param is length
6 --> ???
7 --> Sentence, 4th param is position, 5th param is length
8 --> ???
2 --> End

so i watch for
Code: Select all  Expand view
     CASE aPara[ 1 ] = 2
         // End of Text
      CASE aPara[ 1 ] = 5
         // highlight WORD

Re: SAPI : change Voice

PostPosted: Wed Feb 14, 2024 11:56 am
by Danielmaximiliano
Hola Jmmy :
yo lo resolví asi en HMG/Harbour
Code: Select all  Expand view
Function Play()

 oNarrator:EventInterests := SVEAllEvents + SVEAudioLevel
 oNarrator:Speak( Main.RichEdit_1.VALUE, ( SVSFlagsAsync + SVSFPurgeBeforeSpeak ))
 
 Do While .NOT. (oNarrator:WaitUntilDone (.1) )
    SetProperty( "Main", "RichEdit_1", "HideSelection", .F. )
    SetProperty( "Main", "RichEdit_1", "SelectRange", { oNarrator:Status:InputWordPosition , ;
                                                        oNarrator:Status:InputWordPosition  + oNarrator:Status:InputWordLength } )
    Domethod( "Main", "RichEdit_1", "SetFocus" )
    DoEvents()
 ENDDO
Return

Microsoft Note :
The WaitUntilDone method blocks execution until the voice finishes speaking, enabling an application to speak a text stream asynchronously and determine when it finishes. The hidden SpeakCompleteEvent method is similar to WaitUntilDone, except that it returns an event handle for the background speaking process, and does not block application execution.

Re: SAPI : change Voice

PostPosted: Fri Feb 16, 2024 2:35 pm
by Silvio.Falconi
please can you post the entire test with the recources so we can help you too?


¿Puedes publicar la prueba completa con los recursos para que podamos ayudarte también?

Re: SAPI : change Voice

PostPosted: Fri Feb 16, 2024 5:50 pm
by Danielmaximiliano
Silvio.Falconi wrote:please can you post the entire test with the recources so we can help you too?


¿Puedes publicar la prueba completa con los recursos para que podamos ayudarte también?


Hola Silvio
ya está la solución con respecto a Sapi.Voice con la ayuda de Antonio Linares

Re: SAPI : change Voice

PostPosted: Fri Feb 16, 2024 7:51 pm
by Silvio.Falconi
Danielmaximiliano wrote:
Silvio.Falconi wrote:please can you post the entire test with the recources so we can help you too?


¿Puedes publicar la prueba completa con los recursos para que podamos ayudarte también?


Hola Silvio
ya está la solución con respecto a Sapi.Voice con la ayuda de Antonio Linares


Yes It run ok but i Wish change the voices

Re: SAPI : change Voice

PostPosted: Sat Feb 24, 2024 12:37 am
by Danielmaximiliano
Silvio.Falconi wrote:
Yes It run ok but i Wish change the voices

Hola Silvio :
no soy programador en FW sino en HMG/Harbour no recordaba estar en este foro pero ingrese en el 2012, vi que Jimmy y varios
en harbour tenian el mismo problema de elegir la voz del narrador de windows y jimmy hizo esta pregunta en este foro, la cual fue resuelta por Antonio Linares al cual estoy muy agradecido.
Jimmy tendria que dejar un ejemplo en FW para los otros usarios de este foro pero yo dejo el ejemplo de Harbour/HMG

Code: Select all  Expand view
Function ChangeVoice()  //cambiar voz desde un doble click sobre la lista de narradores
//oNarrator  := CreateObject( "Sapi.SPVoice" , "WithEvents" )
Local oVoice

  For Each oVoice In oNarrator:GetVoices()                      
     If (oVoice:GetDescription(0) == Main.List_1.item(Main.List_1.Value))
      oNarrator:voice := oVoice
      //oNarrator:voice   := oNarrator:GetVoices():Item(  Main.Lis t_1.Value)  // Funciona tambien
      oNarrator:Speak( "Narrador " + oVoice :GetAttribute("Name"), SVSFlagsAsync )
     endif
  Next 

RETURN

Re: SAPI : change Voice

PostPosted: Sat Feb 24, 2024 5:39 am
by Jimmy
hi,

i have not post a Sample while Original Source use *.RC and EDITTEXT
but for full working Sample i need RTF but don´t know how to change *.RC

please can somebody change EDITTEXT to RTF to get it full working, please
https://forums.fivetechsupport.com/viewtopic.php?t=12983

i have used that Source and add CODE, to highlight WORD which is spoken
Code: Select all  Expand view
 oVoice:__hSink := __axRegisterHandler( oVoice:__hObj, { | a,b,c,d,e,f | OnEvent( a,b,c,d,e ) } )

FUNCTION onEvent()
LOCAL iMax  := PCOUNT()
LOCAL i
LOCAL aPara := ARRAY(5)

   FOR i := 1 TO iMax
     aPara[i] := PValue( i )
   NEXT
   
   DO CASE
      CASE aPara[1] = 2
         oSpVoice_Word(aPara[2],aPara[3],0, 0)
      CASE aPara[1] = 5
         oSpVoice_Word(aPara[2],aPara[3],aPara[4], aPara[5])
   ENDCASE
RETURN NIL

PROCEDURE oSpVoice_Word(StreamNumber, StreamPosition, CharacterPosition, Length )
   // NEED RTF Control
   oGetText:SetSel({CharacterPosition , CharacterPosition+Length})
   oGetText:SetFocus()
   SYSRefresh()
RETURN

Re: SAPI : change Voice

PostPosted: Sat Feb 24, 2024 5:55 am
by Antonio Linares
Dear Jimmy,

Please post your current PRG and RC and I will help you to use RTF

thanks

Re: SAPI : change Voice

PostPosted: Sat Feb 24, 2024 6:50 am
by Jimmy
hi Antonio,

here is the CODE of PRG
Code: Select all  Expand view
#include "FiveWin.ch"

static lIsPaused := .F.

MEMVAR oGetText
function Main()

   local oDlg, oBtnPlay, oBtnPause, oBtnStop, oBtnInterroga, oBrowVoices, cVoice, oCbxOutPut, oCbxVoiceNormal
   local oCbxVoiceAlert, oMeter
   local oBtnVolumeUp, oBtnVolumeDown, oBtnVelocityUp, oBtnVelocityDown
   local oGetVolumeLevel, oGetVelocityLevel
   local cMeter
   local aVoices := {}, aPhoneme := {}, aVoiceForSelect := {}
   local aOutPutStream := { "Voz", "File" }, cSelOutPutStream := "Voz"
   local oFile := tOleAuto():New( "Sapi.SpFileStream" )
   local nVolume := 50
   local nVelocity := 0
   local n := 0, n2 := 0
   local oVoice := tOleAuto():New( "Sapi.SPVoice", "WithEvents" )
   local oVoiceAlert := tOleAuto():New( "Sapi.SPVoice", "WithEvents" )
   local cSelNormalVoice, cSelAlertVoice


   // SpeechVoiceFlags
   local SVSFDefault := 0
   local SVSFlagsAsync := 1
   local SVSFPurgeBeforeSpeak  := 2
   local SVSFIsXML := 8
   // SpeechVoiceEvents
   local SVEStartInputStream := 2
   local SVEEndInputStream := 4
   local SVEVoiceChange := 8
   local SVEBookmark := 16
   local SVEWordBoundary := 32
   local SVEPhoneme := 64
   local SVESentenceBoundary := 128
   local SVEViseme := 256
   local SVEAudioLevel := 512
   local SVEPrivate := 32768
   local SVEAllEvents := 33790
   //SpeechVoicePriority
   local SVPNormal := 0
   local SVPAlert := 1
   local SVPOver := 2
   local cTextToRead := "La Guía de Programación de FiveWin, ofrece una introducción dirigida a técnicas" + CRLF +;
                        "de desarrollo de aplicaciones de Windows utilizando (x)Harbour y FiveWin. Esta" + CRLF +;
                        "guía explicará los conceptos básicos que usted debe tener para construir correctamente" + CRLF +;
                        "sus aplicaciones de Windows."
   local ISpeechObjectTokens

   PUBLIC oGetText

   ISpeechObjectTokens := oVoice:GetVoices()

   SET ALTER ON
   SET ALTER TO "_LOGIT2.TXT "

   For n = 0 To Len( oVoice:GetVoices() )
   TRY
       Aadd( aVoices, { n, ISpeechObjectTokens[n]:GetAttribute( 'Name' ), ISpeechObjectTokens[n]:GetAttribute( 'Gender' ),;
                          ISpeechObjectTokens[n]:GetAttribute( 'Age' ), ISpeechObjectTokens[n]:GetAttribute( 'Language' ),;
                          ISpeechObjectTokens[n]:GetAttribute( 'Vendor' ) } )
       Aadd( aVoiceForSelect, ISpeechObjectTokens[n]:GetAttribute( 'Name' ) )
   CATCH
   END
   Next

   cSelNormalVoice := aVoiceForSelect[1]
   cSelAlertVoice := aVoiceForSelect[1]

   oVoiceAlert:Priority := SVPAlert // Set oVoiceAlert as an 'alert' voice
   oVoiceAlert:Voice := oVoiceAlert:GetVoices( "Name = " + cSelNormalVoice ):Item( 0 )
   oVoiceAlert:Volume := 90 // Set the volume of the Alert Voice at 90 to a Maximum of 100

   oVoice:AlertBoundary := SVEPhoneme // Allow to Stop voice by Another one with Alert priority
   oVoice:EventInterests := SVEWordBoundary // SVEAllEvents + SVEAudioLevel

   oVoice:Voice := oVoice:GetVoices( "Name = " + cSelNormalVoice ):Item( 0 )
   oVoice:Volume := nVolume
   oVoice:Rate := nVelocity
   
   oVoice:__hSink := __axRegisterHandler( oVoice:__hObj, { | a,b,c,d,e,f | OnEvent( a,b,c,d,e ) } )

   DEFINE DIALOG oDlg RESOURCE "sound"

   REDEFINE GET oGetText VAR cTextToRead ID 10 OF oDlg MULTILINE UPDATE

   REDEFINE METER oMeter VAR cMeter ID 3000 OF oDlg NOPERCENTAGE UPDATE

   REDEFINE BUTTON oBtnPlay ID 2010 OF oDlg ACTION ( oVoice:Speak( cTextToRead, ( SVSFlagsAsync + SVSFIsXML ) ) ) UPDATE

   REDEFINE BUTTON oBtnPause ID 2020 OF oDlg ACTION ( PauseResume( oVoice, oBtnPause ), oDlg:Update() ) UPDATE

   REDEFINE BUTTON oBtnStop ID 2030 OF oDlg ACTION ( iif( lIsPaused, ( lIsPaused := .F., oBtnPause:SetText( "Pause" ) ) , ),;
            oVoice:Speak( " ",SVSFPurgeBeforeSpeak ) ) UPDATE

   REDEFINE BUTTON oBtnInterroga ID 2040 OF oDlg ACTION ;
           ( oVoiceAlert:Speak( "This is a Warning Interrupt", SVSFlagsAsync ), MsgInfo( "Esto es una prueba de interrupción de Alerta" ) )

   REDEFINE BUTTON oBtnVolumeUp ID 120 OF oDlg;
            ACTION ( nVolume := nVolume + 10, iif( nVolume > 100, ( nVolume := 100 , MsgInfo( "Máximum Volume" ) ) ,;
            oVoice:Volume := nVolume ), oGetVolumeLevel:SetText( AllTrim( Str( nVolume ) ) ) ) UPDATE

   REDEFINE BUTTON oBtnVolumeDown ID 130 OF oDlg;
            ACTION ( nVolume := nVolume - 10, iif( nVolume < 0, ( nVolume := 0 , MsgInfo( "Mínimum Volume" ) ),;
            oVoice:Volume := nVolume ), oGetVolumeLevel:SetText( AllTrim( Str( nVolume ) ) ) ) UPDATE

   REDEFINE BUTTON oBtnVelocityUp ID 140 OF oDlg;
            ACTION ( nVelocity++, iif( nVelocity > 10, ( nVelocity := 10 , MsgInfo( "Máximum Velocity" ) ),;
            oVoice:Rate := nVelocity ), oGetVelocityLevel:SetText( AllTrim( Str( nVelocity ) ) ) ) UPDATE

   REDEFINE BUTTON oBtnVelocityDown ID 150 OF oDlg;
            ACTION ( nVelocity--, iif( nVelocity < -10, ( nVelocity := -10 , MsgInfo( "Mínimum Velocity" ) ),;
            oVoice:Rate := nVelocity ), oGetVelocityLevel:SetText( AllTrim( Str( nVelocity ) ) ) ) UPDATE

   REDEFINE GET oGetVolumeLevel VAR nVolume ID 160 OF oDlg UPDATE

   oGetVolumeLevel:SetText( Str( nVolume ) )

   REDEFINE GET oGetVelocityLevel VAR nVelocity ID 170 OF oDlg UPDATE

   oGetVelocityLevel:SetText( Str( nVelocity ) )

   REDEFINE LISTBOX oBrowVoices FIELDS AllTrim( Str( aVoices[ oBrowVoices:nAt, 1 ] ) ), aVoices[ oBrowVoices:nAt, 2 ],;
            aVoices[ oBrowVoices:nAt, 3 ], aVoices[ oBrowVoices:nAt, 4 ], aVoices[ oBrowVoices:nAt, 5 ], aVoices[ oBrowVoices:nAt, 6 ];
            HEADERS "Item", "Nombre", "_", "Edad", "Idioma ID", "Empresa";
            COLSIZES 31, 77, 72, 42, 58, 64;
            ON DBLCLICK oVoice:Voice := oVoice:GetVoices():Item( aVoices[ oBrowVoices:nAt, 1 ]  );
            ID 200 OF oDlg UPDATE

   oBrowVoices:SetArray( aVoices )

   REDEFINE COMBOBOX oCbxOutPut VAR cSelOutPutStream;
                  ITEMS aOutPutStream ID 210 OF oDlg UPDATE ;
            ON CHANGE OutPutStream( cSelOutPutStream, oVoice, oFile )

   REDEFINE COMBOBOX oCbxVoiceNormal VAR cSelNormalVoice;
                  ITEMS aVoiceForSelect ID 220 OF oDlg UPDATE;
            ON CHANGE oVoice:Voice := oVoice:GetVoices( "name = " + cSelNormalVoice ):Item( 0 )


   REDEFINE COMBOBOX oCbxVoiceAlert VAR cSelAlertVoice;
                  ITEMS aVoiceForSelect ID 230 OF oDlg UPDATE;
            ON CHANGE oVoiceAlert:Voice := oVoiceAlert:GetVoices( "name = " + cSelAlertVoice ):Item( 0 )


   ACTIVATE DIALOG oDlg CENTERED

  SET ALTER TO
  SET ALTER OFF

return nil

function PauseResume( oVoice, oBtnPause )

   iif( lIsPaused,;
      ( oVoice:Resume(), lIsPaused := .F., oBtnPause:SetText( "Pause" ) ),;
      ( oVoice:Pause(), lIsPaused := .T., oBtnPause:SetText( "Resume" ) ) )

return nil

function OutPutStream( cSelOutPutStream, oVoice, oFile )

   local SSFMCreateForWrite := 3

   if cSelOutPutStream = "File"
      MsgInfo( "OutPut redirected to a File in 'c:\TeechToSpeak.wav'", "Warning!!!" )
      oFile:Open( "c:\TeechToSpeak.wav" , SSFMCreateForWrite )
      oVoice:AudioOutputStream := oFile
   else
      MsgInfo( "OutPut redirected to Speakers", "Warning!!!" )
      oFile:Close()
      oVoice:AudioOutputStream := nil
   endif

return nil

FUNCTION onEvent()
LOCAL iMax  := PCOUNT()
LOCAL i
LOCAL aPara := ARRAY(5)

   FOR i := 1 TO iMax
     aPara[i] := PValue( i )
   NEXT
   
   DO CASE
      CASE aPara[1] = 2
         oSpVoice_Word(aPara[2],aPara[3],0, 0)
      CASE aPara[1] = 5
         oSpVoice_Word(aPara[2],aPara[3],aPara[4], aPara[5])
   ENDCASE
RETURN NIL

PROCEDURE oSpVoice_Word(StreamNumber, StreamPosition, CharacterPosition, Length )
   // NEED RTF Control
   oGetText:SetSel({CharacterPosition , CharacterPosition+Length})
   oGetText:SetFocus()
   SYSRefresh()
RETURN

and this is the RC
Code: Select all  Expand view
   #ifdef __FLAT__
      1 24 "WindowsXP.Manifest"
    #endif

    #define IDC_GROUPBOX1   11
    #define DIALOG_10   10
    #define DIALOG_8   8
    #define DIALOG_7   7
    #define DIALOG_6   6
    #define DIALOG_4   4
    #define DIALOG_3   3
    #define DIALOG_2   2
      #define DIALOG_1   1
    #define DIALOG_5   5




    sound DIALOG 76, 48, 379, 241
    STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
    CAPTION "Text To Speech (TTS)"
    FONT 8, "MS Sans Serif"
    {
    PUSHBUTTON "Play", 2010, 312, 98, 55, 14
    PUSHBUTTON "Pause", 2020, 312, 115, 55, 14
    PUSHBUTTON "Stop", 2030, 312, 132, 55, 14
    PUSHBUTTON "Alert!!!", 2040, 312, 149, 55, 14
    LTEXT "Text To Speech:", -1, 16, 7, 59, 10
    EDITTEXT 10, 16, 17, 278, 74, ES_MULTILINE | ES_READONLY | ES_WANTRETURN | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | ES_NOHIDESEL
    LTEXT "Volumen:", -1, 306, 175, 40, 10
    LTEXT "Velocity:", -1, 344, 175, 37, 10
    PUSHBUTTON "+", 120, 313, 185, 17, 14
    PUSHBUTTON "-", 130, 313, 202, 17, 14
    PUSHBUTTON "+", 140, 351, 185, 17, 14
    PUSHBUTTON "-", 150, 351, 202, 17, 14
    CONTROL "", 200, "TWBrowse", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP, 16, 141, 278, 88
    COMBOBOX 210, 308, 15, 55, 22, CBS_DROPDOWNLIST | WS_TABSTOP
    LTEXT "Double Click to Select:", -1, 16, 128, 78, 10
    LTEXT "Voice OutPut:", -1, 308, 5, 49, 10
    GROUPBOX "", IDC_GROUPBOX1, 304, 91, 70, 77, BS_GROUPBOX
    GROUPBOX "", -1, 4, 0, 298, 122, BS_GROUPBOX
    GROUPBOX "", -1, 4, 121, 298, 115, BS_GROUPBOX
    GROUPBOX "", -1, 304, 0, 70, 33, BS_GROUPBOX
    GROUPBOX "", -1, 304, 165, 70, 71, BS_GROUPBOX
    EDITTEXT 160, 313, 220, 17, 13, ES_READONLY | WS_BORDER | WS_TABSTOP
    EDITTEXT 170, 351, 220, 17, 13, ES_READONLY | WS_BORDER | WS_TABSTOP
    CONTROL "TMeter", 3000, "TMeter", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 16, 95, 279, 22
    COMBOBOX 220, 308, 46, 61, 33, CBS_DROPDOWNLIST | WS_TABSTOP
    LTEXT "Normal Voice:", -1, 308, 36, 49, 10
    GROUPBOX "", -1, 304, 31, 70, 33, BS_GROUPBOX
    COMBOBOX 230, 308, 76, 61, 33, CBS_DROPDOWNLIST | WS_TABSTOP
    LTEXT "Alert Voice:", -1, 308, 66, 49, 10
    GROUPBOX "", -1, 304, 61, 70, 33, BS_GROUPBOX
    }