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)
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
;;