Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[function] Easy Text to speech


  • Please log in to reply
43 replies to this topic
Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

WARNING: This is old thread. It is continued here.

 

TTS() for AutoHotkey_L - recommended
See also:
- Text-To-Speech Examples for AutoHotkey_L by jballi
- Text-To-Speech Examples for AutoHotkey Basic by jballi
- Google TTS (on German AHK forum)




_____________________
Here is an old version for AHK Basic which is not maintained any more and requires COM.ahk by Sean

TTS(Voice, Task, Value="") { ; by Learning one. Thanks: jballi and Sean
; Tasks: ToggleSpeak, Speak, Pause, Stop, SetRate, SetVolume, SetVoice, GetVoices, GetStatus
; Reference: http://www.autohotkey.com/forum/topic45471.html
if task = ToggleSpeak ; speak or stop speaking
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 1 ; finished
COM_Invoke(Voice,"Speak",Value,0x1)
Else if Status = 0 ; paused
{
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
COM_Invoke(Voice,"Speak",Value,0x1) ; value is text you want voice to speak
}
Else if Status = 2 ; reading
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
}
Else if task = Speak
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
COM_Invoke(Voice,"Speak",Value,0x1)
}
Else if task = Pause ; Pause toggle
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
else if Status = 2 ; reading
COM_Invoke(Voice,"Pause")
}
Else if task = Stop
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
}
Else if task = SetRate
COM_Invoke(Voice,"Rate",Value) ; rate (reading speed): value from -10 to 10. 0 is default.
Else if task = SetVolume
COM_Invoke(Voice,"Volume",Value) ; volume (reading loudness): value from 0 to 100. 100 is default
Else if task = SetVoice
COM_Invoke(Voice,"Voice","+" . COM_Invoke(Voice,"GetVoices(" . """" . "Name=" . Value . """" . ").Item(0)"))
Else if task = GetVoices
{
loop % COM_Invoke(Voice,"GetVoices.Count")
{
Name:=COM_Invoke(Voice,"GetVoices.Item(" . A_Index-1 . ")" . ".GetAttribute","Name")
VoiceList := (VoiceList = "") ? Name : VoiceList "|" Name
}
Return VoiceList
}
Else if task = GetStatus
{
StatusNum := COM_Invoke(Voice,"Status.RunningState")
if StatusNum = 0 ; paused
Return "paused"
Else if StatusNum = 1 ; finished
Return "finished"
Else if StatusNum = 2 ; reading
Return "reading"
}
}
Example 1 - for AHK Basic
;===Auto-execute===
OnExit, ExitSub
COM_Init(), Voice:=COM_CreateObject("SAPI.SpVoice")
Return

;===Hotkey===
1::TTS(Voice, "ToggleSpeak", gst()) ; speak selected text or stop speaking.


;===Subroutine===
ExitSub:
COM_Release(Voice), COM_Term()
ExitApp






;===Functions===========================================================================
#Include Com.ahk ; by Sean

