[function] Easy Text to speech

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
Learning one
Posts: 173
Joined: 04 Oct 2013, 13:59
Location: Croatia
Contact:

[function] Easy Text to speech

26 Dec 2015, 14:56

Continued from old [function] Easy Text to speech thread.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [function] Easy Text to speech

24 Jul 2016, 09:05

Thanks Learning One for this, I needed something for a project of mine and this saved me having to work it all out for myself.

I took your code and re-packaged it as a class - I include it here in case it is useful to anyone else.
Changes:
Implemented as a class - all "commands" that you passed to TTS are now methods (functions) in the new class.
You no longer pass the oVoice object around. When you create a TTS class, it creates a voice object and stores it as a property of the class. All commands change that one voice.
GetVoices now returns an indexed array.
NextVoice added to switch to the next voice.

The code is largely unaltered in essence. Things like the available voices are calculated on startup and stored as class properties, so the code does not need to use COM to determine if a valid voice name is passed to SetVoice for example.

Code: Select all

#SingleInstance force
s := new TTS()
return

F1::s.Speak(gst())			; select some text and press F1 to test Speak()
F2::s.ToggleSpeak(gst())	; select some text and press F2 to test ToggleSpeak()
F3::s.NextVoice()			; select some text and press F3 to switch to the next voice

gst() {   ; GetSelectedText by Learning one 
	IsClipEmpty := (Clipboard = "") ? 1 : 0
	if !IsClipEmpty {
		ClipboardBackup := ClipboardAll
		While !(Clipboard = "") {
			Clipboard =
			Sleep, 10
		}
	}
	Send, ^c
	ClipWait, 0.1
	ToReturn := Clipboard, Clipboard := ClipboardBackup
	if !IsClipEmpty
	ClipWait, 0.5, 1
	Return ToReturn
}

; Class TTS by evilC
; Based on code by Learning one. For AHK_L. Thanks: jballi, Sean, Frankie.
; AHK forum location:	www.autohotkey.com/forum/topic57773.html
; Read more:			msdn.microsoft.com/en-us/library/ms723602(v=VS.85).aspx, www.autohotkey.com/forum/topic45471.html, www.autohotkey.com/forum/topic83162.html
Class TTS {
	VoiceList := []		; An indexed array of the available voice names
	VoiceAssoc := {}	; An Associative array of voice names, key = voice name, value = voice index (VoiceList lookup)
	VoiceCount := 0		; The number of voices available
	VoiceNumber := 0	; The number of the current voice
	VoiceName := ""		; The name of the current voice
	
	__New(){
		this.oVoice := ComObjCreate("SAPI.SpVoice")
		this._GetVoices()
		this.SetVoice(this.VoiceList.1)
	}

	; speak or stop speaking
	ToggleSpeak(text){
		Status := this.oVoice.Status.RunningState
		if Status = 1	; finished
		this.oVoice.Speak(text,0x1)	; speak asynchronously
		Else if Status = 0	; paused
		{
			this.oVoice.Resume
			this.oVoice.Speak("",0x1|0x2)	; stop
			this.oVoice.Speak(text,0x1)	; speak asynchronously
		}
		Else if Status = 2	; reading
		this.oVoice.Speak("",0x1|0x2)	; stop
	}

	; speak asynchronously
	Speak(text){
		Status := this.oVoice.Status.RunningState
		if Status = 0	; paused
		this.oVoice.Resume
		this.oVoice.Speak("",0x1|0x2)	; stop
		this.oVoice.Speak(text,0x1)	; speak asynchronously
	}
	
	; speak synchronously
	SpeakWait(text){
		Status := this.oVoice.Status.RunningState
		if Status = 0	; paused
		this.oVoice.Resume
		this.oVoice.Speak("",0x1|0x2)	; stop
		this.oVoice.Speak(text,0x0)	; speak synchronously
	}
	
	; Pause toggle
	Pause(){
		Status := this.oVoice.Status.RunningState
		if Status = 0	; paused
		this.oVoice.Resume
		else if Status = 2	; reading
		this.oVoice.Pause
	}
	
	Stop(){
		Status := this.oVoice.Status.RunningState
		if Status = 0	; paused
		this.oVoice.Resume
		this.oVoice.Speak("",0x1|0x2)	; stop
	}
	
	; rate (reading speed): rate from -10 to 10. 0 is default.
	SetRate(rate){
		this.oVoice.Rate := rate
	}
	
	; volume (reading loudness): vol from 0 to 100. 100 is default
	SetVolume(vol){
		this.oVoice.Volume := vol
	}
	
	; pitch : From -10 to 10. 0 is default.
	; http://msdn.microsoft.com/en-us/library/ms717077(v=vs.85).aspx
	SetPitch(pitch){
		this.oVoice.Speak("<pitch absmiddle = '" pitch "'/>",0x20)
	}

	; Set voice by name
	SetVoice(VoiceName){
		if (!ObjHasKey(this.VoiceAssoc, VoiceName))
			return 0
		While !(this.oVoice.Status.RunningState = 1)
		Sleep, 20
		this.oVoice.Voice := this.oVoice.GetVoices("Name=" VoiceName).Item(0) ; set voice to param1
		this.VoiceName := VoiceName
		this.VoiceNumber := this.VoiceAssoc[VoiceName]
		return 1
	}

	; Set voice by index
	SetVoiceByIndex(VoiceIndex){
		return this.SetVoice(this.VoiceList[VoiceIndex])
	}

	; Use the next voice. Loops around at end
	NextVoice(){
		v := this.VoiceNumber + 1
		if (v > this.VoiceCount)
			v := 1
		return this.SetVoiceByIndex(v)
	}
	
	; Returns an array of voice names
	GetVoices(){
		return this.VoiceList
	}

	GetStatus(){
		Status := this.oVoice.Status.RunningState
		if Status = 0 ; paused
		Return "paused"
		Else if Status = 1 ; finished
		Return "finished"
		Else if Status = 2 ; reading
		Return "reading"
	}
	
	GetCount(){
		return this.VoiceCount
	}
	
	SpeakToFile(param1, param2){
		oldAOS := this.oVoice.AudioOutputStream
		oldAAOFCONS := this.oVoice.AllowAudioOutputFormatChangesOnNextSet
		this.oVoice.AllowAudioOutputFormatChangesOnNextSet := 1	
		
		SpStream := ComObjCreate("SAPI.SpFileStream")
		FileDelete, % param2	; OutputFilePath
		SpStream.Open(param2, 3)
		this.oVoice.AudioOutputStream := SpStream
		this.SpeakWait(param1)
		SpStream.Close()
		this.oVoice.AudioOutputStream := oldAOS
		this.oVoice.AllowAudioOutputFormatChangesOnNextSet := oldAAOFCONS
	}

	; ====== Private funcs, not intended to be called by user =======
	_GetVoices(){
		this.VoiceList := []
		this.VoiceAssoc := {}
		this.VoiceCount := this.oVoice.GetVoices.Count
		Loop, % this.VoiceCount
		{
			Name := this.oVoice.GetVoices.Item(A_Index-1).GetAttribute("Name")	; 0 based
			this.VoiceList.push(Name)
			this.VoiceAssoc[Name] := A_Index
		}
	}
}
Last edited by evilC on 18 Jun 2017, 11:32, edited 1 time in total.
guest8

