PureSapi, jouer un texte sélectionné

Programmation d'applications complexes
Avatar de l’utilisateur
DjPoke
Messages : 121
Inscription : mar. 02/nov./2010 13:53
Localisation : Corte, Corse, France
Contact :

PureSapi, jouer un texte sélectionné

Message par DjPoke »

Bonjour à tous,

J'avais besoin d'un lecteur de textes, que l'on puisse sélectionner dans un lecteur externe de fichiers pdf.
Je me suis donc basé sur du code présent sur ce forum, et j'ai fais celui qui suit, nommé PureSapi.

Si vous voyez des choses à améliorer selon vos intérêts... :)

Pour rappel, ce code est x86 exclusivement. (En tout cas chez moi)

Image

Fonctionnement :
1) Lancer le logiciel, et votre lecteur de fichiers pdf.
2) Sélectionnez-y un texte à lire.
3) Appuyez sur le bouton Speak.

Code : Tout sélectionner

#CLSCTX_INPROC_SERVER=1

;// Valeur pour SpeechVoiceSpeakFlags
#SVSFDefault = 0
#SVSFlagsAsync = 1
#SVSFPurgeBeforeSpeak = 2
#SVSFIsFilename = 4
#SVSFIsXML = 8
#SVSFIsNotXML = 16
#SVSFPersistXML = 32
#SVSFNLPSpeakPunc = 64
#SVSFNLPMask = 64
#SVSFVoiceMask = 127
#SVSFUnusedFlags = -128

Enumeration ; SPSTREAMFORMAT
  #SPSF_Default
  #SPSF_NoAssignedFormat
  #SPSF_Text
  #SPSF_NonStandardFormat
  #SPSF_ExtendedAudioFormat
  ;// Standard PCM wave formats
  #SPSF_8kHz8BitMono
  #SPSF_8kHz8BitStereo
  #SPSF_8kHz16BitMono
  #SPSF_8kHz16BitStereo
  #SPSF_11kHz8BitMono
  #SPSF_11kHz8BitStereo
  #SPSF_11kHz16BitMono
  #SPSF_11kHz16BitStereo
  #SPSF_12kHz8BitMono
  #SPSF_12kHz8BitStereo
  #SPSF_12kHz16BitMono
  #SPSF_12kHz16BitStereo
  #SPSF_16kHz8BitMono
  #SPSF_16kHz8BitStereo
  #SPSF_16kHz16BitMono
  #SPSF_16kHz16BitStereo
  #SPSF_22kHz8BitMono
  #SPSF_22kHz8BitStereo
  #SPSF_22kHz16BitMono
  #SPSF_22kHz16BitStereo
  #SPSF_24kHz8BitMono
  #SPSF_24kHz8BitStereo
  #SPSF_24kHz16BitMono
  #SPSF_24kHz16BitStereo
  #SPSF_32kHz8BitMono
  #SPSF_32kHz8BitStereo
  #SPSF_32kHz16BitMono
  #SPSF_32kHz16BitStereo
  #SPSF_44kHz8BitMono
  #SPSF_44kHz8BitStereo
  #SPSF_44kHz16BitMono
  #SPSF_44kHz16BitStereo
  #SPSF_48kHz8BitMono
  #SPSF_48kHz8BitStereo
  #SPSF_48kHz16BitMono
  #SPSF_48kHz16BitStereo
  ;// TrueSpeech format
  #SPSF_TrueSpeech_8kHz1BitMono
  ;// A-Law formats
  #SPSF_CCITT_ALaw_8kHzMono
  #SPSF_CCITT_ALaw_8kHzStereo
  #SPSF_CCITT_ALaw_11kHzMono
  #SPSF_CCITT_ALaw_11kHzStereo
  #SPSF_CCITT_ALaw_22kHzMono
  #SPSF_CCITT_ALaw_22kHzStereo
  #SPSF_CCITT_ALaw_44kHzMono
  #SPSF_CCITT_ALaw_44kHzStereo
  ;// u-Law formats
  #SPSF_CCITT_uLaw_8kHzMono
  #SPSF_CCITT_uLaw_8kHzStereo
  #SPSF_CCITT_uLaw_11kHzMono
  #SPSF_CCITT_uLaw_11kHzStereo
  #SPSF_CCITT_uLaw_22kHzMono
  #SPSF_CCITT_uLaw_22kHzStereo
  #SPSF_CCITT_uLaw_44kHzMono
  #SPSF_CCITT_uLaw_44kHzStereo
  ;// ADPCM formats
  #SPSF_ADPCM_8kHzMono
  #SPSF_ADPCM_8kHzStereo
  #SPSF_ADPCM_11kHzMono
  #SPSF_ADPCM_11kHzStereo
  #SPSF_ADPCM_22kHzMono
  #SPSF_ADPCM_22kHzStereo
  #SPSF_ADPCM_44kHzMono
  #SPSF_ADPCM_44kHzStereo
  ;// GSM 6.10 formats
  #SPSF_GSM610_8kHzMono
  #SPSF_GSM610_11kHzMono
  #SPSF_GSM610_22kHzMono
  #SPSF_GSM610_44kHzMono
  #SPSF_NUM_FORMATS
EndEnumeration

Interface ISpeechObjectTokenCategory Extends IDispatch
  get_Id(Id.i)
  set_Default(TokenId.i)
  get_Default(TokenId.i)
  SetId(Id.p-bstr, CreateIfNotExist.i)
  GetDataKey(SpeechDataKeyLocation.i, ISpeechDataKey.i)
  EnumerateTokens(RequiredAttributes.i, OptionalAttributes.i, ISpeechObjectTokens.i)
EndInterface