TTS(Voice, Task, Value="") { ; by Learning one. Thanks: jballi and Sean
; Tasks: ToggleSpeak, Speak, Pause, Stop, SetRate, SetVolume, SetVoice, GetVoices, GetStatus
; Reference: http://www.autohotkey.com/forum/topic45471.html
if task = ToggleSpeak ; speak or stop speaking
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 1 ; finished
COM_Invoke(Voice,"Speak",Value,0x1)
Else if Status = 0 ; paused
{
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
COM_Invoke(Voice,"Speak",Value,0x1) ; value is text you want voice to speak
}
Else if Status = 2 ; reading
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
}
Else if task = Speak
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
COM_Invoke(Voice,"Speak",Value,0x1)
}
Else if task = Pause ; Pause toggle
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
else if Status = 2 ; reading
COM_Invoke(Voice,"Pause")
}
Else if task = Stop
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
}
Else if task = SetRate
COM_Invoke(Voice,"Rate",Value) ; rate (reading speed): value from -10 to 10. 0 is default.
Else if task = SetVolume
COM_Invoke(Voice,"Volume",Value) ; volume (reading loudness): value from 0 to 100. 100 is default
Else if task = SetVoice
COM_Invoke(Voice,"Voice","+" . COM_Invoke(Voice,"GetVoices(" . """" . "Name=" . Value . """" . ").Item(0)"))
Else if task = GetVoices
{
loop % COM_Invoke(Voice,"GetVoices.Count")
{
Name:=COM_Invoke(Voice,"GetVoices.Item(" . A_Index-1 . ")" . ".GetAttribute","Name")
VoiceList := (VoiceList = "") ? Name : VoiceList "|" Name
}
Return VoiceList
}
Else if task = GetStatus
{
StatusNum := COM_Invoke(Voice,"Status.RunningState")
if StatusNum = 0 ; paused
Return "paused"
Else if StatusNum = 1 ; finished
Return "finished"
Else if StatusNum = 2 ; reading
Return "reading"
}
}

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
}
Example 2 - for AHK Basic
;===Auto-execute===
OnExit, ExitSub
COM_Init(), Voice:=COM_CreateObject("SAPI.SpVoice")
TextToSpeak =
(
This is easy text to speech by Learning one. Thanks to jballi and Sean.
Each script is a plain text file containing commands to be executed by the program (AutoHotkey.exe).
A script may also contain hotkeys and hotstrings, or even consist entirely of them.
However, in the absence of hotkeys and hotstrings, a script will perform its commands sequentially from top to bottom the moment it is launched.
)
Return


;===Hotkeys===
1::TTS(Voice, "Speak", TextToSpeak)
2::TTS(Voice, "Pause")
3::TTS(Voice, "Stop")
4::TTS(Voice, "SetRate", 2)
5::TTS(Voice, "SetRate", 0)
6::TTS(Voice, "SetVolume", 50)
7::TTS(Voice, "SetVolume", 100)
8::TTS(Voice, "SetVoice", "Microsoft Mary")
9::TTS(Voice, "SetVoice", "Microsoft Anna")
0::MsgBox % TTS(Voice, "GetVoices")


;===Subroutine===
ExitSub:
COM_Release(Voice), COM_Term()
ExitApp






;===Functions===========================================================================
#Include Com.ahk ; by Sean

TTS(Voice, Task, Value="") { ; by Learning one. Thanks: jballi and Sean
; Tasks: ToggleSpeak, Speak, Pause, Stop, SetRate, SetVolume, SetVoice, GetVoices, GetStatus
; Reference: http://www.autohotkey.com/forum/topic45471.html
if task = ToggleSpeak ; speak or stop speaking
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 1 ; finished
COM_Invoke(Voice,"Speak",Value,0x1)
Else if Status = 0 ; paused
{
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
COM_Invoke(Voice,"Speak",Value,0x1) ; value is text you want voice to speak
}
Else if Status = 2 ; reading
COM_Invoke(Voice,"Speak","",0x1|0x2) ; stop
}
Else if task = Speak
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
COM_Invoke(Voice,"Speak",Value,0x1)
}
Else if task = Pause ; Pause toggle
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
else if Status = 2 ; reading
COM_Invoke(Voice,"Pause")
}
Else if task = Stop
{
Status := COM_Invoke(Voice,"Status.RunningState")
if Status = 0 ; paused
COM_Invoke(Voice,"Resume")
COM_Invoke(Voice,"Speak","",0x1|0x2)
}
Else if task = SetRate
COM_Invoke(Voice,"Rate",Value) ; rate (reading speed): value from -10 to 10. 0 is default.
Else if task = SetVolume
COM_Invoke(Voice,"Volume",Value) ; volume (reading loudness): value from 0 to 100. 100 is default
Else if task = SetVoice
COM_Invoke(Voice,"Voice","+" . COM_Invoke(Voice,"GetVoices(" . """" . "Name=" . Value . """" . ").Item(0)"))
Else if task = GetVoices
{
loop % COM_Invoke(Voice,"GetVoices.Count")
{
Name:=COM_Invoke(Voice,"GetVoices.Item(" . A_Index-1 . ")" . ".GetAttribute","Name")
VoiceList := (VoiceList = "") ? Name : VoiceList "|" Name
}
Return VoiceList
}
Else if task = GetStatus
{
StatusNum := COM_Invoke(Voice,"Status.RunningState")
if StatusNum = 0 ; paused
Return "paused"
Else if StatusNum = 1 ; finished
Return "finished"
Else if StatusNum = 2 ; reading
Return "reading"
}
}

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
}


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
TTS() updated:
- two new tasks: ToggleSpeak and GetStatus
- some other changes in code

  • Guests
  • Last active:
  • Joined: --