Re: [function] Easy Text to speech

18 Jun 2017, 03:12

Help.
Can`t write audioOut to file.
Without classes no such problem.
Can`t dig it why. newbie.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [function] Easy Text to speech

18 Jun 2017, 07:30

I tried it myself, and yeah there appears to be something wrong. It writes a file, but it does not appear to be valid.
I cannot see what is wrong though, I see no obvious mistakes in my port of that method.

Old code:

Code: Select all

	Else if command = SpeakToFile	; param1 = TextToSpeak,    param2 = OutputFilePath
	{
		oldAOS := oVoice.AudioOutputStream
		oldAAOFCONS := oVoice.AllowAudioOutputFormatChangesOnNextSet
		oVoice.AllowAudioOutputFormatChangesOnNextSet := 1	
		
		SpStream := ComObjCreate("SAPI.SpFileStream")
		FileDelete, % param2	; OutputFilePath
		SpStream.Open(param2, 3)
		oVoice.AudioOutputStream := SpStream
		TTS(oVoice, "SpeakWait", param1)
		SpStream.Close()
		oVoice.AudioOutputStream := oldAOS
		oVoice.AllowAudioOutputFormatChangesOnNextSet := oldAAOFCONS
	}
New code - should be identical, but the oVoice variable is this.oVoice and the syntax of the TTS method changed.

Code: Select all

	SpeakToFile(param1, param2){
		oldAOS := this.oVoice.AudioOutputStream
		oldAAOFCONS := this.oVoice.AllowAudioOutputFormatChangesOnNextSet
		this.oVoice.AllowAudioOutputFormatChangesOnNextSet := 1	
		
		SpStream := ComObjCreate("SAPI.SpFileStream")
		FileDelete, % param2	; OutputFilePath
		SpStream.Open(param2, 3)
		this.oVoice.AudioOutputStream := SpStream
		this.TTS("SpeakWait", param1)
		SpStream.Close()
		this.oVoice.AudioOutputStream := oldAOS
		this.oVoice.AllowAudioOutputFormatChangesOnNextSet := oldAAOFCONS
	}
guest8

Re: [function] Easy Text to speech

18 Jun 2017, 09:56

evilC
wow! Its working now!
Thank you so much, evilC.
guest8

Re: [function] Easy Text to speech

18 Jun 2017, 12:08

With this apart works for me:
Spoiler
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: [function] Easy Text to speech

10 Aug 2018, 17:11

evilC wrote:Oh really? Didn't seem to work for me, but happy days. I will update the OP.
Hi evilC

Would you know how I can get a list of available voices and their index number on a machine?

I saw the class above but it is over my head
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: [function] Easy Text to speech

11 Aug 2018, 17:17

evilC wrote:s.GetVoiceList() returns an indexed array

Code: Select all

s := new TTS()
vl := s.GetVoices()
Loop % vl.Length() {
	voices .= A_Index ": " s.VoiceList[A_Index] "`n"
}
msgbox % voices
:thumbup: :thumbup: :thumbup: thank you

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: furqan and 78 guests