Interface ISpeechObjectToken Extends IDispatch
  get_Id(ObjectId.i)
  get_DataKey(ISpeechDataKey.i)
  get_Category(ISpeechObjectTokenCategory.i)
  GetDescription(Locale.i, Description.i)
  SetId(Id.i, CategoryID.i, CreateIfNotExist.l) 
  GetAttribute(AttributeName.p-bstr, AttributeValue.i) 
  CreateInstance(pUnkOuter.i, SpeechTokenContext.l, Object.i)
  Remove(ObjectStorageCLSID.i)
  GetStorageFileName(ObjectStorageCLSID.i, KeyName,FileName.i, SpeechTokenShellFolder.l, FilePath.i)
  RemoveStorageFileName(ObjectStorageCLSID.i, KeyName.i, DeleteFile.l)
  IsUISupported(TypeOfUI.i, ExtraData.variant, Object.i, Supported.i)
  DisplayUI(hWnd.i, Title.i, TypeOfUI.i, ExtraData.variant, Object.i)
  MatchesAttributes(Attributes.i, Matches.i)
EndInterface

Interface ISpeechObjectTokens Extends IDispatch
  get_Count(a.l)
  Item(Index.l, Token.ISpeechObjectToken)
  get__NewEnum(ppEnumVARIANT.IUnknown)
EndInterface

Interface ISpeechAudioFormat Extends IDispatch
  get_Type(SpeechAudioFormatType.i)
  set_Type(SpeechAudioFormatType.i)
  get_Guid(Guid.i)
  set_Guid(Guid.i)
  GetWaveFormatEx(ISpeechWaveFormatEx.i)
  SetWaveFormatEx(ISpeechWaveFormatEx.i)
EndInterface

Interface ISpeechFileStream Extends IDispatch
  get_Format(ISpeechAudioFormat.i)
  set_putref_Format(ISpeechAudioFormat.i)
  Read(Buffer.i, NumberOfBytes.i, BytesRead.i, BytesWritten.i)
  Write(Buffer.i, BytesWritten.i)
  Seek(Position.i, SpeechStreamSeekPositionType.i, NewPosition.i)
  Open(FileName.p-bstr, SpeechStreamFileMode.i, DoEvents.i)
  Close()
EndInterface

Interface ISpeechVoiceStatus Extends IDispatch
  get_CurrentStreamNumber(StreamNumber.i)                                        
  get_LastStreamNumberQueued(StreamNumber.i)                                          
  get_LastHResult(Hresult.i)                                         
  get_RunningState(State.i)                                        
  get_InputWordPosition(Position.i)                                        
  get_InputWordLength(Length.i)                                        
  get_InputSentencePosition(Position.i)                                       
  get_InputSentenceLength(Length.i)                                       
  get_LastBookmark(Bookmark.i)                                     
  get_LastBookmarkId(BookmarkId.i)                                         
  get_PhonemeId(PhoneId.i)                                        
  get_VisemeId(VisemeId.i)                                       
EndInterface

Interface ISpeechVoice Extends IDispatch
  get_Status(ISpeechVoiceStatus.i)
  get_Voice(ISpeechObjectToken.i)
  put_Voice(ISpeechObjectToken.i)
  get_AudioOutput(ISpeechObjectToken.i)
  put_AudioOutput(ISpeechObjectToken.i)
  get_AudioOutputStream(ISpeechBaseStream.i)
  put_AudioOutputStream(ISpeechBaseStream.i)
  get_Rate(long.i)
  put_Rate(long.i)
  get_Volume(long.i)
  put_Volume(long.i)
  put_AllowAudioOutputFormatChangesOnNextSet(VARIANT_BOOL.i)
  get_AllowAudioOutputFormatChangesOnNextSet(VARIANT_BOOL.i)
  get_EventInterests(SpeechVoiceEvents.i)
  put_EventInterests(SpeechVoiceEvents.i)
  put_Priority(SpeechVoicePriority.i)
  get_Priority(SpeechVoicePriority.i)
  put_AlertBoundary(SpeechVoiceEvents.i)
  get_AlertBoundary(SpeechVoiceEvents.i)
  put_SynchronousSpeakTimeout(long.i)
  get_SynchronousSpeakTimeout(long.i)
  Speak(Text.p-bstr, SpeechVoiceSpeakFlags.l, IDVoice.i)
  SpeakStream(ISpeechBaseStream.i, SpeechVoiceSpeakFlags.l, StreamNumber.i)
  Pause()
  Resume()
  Skip(Type.i, NumItems.l, NumSkipped.i)
  GetVoices(RequiredAttributes.i, OptionalAttributes.i, ISpeechObjectTokens.i)
  GetAudioOutputs(RequiredAttributes.i, OptionalAttributes.i, ISpeechObjectTokens.i)
  WaitUntilDone(msTimeout.l, VARIANT_BOOL.i)
  SpeakCompleteEvent(Handle.i)
  IsUISupported(TypeOfUI.i, ExtraData.VARIANT, VARIANT_BOOL.i)
  DisplayUI(hWndParent.i, Title.i, TypeOfUI.i, ExtraData.VARIANT)
EndInterface

Structure AttributeVoice
  Description.s
  LangID.s
  Item.l
  Rate.l
  Volume.l
  SpeechVoice.i
EndStructure

Structure AttributeAudio
  Description.s
  Item.l
  DeviceId.s
EndStructure