TTS() updated:
- two new tasks: ToggleSpeak and GetStatus
- some other changes in code


I would appreciate it if you make it more user-friendly GUI for a no0b like me. I'm unable to comprehend how to actually 'use' your code?

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
Well, this is not designed to be used with GUI, but with hotkey.
Have you tried example 1? Run script, select some text, and press hotkey (number "1" in that example).
If you insist on GUI, check out Text-To-Speech via COM - Examples

clever_j
  • Members
  • 37 posts
  • Last active: Feb 05 2014 07:34 AM
  • Joined: 24 Jul 2005
Hi guys


quick questions..

1) where can i download the required COM.ahk?
2) To those who's tried this.. how is the resource load during text2speech routines? i've notice lots of text2speech apps that seem to semi-freeze a lowend system during speaking.. and can it speak multiple phrases at the same time? say i pressed 2 hotkeys w/ different phrases w/in 1 second... will it que the next word? or will it say them together as the hotkey is pressed?

Would love it if you guys can answer this for me, will save me some time trying to figure this out myself..

regards

TJ

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
You can download COM here.
Resource load during TTS() routines is low.
You can't speak multiple phrases at the same time.

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
TTS() for AutoHotkey_L
Function(s)
TTS(oVoice, command, param1="", param2="") {		; 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
	static CommandList := "ToggleSpeak,Speak,SpeakWait,Pause,Stop,SetRate,SetVolume,SetPitch,SetVoice,GetVoices,GetStatus,GetCount,SpeakToFile"
	if command not in %CommandList%
	{
		MsgBox, 16, TTS() error, "%command%" is not valid command.
		return
	}
	if command = ToggleSpeak	; speak or stop speaking
	{
		Status := oVoice.Status.RunningState
		if Status = 1	; finished
		oVoice.Speak(param1,0x1)	; speak asynchronously
		Else if Status = 0	; paused
		{
			oVoice.Resume
			oVoice.Speak("",0x1|0x2)	; stop
			oVoice.Speak(param1,0x1)	; speak asynchronously
		}
		Else if Status = 2	; reading
		oVoice.Speak("",0x1|0x2)	; stop
	}
	Else if command = Speak		; speak asynchronously
	{
		Status := oVoice.Status.RunningState
		if Status = 0	; paused
		oVoice.Resume
		oVoice.Speak("",0x1|0x2)	; stop
		oVoice.Speak(param1,0x1)	; speak asynchronously
	}
	Else if command = SpeakWait		; speak synchronously
	{
		Status := oVoice.Status.RunningState
		if Status = 0	; paused
		oVoice.Resume
		oVoice.Speak("",0x1|0x2)	; stop
		oVoice.Speak(param1,0x0)	; speak synchronously
	}
	Else if command = Pause	; Pause toggle
	{
		Status := oVoice.Status.RunningState
		if Status = 0	; paused
		oVoice.Resume
		else if Status = 2	; reading
		oVoice.Pause
	}
	Else if command = Stop
	{
		Status := oVoice.Status.RunningState
		if Status = 0	; paused
		oVoice.Resume
		oVoice.Speak("",0x1|0x2)	; stop
	}
	Else if command = SetRate
		oVoice.Rate := param1		; rate (reading speed): param1 from -10 to 10. 0 is default.
	Else if command = SetVolume
		oVoice.Volume := param1		; volume (reading loudness): param1 from 0 to 100. 100 is default
	Else if command = SetPitch				; http://msdn.microsoft.com/en-us/library/ms717077(v=vs.85).aspx
		oVoice.Speak("<pitch absmiddle = '" param1 "'/>",0x20)	; pitch : param1 from -10 to 10. 0 is default.
	Else if command = SetVoice
	{
		Loop, % oVoice.GetVoices.Count
		{
			Name := oVoice.GetVoices.Item(A_Index-1).GetAttribute("Name")	; 0 based
			If (Name = param1)
			{
				DoesVoiceExist := 1
				break
			}
		}
		if !DoesVoiceExist
		{
			MsgBox,64,, Voice "%param1%" does not exist.
			return
		}
		While !(oVoice.Status.RunningState = 1)
		Sleep, 20
		oVoice.Voice := oVoice.GetVoices("Name=" param1).Item(0) ; set voice to param1
	}
	Else if command = GetVoices
	{
		param1 := (param1 = "") ? "`n" : param1		; param1 as delimiter
		Loop, % oVoice.GetVoices.Count
		{
			Name := oVoice.GetVoices.Item(A_Index-1).GetAttribute("Name")	; 0 based
			VoiceList .= Name param1
		}
		Return RTrim(VoiceList,param1)
	}
	Else if command = GetStatus
	{
		Status := oVoice.Status.RunningState
		if Status = 0 ; paused
		Return "paused"
		Else if Status = 1 ; finished
		Return "finished"
		Else if Status = 2 ; reading
		Return "reading"
	}
	Else if command = GetCount
		return oVoice.GetVoices.Count
	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
	}
}	
 TTS_CreateVoice(VoiceName="", VoiceRate="", VoiceVolume="", VoicePitch="") {		; by Learning one. For AHK_L.
	oVoice := ComObjCreate("SAPI.SpVoice")
	if !(VoiceName = "")
		TTS(oVoice, "SetVoice", VoiceName)
	if VoiceRate between -10 and 10
		oVoice.Rate := VoiceRate		; rate (reading speed): from -10 to 10. 0 is default.
	if VoiceVolume between 0 and 100
		oVoice.Volume := VoiceVolume	; volume (reading loudness): from 0 to 100. 100 is default
	if VoicePitch between -10 and 10
		TTS(oVoice, "SetPitch", VoicePitch)	; pitch: from -10 to 10. 0 is default.
	return oVoice
}
Example 1 - speak selected text or stop speaking (if speaking in progress)
Voice := ComObjCreate("SAPI.SpVoice")
return

