I'm new to Nim but want to use it instead of Python because it can be compiled into much faster and smaller code.
I'm having issues with using Windows Speech Api in Nim. The winim com sample works, but I need access to the list of installed voices and be able to set the voice, speech rate and volume. I got a VariantConversionError on VT_DISPATCH when trying to get the available voices. So it's a variable type issue, but can't find any mention in the docs regarding VT_DISPATCH.
Here is the code (based on the Winim example (c) Copyright 2016-2021 Ward)
import winim/com
var obj = CreateObject("SAPI.SpVoice")
obj.speak "Nim is a statically typed, imperative programming language."
# above works flawless, the sentence is spoken
var voices = obj.GetVoices()
echo voices
I tried some conversions with
fromvariant [VT_DISPATCH] (voices), but to no avail.
Below is some of the working Python code :
def getVoices(self): voice_list = [] voices = self.speech.GetVoices() for voice in voices: voice_list.append(voice) return voice_list def getVoiceNames(self): return [voice.GetDescription() for voice in self.getVoices()] def setVoice(self, voice): self.speech.Voice = voice def setRate(self, rate): self.speech.Rate = rate def setVolume(self, volume): self.speech.Volume = volume
Any help is greatly appreciated. The rest of the project is monitoring files and generating sentences to say, I don't expect major problems there. But this speech issue needs to be solved first in order to continue.
Replying to my own message... Someone on Discord helped me. Less complicated than I tought, but one has to know how.
Should anyone else need it here is the code :
import winim/com
proc main() =
var obj = CreateObject("SAPI.SpVoice")
obj.speak "Nim is a statically typed, imperative programming language."
var vcs = obj.GetVoices()
let cnt = vcs.count()
for idx in 0 ..< cnt:
echo vcs.item(idx).id
echo vcs.item(idx).getDescription()
when isMainModule:
main()
echo("Finished")
Less complicated than I tought, but one has to know how.
this.
Not quite there yet, everything works (getting thve available voices, setting volume, rate & pitch), only selecting a voice ( obj.Voice = vcs.item(x) ) throws a COMError... For rate, volume and pitch I just need to set the sapi parameter to an int like this for example:
obj.Rate = 3
for obj.Voice, I should pass the pointer which is in vcs.item(x) (I believe). Should it be converted before passing, and if so : how ?
I tried it like this :
let voiceno = 2
echo "Testing another voice"
echo vcs.item(voiceno).getDescription()
# all of the above works
obj.Voice = vcs.item(voiceno) # throws COMError
obj.speak "Now speaking in a different voice."
Good problem, even I (hmm, the author of winim) cannot resolve it without searching on google. The key is, Voice property use set by ref instead of set. So, you must use setRef instead of set. The dot assignment operator only supports set and get, you muse call setRef by yourself.
import winim/com
var obj = CreateObject("SAPI.SpVoice")
for i in obj.GetVoices():
echo i.id
echo i.getDescription()
obj.setRef("Voice", i)
obj.speak "Nim is a statically typed, imperative programming language."
Information: https://stackoverflow.com/questions/61442080/how-to-change-the-voice-used-for-sapi-spvoice
Thank you Ward ! You're my hero of the week !
I also got speech to wav file working. Here's that code snippet:
var ttsStream = CreateObject("SAPI.SpFileStream")
ttsStream.Open("nimtts.wav", 3) # 3 is Create File (overwrites existing)
var ttswav = CreateObject("SAPI.SpVoice")
ttswav.setRef("AudioOutputStream", ttsStream)
ttswav.speak "Nim, Windows text to speech test to a wav file", 3
# wait for tts to wav to finish, but not longer than 10 seconds
# note that output to wav is much much faster than actually 'speaking' to audio device
ttswav.WaitUntilDone(10000)
ttsStream.Close()
Feel free to alter and include in your winim sample if you want.