Skip to content

Commit

Permalink
Aligning the onecore code with the NVDA source onecore code
Browse files Browse the repository at this point in the history
  • Loading branch information
tsengwoody committed Mar 10, 2024
1 parent a50eb3a commit a4bf49d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 17 deletions.
44 changes: 29 additions & 15 deletions addon/synthDrivers/WorldVoice/voice/_onecore.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#: The number of 100-nanosecond units in 1 second.
HUNDRED_NS_PER_SEC = 10000000 # 1000000000 ns per sec / 100 ns
WAVE_HEADER_LENGTH = 46
ocSpeech_Callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p)

class _OcSsmlConverter(speechXml.SsmlConverter):
Expand Down Expand Up @@ -180,9 +181,9 @@ def __init__(self, lock):
# Set initial values for parameters that can't be queried when prosody is not supported.
# This initialises our cache for the value.
# When prosody is supported, the values are used for cachign reasons.
self._rate = 50
self._pitch = 50
self._volume = 100
self._rate: int = 50
self._pitch: int = 50
self._volume: int = 100

if self.supportsProsodyOptions:
self._dll.ocSpeech_getPitch.restype = ctypes.c_double
Expand Down Expand Up @@ -229,7 +230,7 @@ def _maybeInitPlayer(self, wav):
def terminate(self):
# prevent any pending callbacks from interacting further with the synth.
self._earlyExitCB = True
# super().terminate()
super().terminate()
# Terminate the synth, the callback function should no longer be called after this returns.
self._dll.ocSpeech_terminate(self._ocSpeechToken)
# Drop the ctypes function instance for the callback and handle,
Expand Down Expand Up @@ -293,22 +294,26 @@ def pitch(self, pitch):
return
rawPitch = self._percentToParam(pitch, self.MIN_PITCH, self.MAX_PITCH)
self._queuedSpeech.append((self._dll.ocSpeech_setPitch, rawPitch))

# must process to apply pitch change
self._processQueue()

@property
def volume(self):
def volume(self) -> int:
if not self.supportsProsodyOptions:
return self._volume
rawVolume = self._dll.ocSpeech_getVolume(self._ocSpeechToken)
return int(rawVolume * 100)

@volume.setter
def volume(self, volume):
def volume(self, volume: int):
self._volume = volume
if not self.supportsProsodyOptions:
return
rawVolume = volume / 100.0
self._queuedSpeech.append((self._dll.ocSpeech_setVolume, rawVolume))

# must process to apply volume change
self._processQueue()

@property
Expand All @@ -327,6 +332,8 @@ def rate(self, rate):
maxRate = self.BOOSTED_MAX_RATE if self._rateBoost else self.DEFAULT_MAX_RATE
rawRate = self._percentToParam(rate, self.MIN_RATE, maxRate)
self._queuedSpeech.append((self._dll.ocSpeech_setRate, rawRate))

# must process to apply rate change
self._processQueue()

_rateBoost = False
Expand Down Expand Up @@ -419,10 +426,11 @@ def _callback(self, bytes, len, markers):
else:
self._consecutiveSpeechFailures = 0
# This gets called in a background thread.
stream = io.BytesIO(ctypes.string_at(bytes, len))
stream = io.BytesIO(ctypes.string_at(bytes, WAVE_HEADER_LENGTH))
wav = wave.open(stream, "r")
self._maybeInitPlayer(wav)
data = wav.readframes(wav.getnframes())
data = bytes + WAVE_HEADER_LENGTH
dataLen = wav.getnframes() * wav.getnchannels() * wav.getsampwidth()
if markers:
markers = markers.split('|')
else:
Expand All @@ -441,14 +449,17 @@ def _callback(self, bytes, len, markers):
# Order the equation so we don't have to do floating point.
pos = pos * self._bytesPerSec // HUNDRED_NS_PER_SEC
# Push audio up to this marker.
self._player.feed(data[prevPos:pos],
onDone=lambda index=index: synthIndexReached.notify(synth=getSynth(), index=index))
self._player.feed(
ctypes.c_void_p(data + prevPos),
size=pos - prevPos,
onDone=lambda index=index: synthIndexReached.notify(synth=getSynth(), index=index)
)
prevPos = pos
if self._wasCancelled:
if isDebugForSynthDriver():
log.debug("Cancelled, stopped pushing audio")
else:
self._player.feed(data[prevPos:])
self._player.feed(ctypes.c_void_p(data + prevPos), size=dataLen - prevPos)
if isDebugForSynthDriver():
log.debug("Done pushing audio")
self._processQueue()
Expand All @@ -462,8 +473,6 @@ def _getVoiceInfoFromOnecoreVoiceString(self, voiceStr):
language=language.replace('-','_')
return [ID,language,name]



@property
def availableVoices(self):
result = []
Expand Down Expand Up @@ -565,7 +574,6 @@ def voice(self, id):
voicesStr = self._dll.ocSpeech_getVoices(self._ocSpeechToken).split('|')
for index,voiceStr in enumerate(voicesStr):
ID, language, name = voiceStr.split(':')
language=language.replace('-','_')
if id == ID:
self._dll.ocSpeech_setVoice(self._ocSpeechToken, index)
return
Expand All @@ -574,7 +582,13 @@ def voice(self, id):
def _getDefaultVoice(self, pickAny: bool = True) -> str:
voicesStr = self._dll.ocSpeech_getVoices(self._ocSpeechToken).split('|')
for index,voiceStr in enumerate(voicesStr):
ID, language, name = voiceStr.split(':')
try:
print("YAA")
print(voiceStr)
ID, language, name = voiceStr.split(':')
except:
print(voiceStr)
raise VoiceUnsupportedError("No voices available that match the user language")
return ID
raise VoiceUnsupportedError("No voices available that match the user language")

Expand Down
4 changes: 2 additions & 2 deletions buildVars.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def _(arg):
# Documentation file name
"addon_docFileName": "readme.html",
# Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional)
"addon_minimumNVDAVersion" : "2022.1",
"addon_minimumNVDAVersion" : "2023.3",
# Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version)
"addon_lastTestedNVDAVersion" : "2023.1",
"addon_lastTestedNVDAVersion" : "2024.1",
# Add-on update channel (default is None, denoting stable releases,
# and for development releases, use "dev".)
# Do not change unless you know what you are doing!
Expand Down

0 comments on commit a4bf49d

Please sign in to comment.