F1::TTS(Voice, "ToggleSpeak", gst())	; select some text and press F1


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
}
Example 2 - two girls and fat Sam. :)
AnnaVoice := TTS_CreateVoice("Microsoft Anna")
MaryVoice := TTS_CreateVoice("Microsoft Mary")
SamVoice := TTS_CreateVoice("Microsoft Sam", 3)

TTS(AnnaVoice, "SpeakWait", "Hi Mary, Anna speaking.")
TTS(MaryVoice, "SpeakWait", "Hi Anna! How are you?")
TTS(AnnaVoice, "SpeakWait", "I'm fine. Will you go shopping with me?")
TTS(MaryVoice, "SpeakWait", "Yes of course! Sam, will you go with us?")
TTS(SamVoice, "SpeakWait", "No, I'll stay home watching TV and drinking beer.")
TTS(MaryVoice, "SetRate", -3)
TTS(MaryVoice, "SpeakWait", "You lazy fat!")
ExitApp
Example 3 - speaking to file
AnnaVoice := TTS_CreateVoice("Microsoft Anna")
MaryVoice := TTS_CreateVoice("Microsoft Mary")

TTS(AnnaVoice, "SpeakToFile","My name is Anna. I was speaking to this file. Do you like my voice?", A_ScriptDir "\Anna speaking.wav")
TTS(MaryVoice, "SpeakToFile", "My name is Mary. I was speaking to this file. Do you like my voice?", A_ScriptDir "\Mary speaking.wav")
ExitApp
Example 4 - get available voices
Voice := ComObjCreate("SAPI.SpVoice")
MsgBox % TTS(Voice, "GetVoices")
ExitApp
Example 5 - easily create voice and set its basic attributes
VoiceName := "Microsoft Anna"
VoiceRate := -2
VoiceVolume := 90
VoicePitch := -10

