Page 1 of 1

[function] Easy Text to speech

Posted: 26 Dec 2015, 14:56
by Learning one
Continued from old [function] Easy Text to speech thread.

Re: [function] Easy Text to speech

Posted: 24 Jul 2016, 09:05
by evilC
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
		}
	}
}

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 03:12
by guest8
Help.
Can`t write audioOut to file.
Without classes no such problem.
Can`t dig it why. newbie.

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 07:30
by evilC
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
	}

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 07:37
by evilC
Hmm, actually, this line is wrong:
this.TTS("SpeakWait", param1) should be this.SpeakWait(param1)

But that does not seem to fix it.

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 09:56
by guest8
evilC
wow! Its working now!
Thank you so much, evilC.

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 11:32
by evilC
Oh really? Didn't seem to work for me, but happy days. I will update the OP.

Re: [function] Easy Text to speech

Posted: 18 Jun 2017, 12:08
by guest8
With this apart works for me:
Spoiler

Re: [function] Easy Text to speech

Posted: 10 Aug 2018, 17:11
by AHKStudent
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

Re: [function] Easy Text to speech

Posted: 11 Aug 2018, 10:53
by evilC
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

Re: [function] Easy Text to speech

Posted: 11 Aug 2018, 17:17
by AHKStudent
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