Procedure.i ConvertToUnicode(AsciiOrUnicode.s)
  Static *MemoryUnicode
  Protected LenMemoryUnicode.l
  
  If *MemoryUnicode <> 0
    FreeMemory(*MemoryUnicode)
  EndIf
  
  LenMemoryUnicode = StringByteLength(AsciiOrUnicode , #PB_Unicode)
  *MemoryUnicode = AllocateMemory(LenMemoryUnicode + 2)
  If *MemoryUnicode <> 0
    PokeS(*MemoryUnicode, AsciiOrUnicode, Len(AsciiOrUnicode), #PB_Unicode)
    ProcedureReturn *MemoryUnicode
  EndIf
  
  ProcedureReturn 0
EndProcedure

Procedure.i InterfaceData(Option.l, Donnees.i)
  Static NameInterface.l
  
  If Option = 1
    NameInterface = Donnees
    ProcedureReturn 1
  ElseIf Option = -1
    ProcedureReturn NameInterface
  EndIf
  
  ProcedureReturn 0
EndProcedure  

Procedure SetInterfaceData(Option.l, Donnees.i)
  ProcedureReturn InterfaceData(Option.l, Donnees.i)
EndProcedure

Procedure.i GetInterfaceData(Option.l)
  ProcedureReturn InterfaceData(-1, 0)
EndProcedure


Procedure.l SaveTextToWav(Array VoicesSapi.AttributeVoice(1), VoixID.l, Texte.s, File.s, Format.l)
  Protected SpeechFileStream.ISpeechFileStream, SpeechAudioFormat.ISpeechAudioFormat
  Protected SpeechFileStreamDefault.i, IDVoice.l, NameInterface$, NameInterface.l, Ret.l=-10
  Protected  SSFMCreateForWrite = 3 
  
  If VoixID > -1 And VoixID <= ArraySize(VoicesSapi())
    NameInterface = GetInterfaceData(-1)
    
    If NameInterface = 2 ; Speech 11
      NameInterface$ = "SAPI.SpFileStream"
    ElseIf NameInterface = 1 ; Sapi 5
      NameInterface$ = "Speech.SpFileStream"
    Else
      Ret = -9
      Goto Error2
    EndIf
    
    If CLSIDFromProgID_(ConvertToUnicode(NameInterface$), @CLSID.CLSID) < #S_OK
      Ret = -8
      Goto Error2
    EndIf
    
    If CLSIDFromString_(ConvertToUnicode("{AF67F125-AB39-4E93-B4A2-CC2E66E182A7}"), @Refiid.CLSID) < #S_OK
      Ret = -7
      Goto Error2
    EndIf
    
    If  CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechFileStream.ISpeechFileStream) < #S_OK
      Ret = -6
      Goto Error2
    EndIf
    
    If SpeechFileStream\get_Format(@SpeechAudioFormat.ISpeechAudioFormat) < #S_OK 
      Ret = -5
      Goto Error2
    EndIf
    
    If SpeechAudioFormat\set_Type(Format) < #S_OK
      Ret = -4
      Goto Error2
    EndIf 
    
    If SpeechFileStream\Open(File, SSFMCreateForWrite, 0) < #S_OK
      Ret = -3
      Goto Error2
    EndIf 
    
    SpeechVoice.ISpeechVoice = VoicesSapi(VoixID)\SpeechVoice
    If SpeechVoice = 0
      Ret = -2
      Goto Error2
    EndIf 
    
    If SpeechVoice\get_AudioOutputStream(@SpeechFileStreamDefault) < #S_OK
      Ret = -1
      Goto Error2
    EndIf 
    
    If SpeechVoice\put_AudioOutputStream(SpeechFileStream) < #S_OK
      Ret = 0
      Goto Error2
    EndIf
    
    If SpeechVoice\Speak(Texte, 0, @IDVoice) < #S_OK
      Ret = 1
      Goto Error2
    EndIf
    
    
    error2:
    If SpeechFileStream <> 0
      SpeechFileStream\Close()
      SpeechFileStream\Release()
    EndIf
    
    If SpeechAudioFormat <> 0
      SpeechAudioFormat\Release()
    EndIf 
    
    If SpeechFileStreamDefault <> 0
      SpeechVoice\put_AudioOutputStream(SpeechFileStreamDefault)
    EndIf
    
  EndIf 
  ; Renvoie 1 si la fonction réussie
  ProcedureReturn Ret
EndProcedure

Procedure GetAudioOutput(Array VoicesAudio.AttributeAudio(1), *DefaultAudio.long)
  Protected SpeechObjectTokenCategory.ISpeechObjectTokenCategory
  Protected SpeechObjectTokens.ISpeechObjectTokens
  Protected SpeechObjectToken.ISpeechObjectToken
  Protected Clsid.CLSID, Refiid.CLSID, NameInterface$
  Protected Description.i, DescriptionAudio.s, ID.s, DefaultID.s
  Protected a.l, Ret.l=0
  
  #SpeechCategoryAudioOut = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\AudioOutput"
  
  
  NameInterface = GetInterfaceData(-1)
  
  If NameInterface = 2 ; Speech 11
    NameInterface$ = "Speech.SpObjectTokenCategory"
  ElseIf NameInterface = 1 ; Sapi 5
    NameInterface$ = "Sapi.SpObjectTokenCategory"
  Else
    Goto Error3
  EndIf
  
  If CLSIDFromProgID_(ConvertToUnicode(NameInterface$), @Clsid.CLSID) < #S_OK
    Goto Error3
  EndIf
  
  If CLSIDFromString_(ConvertToUnicode("{CA7EAC50-2D01-4145-86D4-5AE7D70F4469}"), @Refiid.CLSID) < #S_OK
    Goto Error3
  EndIf
  
  If CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechObjectTokenCategory.ISpeechObjectTokenCategory) >= #S_OK   
    If SpeechObjectTokenCategory\SetId(#SpeechCategoryAudioOut, 0) >= #S_OK
      
      If SpeechObjectTokenCategory\get_Default(@Description) >= #S_OK
        If Description <> 0
          DefaultID.s = PeekS(Description, -1, #PB_Unicode)
          SysFreeString_(Description)
        EndIf 
      EndIf 
      
      If SpeechObjectTokenCategory\EnumerateTokens(0, 0, @SpeechObjectTokens.ISpeechObjectTokens) >= #S_OK
        If SpeechObjectTokens\get_Count(@Count.l) >= #S_OK
          
          For a = 0 To count -1
            If SpeechObjectTokens\Item(a, @SpeechObjectToken.ISpeechObjectToken) >= #S_OK
              If SpeechObjectToken\GetDescription(0, @Description) >= #S_OK
                If Description <> 0
                  DescriptionAudio.s = PeekS(Description, -1, #PB_Unicode)
                  SysFreeString_(Description)
                  VoicesAudio(a)\Description = DescriptionAudio
                  Ret = Ret + 1
                EndIf
                
                If SpeechObjectToken\get_Id(@Description) >= #S_OK
                  If Description <> 0
                    ID.s = PeekS(Description, -1, #PB_Unicode)
                    VoicesAudio(a)\DeviceId = ID
                    If DefaultID = ID
                      *DefaultAudio\l = a
                    EndIf
                  EndIf 
                  SysFreeString_(Description)
                EndIf
                
                VoicesAudio(a)\Item = a
              EndIf 
              SpeechObjectToken\Release()
            EndIf 
          Next a
          
        EndIf
        SpeechObjectTokens\Release()
      EndIf 
      
    EndIf
    SpeechObjectTokenCategory\Release()
  EndIf 
  
  Error3:
  ProcedureReturn Ret
EndProcedure

Procedure SetAudioOutput(Array VoicesSapi.AttributeVoice(1), Item.l)
  Protected SpeechObjectTokenCategory.ISpeechObjectTokenCategory
  Protected SpeechObjectTokens.ISpeechObjectTokens
  Protected SpeechObjectToken.ISpeechObjectToken
  Protected Clsid.CLSID, Refiid.CLSID, NameInterface$, NameInterface.l
  Protected Description.i, DescriptionAudio.s, a.l, Ret.l=0
  Protected SpeechVoice.ISpeechVoice
  
  #SpeechCategoryAudioOut = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\AudioOutput"
  
  If Item > -1 And Item  <= ArraySize(VoicesSapi())
    SpeechVoice = VoicesSapi(0)\SpeechVoice
    
    If SpeechVoice <> 0
      
      NameInterface = GetInterfaceData(-1)
      
      If NameInterface = 2 ; Speech 11
        NameInterface$ = "Speech.SpObjectTokenCategory"
      ElseIf NameInterface = 1 ; Sapi 5
        NameInterface$ = "Sapi.SpObjectTokenCategory"
      Else
        Goto Error5
      EndIf
      
      If CLSIDFromProgID_(ConvertToUnicode(NameInterface$), @Clsid.CLSID) < #S_OK
        Goto Error5
      EndIf
      
      If CLSIDFromString_(ConvertToUnicode("{CA7EAC50-2D01-4145-86D4-5AE7D70F4469}"), @Refiid.CLSID) < #S_OK
        Goto Error5
      EndIf
      
      If  CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechObjectTokenCategory.ISpeechObjectTokenCategory) >= #S_OK   
        If SpeechObjectTokenCategory\SetId(#SpeechCategoryAudioOut, 0) >= #S_OK
          If SpeechObjectTokenCategory\EnumerateTokens(0, 0, @SpeechObjectTokens.ISpeechObjectTokens) >= #S_OK
            If SpeechObjectTokens\Item(Item, @SpeechObjectToken.ISpeechObjectToken)>= #S_OK
              If SpeechObjectToken <> 0
                If SpeechVoice\put_AudioOutput(SpeechObjectToken)>= #S_OK
                  Ret = 1
                EndIf 
                SpeechObjectToken\Release()
              EndIf 
            EndIf 
            SpeechObjectTokens\Release()
          EndIf 
        EndIf
        SpeechObjectTokenCategory\Release()
      EndIf
      
    EndIf
  EndIf
  
  Error5:
  ProcedureReturn Ret
EndProcedure

Procedure.l FreeSpeakVoice(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice, a.l
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    SpeechVoice\Pause()
    SpeechVoice\Release()
    For a = 0 To ArraySize(VoicesSapi())
      VoicesSapi(a)\SpeechVoice = 0
    Next a
    ProcedureReturn #True
  EndIf 
  
  ProcedureReturn #False
  ; Renvoi vrai ou faux
EndProcedure

Procedure.l InitSpeakVoice(Array VoicesSapi.AttributeVoice(1), NameInterface.l)
  Protected ClsidSpVoice.i, RefiidISpeechVoice.i, Clsid.CLSID, Refiid.CLSID, Language.i
  Protected SpeechVoice.ISpeechVoice, SpeechObjectTokens.ISpeechObjectTokens
  Protected SpeechObjectToken.ISpeechObjectToken, SpeechObjectTokenCategory.ISpeechObjectTokenCategory
  Protected LangIDVoice.s, DescriptionVoice.s, Ret.l = -2, Longueur.l, a.l
  Protected NameInterface$, Count.l, Lang.i, Description.i
  
  ; Sécurité, afin de libérer l'interface Sapi.SpVoice si déjà créé
  FreeSpeakVoice(VoicesSapi())
  
  Longueur =  ArraySize(VoicesSapi())
  
  ;   
  ; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices
  
  If NameInterface = 2 ; Speech 11
    NameInterface$ = "Speech.SpVoice" ; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices
    SetInterfaceData(1, 2)
  ElseIf NameInterface = 1 ; Sapi 5
    NameInterface$ = "SAPI.SpVoice" ; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices
    SetInterfaceData(1, 1)
  Else 
    SetInterfaceData(1, 0)
    Ret = -1
    Goto Error
  EndIf
  
  If CLSIDFromProgID_(ConvertToUnicode(NameInterface$), @Clsid.CLSID) < #S_OK
    Goto Error
  EndIf
  
  If CLSIDFromString_(ConvertToUnicode("{269316D8-57BD-11D2-9EEE-00C04F797396}"), @Refiid.CLSID) < #S_OK
    Goto Error
  EndIf
  
  If  CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechVoice) >= #S_OK   
    Ret = 0
    
    If SpeechVoice\GetVoices(0, 0, @SpeechObjectTokens) >= #S_OK  
      If SpeechObjectTokens\get_Count(@Count) >= #S_OK   
        ;Listing des voix installés
        For a= 0 To Count-1
          If SpeechObjectTokens\Item(a, @SpeechObjectToken) >= #S_OK
            
            If SpeechObjectToken\GetDescription(0, @Description) >= #S_OK
              If Description <> 0
                DescriptionVoice = PeekS(Description, -1, #PB_Unicode)
                SysFreeString_(Description)
              EndIf 
            EndIf  
            
            If SpeechObjectToken\GetAttribute("Language", @Lang) >= #S_OK
              If Lang <> 0
                LangIDVoice = PeekS(Lang, -1, #PB_Unicode)
                SysFreeString_(Lang)
              EndIf 
            EndIf 
            
            If a < (Longueur + 1)
              VoicesSapi(a)\Description = DescriptionVoice
              VoicesSapi(a)\LangID = LangIDVoice
              VoicesSapi(a)\Item = a
              VoicesSapi(a)\Rate = 0
              VoicesSapi(a)\Volume = 100
              VoicesSapi(a)\SpeechVoice = SpeechVoice
              Ret = a + 1
            EndIf 
            
            SpeechObjectToken\Release() 
          EndIf 
        Next
      EndIf
      SpeechObjectTokens\Release() 
    EndIf
  EndIf
  
  Error:
  If ClsidSpVoice <> 0
    FreeMemory(ClsidSpVoice)
  EndIf
  If RefiidSpVoice <> 0
    FreeMemory(RefiidSpVoice)
  EndIf 
  
  ProcedureReturn Ret
  ; Renvoi une valeur > 0 indiquant le nombre de voix
  ; Renvoi 0 si pas de voix installées (possible si le Runtime est installé mais pas les voix!)
  ; Renvoi -1 si Sapi 5 ou Speech Plateform 11 non installés
  ; Renvoi -2 si InterfaceName n'as pas la valeur recquise
EndProcedure

Procedure.l PauseSpeak(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\Pause() >= #S_OK 
      ProcedureReturn #True
    EndIf 
  EndIf 
  
  ProcedureReturn #False
  ; Renvoi vrai ou faux
EndProcedure

Procedure.l ResumeSpeak(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\Resume() >= #S_OK 
      ProcedureReturn #True
    EndIf 
  EndIf 
  
  ProcedureReturn #False
  ; Renvoi vrai ou faux
EndProcedure

Procedure.l StatusSpeak(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice, State.l=-1
  Protected SpeechVoiceStatus.ISpeechVoiceStatus
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\get_Status(@SpeechVoiceStatus) >= #S_OK  
      SpeechVoiceStatus\get_RunningState(@State)
      SpeechVoiceStatus\Release()
    EndIf
  EndIf 
  
  ProcedureReturn State
  ; Renvoi 2 si Sapi en cours d'utilisation
  ; Renvoi 1 si Sapi ok
  ; Renvoi 0 si Speak non lancé une première fois
  ; Renvoi -1 si échec de la fonction
EndProcedure

Procedure.l CurrentStream(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice, Number.l = -1
  Protected SpeechVoiceStatus.ISpeechVoiceStatus
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\get_Status(@SpeechVoiceStatus) >= #S_OK  
      SpeechVoiceStatus\get_CurrentStreamNumber(@Number)
      SpeechVoiceStatus\Release()
    EndIf
  EndIf 
  
  ProcedureReturn Number
  
  ; Renvoi l'ID du Stream en cours, si 0 pas de Stream en cours
  ; Renvoi -1 si échec de la fonction
EndProcedure

Procedure.l SkipStream(Array VoicesSapi.AttributeVoice(1), Item.l)
  Protected SpeechVoice.ISpeechVoice, Sentence.i, ItemSkipped.l
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\Skip(ConvertToUnicode("SENTENCE"), Item, @ItemSkipped) >= #S_OK  
      ProcedureReturn ItemSkipped
    EndIf
  EndIf
  
  ProcedureReturn #False
  
  ; Renvoi le nombre de phrase sautée, peut être positif ou négatif
  ; Renvoi 0 si échec de la fonction
EndProcedure

Procedure.l InputSentenceLength(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice, Lenght.l = -1
  Protected SpeechVoiceStatus.ISpeechVoiceStatus
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    If SpeechVoice\get_Status(@SpeechVoiceStatus) >= #S_OK  
      SpeechVoiceStatus\get_InputSentenceLength(@Lenght) 
      SpeechVoiceStatus\Release()
    EndIf
  EndIf 
  
  ProcedureReturn Lenght
  
  ; Renvoi la longueur de texte de la phrase en cours
  ; Renvoi -1 si échec de la fonction
EndProcedure

Procedure.l PlaySpeakAsync(Array VoicesSapi.AttributeVoice(1), VoixID.l, Texte.s, *Stream.long = 0, Rate.l = -20, Volume.l = -1)
  Protected SpeechVoice.ISpeechVoice, StreamNumber.l, Ret.l = 0, Ret2.l
  Protected SpeechObjectTokens.ISpeechObjectTokens, SpeechObjectToken.ISpeechObjectToken
  
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    
    If VoixID > -1 And VoixID <= ArraySize(VoicesSapi())
      If SpeechVoice\GetVoices(0, 0, @SpeechObjectTokens) >= #S_OK   
        If SpeechObjectTokens\Item(VoixID, @SpeechObjectToken) >= #S_OK     
          If SpeechVoice\put_Voice(SpeechObjectToken) >= #S_OK 
            Ret2 = 1
          EndIf
          SpeechObjectToken\Release() 
        EndIf
        SpeechObjectTokens\Release() 
      EndIf
      
      
      If Volume = -1
        SpeechVoice\put_Volume(VoicesSapi(VoixID)\Volume)
      Else
        If SpeechVoice\put_Volume(Volume) >= #S_OK 
          VoicesSapi(VoixID)\Volume = Volume
        EndIf
      EndIf
      
      If Rate = -20
        SpeechVoice\put_Rate(VoicesSapi(VoixID)\Rate)
      Else
        If SpeechVoice\put_Rate(Rate) >= #S_OK 
          VoicesSapi(VoixID)\Rate = Rate
        EndIf  
      EndIf 
      
    EndIf 
    
    If SpeechVoice\Speak(Texte, #SVSFlagsAsync, @StreamNumber) >= #S_OK 
      If *Stream <> 0
        *Stream\l = StreamNumber
      EndIf 
      Ret = Ret2 + 1
    EndIf
    
  EndIf
  
  ProcedureReturn Ret
  ; Renvoi 2 si la fonction réussie avec l'indice de Voix spécifié
  ; Renvoi 1 si la fonction réussie alors que l'indice de Voix n'est pas spécifié ou 1 si l'indice de Voix est spécifié mais non trouvé (voix par défaut dans ce cas)
  ; Renvoi 0 si la fonction SpeechVoice\Speak échoue 
EndProcedure

Procedure.l StopSpeak(Array VoicesSapi.AttributeVoice(1))
  Protected SpeechVoice.ISpeechVoice, IDVoice.l
  
  SpeechVoice = VoicesSapi(0)\SpeechVoice
  If SpeechVoice <> 0
    ; Le ResumeSpeak est nécessaire au cas où l'état précédent est en Pause
    ; car sinon il faudrait faire un ResumeSpeak après un nouveau Speak
    ResumeSpeak(VoicesSapi())
    If SpeechVoice\Speak("", #SVSFPurgeBeforeSpeak, 0) >= #S_OK 
      ProcedureReturn #True
    EndIf
  EndIf 
  
  ProcedureReturn #False
  ; Renvoi vrai ou faux
EndProcedure



; Envoyer un "Control-C" pour copier le texte de la fenêtre ayant le focus vers le clipboard
Procedure SendControlC(hWnd.i, win.i)
  #KeyDown = 0
  #KeyUp = 2
  
  ; Réactiver le handle sur la fenêtre perdue
  SetForegroundWindow_(hWnd)
  
  Delay(50)

  ; Effectuer un "Control + C"
  keybd_event_(#VK_CONTROL,0,0,0) 
  keybd_event_(#VK_C,0,0,0)
  
  Delay(50)
  
  keybd_event_(#VK_C,0,2,0)
  keybd_event_(#VK_CONTROL,0,2,0)
  
  Delay(50)
    
  ; Réactiver le player
  SetForegroundWindow_(WindowID(win))
  
EndProcedure

Procedure.i GetSelLen(hWnd.i)
  Protected Start.i = 0
  Protected Stop.i = 0
  
  SendMessage_(hWnd, #EM_GETSEL, @Start.i, @Stop.i)
  
  ProcedureReturn Stop.i - Start.i
EndProcedure
  



; Début de création du programme

Enumeration
  #Menu 
EndEnumeration

Enumeration
  #Window
EndEnumeration

Enumeration
  #TimerDetectSpeakfinish
EndEnumeration

Enumeration
  #Indication
  #Voix
  #LangID
  #Edit
  #TextRate
  #Rate
  #Precision
  #TextVolume
  #Volume
  #Speak
  #Pause
  #Resume
  #Stop
  #SaveToWav
EndEnumeration

; invoquer Coinitialize_() qu'une seule fois dans le programme
CoInitialize_(0)


#SAPI_5 = 1
#SPEECH_11 = 2

; // Initialisation du tableau
; Il sera redimensionné par la suite
#LmiteNbVoices = 30
Dim VoicesSapi1.AttributeVoice(#LmiteNbVoices)
; ----------------------------------------------

; // Déclaration du premier index pour le menu qui servira à l'affichage des voix
#DebutIndiceVoix = 10
#DebutIndiceAudio = 100
; ----------------------------------------------


If OpenWindow(#Window, 200, 200, 400, 200, "PureSapi (Player)")
  
  TextGadget(#Voix, 10, 10, 380, 40, "Voix sélectionnée :")
  TextGadget(#LangID, 10, 30, 380, 20, "LangID :")
  
  SetGadgetColor(#Voix, #PB_Gadget_FrontColor, RGB(255,0,0))
  SetGadgetColor(#LangID, #PB_Gadget_FrontColor, RGB(255,0,0)) 
  
  StickyWindow(#Window, #True)
  
  ; // InitSpeakVoice initialise l'interface Sapi.SpVoice et met à jour dans le tableau en argument la liste des voix installées
  ; La valeur de retour de la fonction permet de savoir si on a pu obtenir un pointeur Sapi.SpVoice ainsi que le nombre de voix installées
  If CreateMenu(#Menu, WindowID(0))
    MenuTitle("Sélection de la Voix")
    
    ; Au choix, choisir #SPEECH_11 ou #SAPI_5, vous pouvez tester l'un ou l'autre
    ; comme dans cet exemple
    NbVoice = InitSpeakVoice(VoicesSapi1(), #SPEECH_11)
    If NbVoice < 0
      NbVoice =  InitSpeakVoice(VoicesSapi1(), #SAPI_5)
    EndIf 
    
    If NbVoice > 0
      ; On redimensionne le tableau avec le nombre exacte de voix, -1 car commence à 0
      ReDim VoicesSapi1(NbVoice-1)
            
      For a = 0 To NbVoice -1
        MenuItem( #DebutIndiceVoix + a, VoicesSapi1(a)\Description)
      Next 
      ; la voix par défaut est 0 d'après mes essais.
      SetMenuItemState(#Menu, #DebutIndiceVoix, #True)

      VoiceSapi1 = 0
      SetGadgetText(#Voix, "Voix sélectionnée : " + VoicesSapi1(VoiceSapi1)\Description)
      SetGadgetText(#LangID, "LangID : " + VoicesSapi1(VoiceSapi1)\LangID)
      
    ElseIf NbVoice = 0
      SetGadgetText(#Voix, "Voix sélectionnée : Pas de voix installée")
      MenuItem( #DebutIndiceVoix, "Pas de voix installée")
      
    Else
      SetGadgetText(#Voix, "Voix sélectionnée : Aucune, Veuillez installer SAPI 5")
      MenuItem( #DebutIndiceVoix, "Veuillez installer SAPI 5")
    EndIf
    
    MenuTitle("Sortie Audio")
    Dim SortieAudio.AttributeAudio(10)
    CountAudioOutput.l = GetAudioOutput(SortieAudio(), @DefautAudio)
    If CountAudioOutput > 0
      For a = 0 To CountAudioOutput - 1
        MenuItem(#DebutIndiceAudio + a, SortieAudio(a)\Description)
      Next a
      SetMenuItemState(#Menu, #DebutIndiceAudio + DefautAudio, #True)
    Else
      MenuItem(#DebutIndiceAudio, "Erreur Sortie Audio")
    EndIf
    
  EndIf
  
  TextGadget(#TextVolume, 10, 50, 380, 20, "Volume")
  TrackBarGadget(#Volume, 10,  70, 380, 20, 0, 100)
  
  TextGadget(#TextRate, 10, 90, 380, 20, "Vitesse")
  TrackBarGadget(#Rate, 10,  110, 380, 20, 0, 20)
  
  ButtonGadget(#Speak, 20, 140, 80, 30, "Speak")
  ButtonGadget(#Pause, 110, 140, 80, 30, "Pause")
  ButtonGadget(#Resume, 200, 140, 80, 30, "Resume")
  ButtonGadget(#Stop, 290, 140, 80, 30, "Stop")
  
  DisableGadget(#Resume, 1)
  DisableGadget(#Pause, 1)
  DisableGadget(#Stop, 1)
  
  If NbVoice < 1
    DisableGadget(#Speak, 1)
  EndIf
  
  Volume = 100
  
  If OpenPreferences("settings.ini")
    Rate = ReadPreferenceInteger("Rate", 0)
    ClosePreferences()
  Else
    Rate = 0
    CreatePreferences("settings.ini")
    WritePreferenceInteger("Rate", Rate)
    ClosePreferences()
  EndIf
  
  
  SetGadgetState(#Volume, Volume)
  SetGadgetState(#Rate, Rate + 10)
  
  SetGadgetText(#TextVolume, "Volume = " + Str(Volume) + "%")
  SetGadgetText(#TextRate, "Vitesse = " + Str(Rate) + "    ( -10 à + 10 ), Vitesse normale = 0")
  
  
  ClearClipboard()
  
  SetActiveWindow(#Window)
  LastHandle = WindowID(#Window)
 
  Repeat
    
    ; Obtenir Handle de la fenêtre externe ayant le focus
    Handle = GetForegroundWindow_()
    If Handle <> WindowID(#Window) ; Et si ce n'est pas celle du player,
      If Handle > 0 ; Si une fenêtre est sélectionnée,
        LastHandle = Handle ; On mémorise son Handle
      EndIf
    EndIf

    ; Saisie des événements
    Event = WaitWindowEvent(20)
                
    Select Event
        
        Case #PB_Event_Menu
          EventMenu = EventMenu() 
          Select EventMenu
            Case #DebutIndiceVoix To (#DebutIndiceVoix + NbVoice-1)
              For a = #DebutIndiceVoix To (#DebutIndiceVoix + NbVoice-1)
                SetMenuItemState(#Menu, a, #False)
              Next 
              SetMenuItemState(#Menu, EventMenu, #True)
              VoiceSapi1 = EventMenu - #DebutIndiceVoix
              SetGadgetText(#Voix, "Voix sélectionnée : " + VoicesSapi1(VoiceSapi1)\Description)
              SetGadgetText(#LangID, "LangID : " + VoicesSapi1(VoiceSapi1)\LangID)
            
              
            Case #DebutIndiceAudio To (#DebutIndiceAudio + CountAudioOutput-1)
              For a = #DebutIndiceAudio To (#DebutIndiceAudio + CountAudioOutput-1)
                SetMenuItemState(#Menu, a, #False)
              Next  
              SetMenuItemState(#Menu, EventMenu, #True)
              SetAudioOutput(VoicesSapi1(), EventMenu - #DebutIndiceAudio)
          EndSelect
          
        Case  #PB_Event_Timer
          Select EventTimer() 
            Case #TimerDetectSpeakfinish
              ; En pause, le StatusSpeak est 0, en fait il n'y a pas d'état
              ; En cours il est de 2
              ; Prêt il est de 1
              If StatusSpeak(VoicesSapi1()) = 1
                RemoveWindowTimer(#Window, #TimerDetectSpeakfinish)
                DisableGadget(#Speak, 0)
                DisableGadget(#Resume, 1)
                DisableGadget(#Pause, 1)
                DisableGadget(#Stop, 1)
              EndIf
 
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
            Case #Volume
              Volume = GetGadgetState(#Volume)
              SetGadgetText(#TextVolume, "Volume = " + Str(Volume) + "%")
              
            Case #Rate
              Rate = GetGadgetState(#Rate) - 10
              SetGadgetText(#TextRate, "Vitesse = " + Str(Rate) + "    ( -10 à + 10 ), Vitesse normale = 0")
              
            Case #Speak
              
              selectedText$ = " "
              
              ; Récupérer le texte éventuel dans la dernère fenêtre ayant eu le focus
              If LastHandle <> WindowID(#Window)
                SendControlC(LastHandle, #Window) ; on en fait un Control-C
                selectedText$ = GetClipboardText()
                ClearClipboard()
                selectedText$ = ReplaceString(selectedText$, Chr(13)+Chr(10), " ")
                selectedText$ = ReplaceString(selectedText$, Chr(13), " ")
                selectedText$ = ReplaceString(selectedText$, Chr(10), " ")
                If Trim(selectedText$) = "" : selectedText$ = " " : EndIf
              EndIf   
                            
              If PlaySpeakAsync(VoicesSapi1(), VoiceSapi1, selectedText$, @Stream, Rate, Volume) > 0
                ; Timer de détection de Fin de Lecture du Texte
                AddWindowTimer(#Window, #TimerDetectSpeakfinish, 500)
                DisableGadget(#Speak, 1)
                DisableGadget(#Pause, 0)
                DisableGadget(#Stop, 0)
              Else
                MessageRequester("Info", "La fonction Speak a échoué sur la voix sélectionnée !")
              EndIf
              
            Case #Pause
              DisableGadget(#Resume, 0)
              DisableGadget(#Pause, 1)
              DisableGadget(#Speak, 1)
              PauseSpeak(VoicesSapi1())
              
            Case #Resume
              DisableGadget(#Resume, 1)
              DisableGadget(#Pause, 0)
              ResumeSpeak(VoicesSapi1())
              
            Case #Stop
              DisableGadget(#Speak, 0)
              DisableGadget(#Resume, 1)
              DisableGadget(#Pause, 1)
              DisableGadget(#Stop, 1)
              StopSpeak(VoicesSapi1())
              
          EndSelect
          
        Case #PB_Event_CloseWindow
          Quit = 1
                
      EndSelect 
      
      Delay(1)
      
    Until Quit = 1
    
    RemoveWindowTimer(#Window, #TimerDetectSpeakfinish)
    
EndIf



; Sauvegarde les préférences
CreatePreferences("settings.ini")
WritePreferenceInteger("Rate", Rate)
ClosePreferences()
  
; Libérer les ressources avant de quitter
; N'as pas d'effet si déjà  libéré
FreeSpeakVoice(VoicesSapi1())
; invoquer CoUninitialize_() qu'une seule fois dans le programme
CoUninitialize_()
End 

;;
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Re: PureSapi, jouer un texte sélectionné

Message par nico »

Bonjour, cela fonctionne bien en compilation x86. :)

Mais le cœur du code, que j'ai conçu à partir d'autres échantillons que j'ai pu trouver ici et là, nécessiterais une petite refonte, notamment sur les appels COM qui récupèrent un pointeur sur une autre interface. Car même si l'appel réussit, cela ne signifie pas pour autant que le pointeur reçu est valide, c'est pourquoi le programme plante en x64.

Par exemple au lieu de faire ceci:

Code : Tout sélectionner

  If  CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechVoice) >= #S_OK   
    
    If SpeechVoice\GetVoices(0, 0, @SpeechObjectTokens) >= #S_OK   ; peut planter si  SpeechVoice=0
Il faudrait faire cela:

Code : Tout sélectionner

  If  CoCreateInstance_(Clsid, #Null, #CLSCTX_INPROC_SERVER, Refiid, @SpeechVoice) >= #S_OK   
    
    If SpeechVoice And SpeechVoice\GetVoices(0, 0, @SpeechObjectTokens) >= #S_OK  ; ainsi on évite le plantage

Il faudrait aussi compléter en rajoutant des boites de messages d'erreurs pour informer de l'erreur et plutôt que de laisser le programme décider du choix entre Sapi 5 et Speech 11, on pourrait laisser à l'utilisateur le choix de sélection au niveau de l'interface. A l'époque, je l'avais fais ainsi car suivant l'OS que l'on avait, on avait forcément l'un ou l'autre, soit le dernier en date.

Voilà pour la petite explication.
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: PureSapi, jouer un texte sélectionné

Message par Kwai chang caine »

Marche nickel W10 x64 / v5.62 x86 :wink:
Ce genre de programme m'épate tout le temps 8O
Et la voix française parle presque aussi bien qu'une vraie personne "bien française", c'est ébouriffant 8O
On est loin des anciennes voix, ou on était mort de rire en entendant l'accent "Métalamericain", mais qui déjà à l'époque nous bluffait....
Merci de ce partage 8)
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Re: PureSapi, jouer un texte sélectionné

Message par nico »

Salut KCC,

Oui, les voix sont excellentes et la lecture fonctionne vraiment bien, la reconnaissance vocale est encore plus impressionnante et plus compliquée encore. :)
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: PureSapi, jouer un texte sélectionné

Message par Kwai chang caine »

Jsuis en admiration devant ces fonctions.
Ca fait tellement vrai. qu'on va bientôt pouvoir de passer de "machine" qui fait le même son et qu'on écoute plus depuis longtemps...
Bref un bon lave vaisselle, lave linge, allo pizza...une machine qui cause aussi bien voir mieux, et surtout de sujets intéressants...
Pour le reste les japonais ont fait le nécessaire plus vraie que nature pour seulement 5000 euros
Crois moi...la paix et le réel bonheur du geek n'est plus vraiment loin ...... :mrgreen:
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Répondre