AnnaVoice := TTS_CreateVoice(VoiceName, VoiceRate, VoiceVolume, VoicePitch)
TTS(AnnaVoice, "SpeakWait", "Strength is bolstered by heavenly faith.")
ExitApp
Example 6 - changing pitch
Voice := ComObjCreate("SAPI.SpVoice")

TTS(Voice,"SetPitch", 10)	; maximum pitch
TTS(Voice, "SpeakWait", "I'm speaking at pitch ten.")

TTS(Voice,"SetPitch", 0)	; default pitch
TTS(Voice, "SpeakWait", "I'm speaking at pitch zero.")

TTS(Voice,"SetPitch", -10)	; minimum pitch
TTS(Voice, "SpeakWait", "I'm speaking at pitch negative ten.")
ExitApp


  • Guests
  • Last active:
  • Joined: --
You have Anna, Mary & Sam on the same computer? How? What OS? I need to download more voices. Only got Sam on XP & Anna on Win7.

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
Yes, I got Anna, Mary, Sam and some other voices on the same computer. I downloaded them from internet long time ago. Don't remember exactly from where, but maybe from here:
<!-- m -->http://www.bytecool.com/voices.htm<!-- m -->
<!-- m -->http://www.microsoft... ... laylang=en<!-- m -->
Google free TTS voices and free sapi voices. If you'll find something interesting, post links here.

  • Guests
  • Last active:
  • Joined: --
Thanks for the script.

I have found that if I run a script that includes this function, it initially takes like 3000 K of memory. After the function is used the script takes 40000 K.
May this be related with the Objects leakage recently mentioned by Sean in this post?.

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
Well, I don't feel competent enough to give you 100% correct answer, but I think the answer is no. Btw, the last example - Two girls and fat Sam consumes 16.944 K on my system.

a4u
  • Guests
  • Last active:
  • Joined: --

... but I think the answer is no.

Correct. You aren't (u|e)nwrapping objects, but working entirely with Native COM. Whereas Sean's concern is quite accurate, it shoudn't be an issue if you are only using the COM Library or Native COM:
AnnaVoice := [color=red]ComObjCreate[/color]("SAPI.SpVoice")


genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Hey - here is a weird question.
Is there anyway to get the voices reading in time with a midi clock?
Or the fly timing adjustments?
I'm thinking musically.

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
TTS() for AutoHotkey_L updated:
- new command: SpeakToFile. Credits: Frankie
- new example

Announcement: TTS() for AHK Basic is not maintained any more


@genmce: Yes. Just tell the voice when to speak. MIDI message --> oVoice.Speak(TextToSpeak,0x1)

guest(genmce)
  • Guests
  • Last active:
  • Joined: --
Hey Learning One.

Ok so a midi note could trigger the voice to start talking...

Looks like "set rate" is the speed of speech. I see values
; rate (reading speed): param1 from -10 to 10. 0 is default.
What affects the actual timing of the speech?
I am after words being spoken in time with a midi clock.
Example:
My dog has fleas. (or a paragraph)
The paragraph - triggered from a single midi note or cc.
Each word spoken on the beat/in time with the midi clock.

Also - I don't see a pitch adjustment (perhaps I missed it), is that possible?

Thank you.