Page 1 of 1

how can i use text-to-speech in windows ?

Posted: Wed Apr 05, 2017 5:46 am
by skinkairewalker
hi everyone , have a way to i use windows voices native in my Pb application ? like text-to-speech ?

Re: how can i use text-to-speech in windows ?

Posted: Wed Apr 05, 2017 9:04 am
by ar-s
From Nico : Pb 5.44 LTS x86 : http://www.purebasic.fr/french/viewtopi ... ese+vocale
You may need the windows 'runtime : https://www.microsoft.com/en-us/downloa ... x?id=10121

Code: Select all

#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)
          Debug DefaultID
          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)
                  Debug DescriptionAudio
                  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)
                    Debug ID
                    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
     Debug "SpeechVoice " + SpeechVoice
      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
    Debug "Free InterfaceVoice -> OK"
    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)
      Debug "RunningState = " + Str(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
      Debug "Stop -> OK"
      ProcedureReturn #True
    EndIf
  EndIf
 
  ProcedureReturn #False
  ; Renvoi vrai ou faux
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, 500, "PureBasic - SAPI 5 / SPEECH 11 - Asynchrone")
 
  TextGadget(#Indication, 10, 10, 380, 40, "Le choix de la voix ne modifie pas celle par défaut dans le panneau de configuration!")
  TextGadget(#Voix, 10, 50, 380, 40, "Voix sélectionnée :")
  TextGadget(#LangID, 10, 80, 380, 20, "LandID :")
 
  SetGadgetColor(#Voix, #PB_Gadget_FrontColor, RGB(255,0,0))
  SetGadgetColor(#LangID, #PB_Gadget_FrontColor, RGB(255,0,0))
 
  ; // 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)
     
      Debug "------------------------------"
      Debug "Nombre de Voix = " + Str(NbVoice)
     
      For a = 0 To NbVoice -1
        MenuItem( #DebutIndiceVoix + a, VoicesSapi1(a)\Description)
        Debug "------------------------------"
        Debug "Description = " + VoicesSapi1(a)\Description
        Debug "LangID = " + VoicesSapi1(a)\LangID
        Debug "------------------------------"
      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, "LandID : " + 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
 
  EditorGadget(#Edit, 10, 100, 380, 40)
  SetGadgetText(#Edit, "Purebasic is very nice! Purebasic est génial! ")
 
  TextGadget(#Precision, 10, 220, 380, 40, "Précision : le % du volume est en rapport avec le volume réglé actuellement, permet seulement de le baisser par rapport au niveau actuel!")
  TextGadget(#TextVolume, 10, 260, 380, 20, "Volume")
  TrackBarGadget(#Volume, 10,  280, 380, 20, 0, 100)
 
  TextGadget(#TextRate, 10, 160, 380, 20, "Vitesse")
  TrackBarGadget(#Rate, 10,  180, 380, 20, 0, 20)
 
  ButtonGadget(#Speak, 20, 320, 80, 30, "Speak")
  ButtonGadget(#Pause, 110, 320, 80, 30, "Pause")
  ButtonGadget(#Resume, 200, 320, 80, 30, "Resume")
  ButtonGadget(#Stop, 290, 320, 80, 30, "Stop")
 
  ButtonGadget(#SaveToWav, 20, 370, 80, 30, "Save to Wav")
 
  DisableGadget(#Resume, 1)
  DisableGadget(#Pause, 1)
  DisableGadget(#Stop, 1)
 
  If NbVoice < 1
    DisableGadget(#Speak, 1)
  EndIf
 
  Volume = 100
  Rate = 0
  SetGadgetState(#Volume, Volume)
  SetGadgetState(#Rate, Rate + 10)
 
  SetGadgetText(#TextVolume, "Volume = " + Str(Volume) + "%")
  SetGadgetText(#TextRate, "Vitesse = " + Str(Rate) + "    ( -10 à + 10 ), Vitesse normale = 0")
 
  Repeat
    Select WaitWindowEvent()
       
      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, "LandID : " + 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)
            Debug "/////////////////////////"
            Debug EventMenu - #DebutIndiceAudio
            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
            Debug InputSentenceLength(VoicesSapi1())
            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 #SaveToWav
            Texte.s = GetGadgetText(#Edit)
            File$ = SaveFileRequester("Enregistrer le fichier sous...", "c:\", "*.wav", 0)
            If File$ <> ""
              If LCase(Right(File$, 3)) <> "wav"
                File$ = File$ + ".wav"
              EndIf
              Debug File$
              If SaveTextToWav(VoicesSapi1(), VoiceSapi1, Texte, File$, #SPSF_22kHz16BitMono)
                MessageRequester("Info", "Texte To Wav Réussie!")
              EndIf
            EndIf
           
           
          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
            Texte.s = GetGadgetText(#Edit)
           
            If PlaySpeakAsync( VoicesSapi1(), VoiceSapi1, Texte, @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
  Until Quit = 1
EndIf

; 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 

Re: how can i use text-to-speech in windows ?

Posted: Wed Apr 05, 2017 9:17 am
by ar-s

Re: how can i use text-to-speech in windows ?

Posted: Thu Apr 06, 2017 3:41 am
by skinkairewalker
ar-s wrote:You can also try Dobro Lib :
http://forums.purebasic.com/english/vie ... 55518be52c
thanks you very much :)

Re: how can i use text-to-speech in windows ?

Posted: Thu Apr 06, 2017 3:43 am
by skinkairewalker
i am using : http://espeak.sourceforge.net/ to try create Text-To-Speech

here my espeak code :

Code: Select all


Define.i espeakCHARS_AUTO = 0
Define.i espeakCHARS_UTF8 = 1
Define.i espeakCHARS_8BIT = 2
Define.i espeakCHARS_WCHAR = 3
Define.i espeakCHARS_16BIT = 4

Define.i EE_OK = 0
Define.i EE_INTERNAL_ERROR = -1
Define.i EE_BUFFER_FULL = 1
Define.i EE_NOT_FOUND = 2

Prototype.i Proto_espeak_Initialize(audio_output.i,buflength.i,path.p-utf8,options.i)
Prototype.i Proto_espeak_Synth(text.p-utf8,textLen.i,position.i,position_type.i,end_position.i,flags.i,unique_identifier.i,user_data.i)

If ( OpenLibrary(0,"espeak_sapi.dll") <> 0)
    
  espeak_Initialize.Proto_espeak_Initialize = GetFunction(0,"espeak_Initialize")
  espeak_Synth.Proto_espeak_Synth = GetFunction(0,"espeak_Synth")
  
  Debug espeak_Initialize(4,0,GetCurrentDirectory(),0)
   ; Debug espeak_Synth("Hello World",StringByteLength("Hello World",#PB_UTF8),0,0,0,espeakCHARS_AUTO,#Null,#Null)
  ; espeak_Synth have "error : invalid memory acess ( error adress 0 ) ... how can i fix it ?
EndIf  

how can i fix it code to works ?

Re: how can i use text-to-speech in windows ?

Posted: Fri Sep 11, 2020 9:30 pm
by Michael Vogel
ar-s wrote:From Nico : Pb 5.44 LTS x86 : http://www.purebasic.fr/french/viewtopi ... ese+vocale
You may need the windows 'runtime : https://www.microsoft.com/en-us/downloa ... x?id=10121

Code: Select all

[...]
Procedure.l InitSpeakVoice(Array VoicesSapi.AttributeVoice(1), NameInterface.l)
[...] 
  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   
      ; invoquer CoUninitialize_() qu'une seule fois dans le programme
     [...]
I like that one, but when compiling for 64 bit, the code crashes at 'If SpeechVoice\GetVoices(0, 0, @SpeechObjectTokens) >= #S_OK'

When forcing the NameInterface=1, everything works fine.

Re: how can i use text-to-speech in windows ?

Posted: Fri Sep 11, 2020 11:58 pm
by BarryG
I use this by RSBasic -> viewtopic.php?f=27&t=70342

Re: how can i use text-to-speech in windows ?

Posted: Sat Dec 26, 2020 6:33 pm
by MBall
Hi, I just tried the first example posted here.
I do get speech output. but I can't seem to set the output audio device. which ever audio device i select in the example. it still sends the speech output to the default windows output device.

any help please.

Martin