2024-12-08 06:43:22 -05:00
|
|
|
#!/bin/python
|
2018-03-21 11:10:28 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Fenrir TTY screen reader
|
2024-12-08 06:43:22 -05:00
|
|
|
# By Chrys, Storm Dragon, and contributors.
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
from fenrirscreenreader.core import debug
|
|
|
|
from fenrirscreenreader.utils import line_utils
|
|
|
|
import string, time, re
|
|
|
|
|
|
|
|
class outputManager():
|
|
|
|
def __init__(self):
|
|
|
|
self.lastEcho = ''
|
2024-12-08 06:43:22 -05:00
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
def initialize(self, environment):
|
|
|
|
self.env = environment
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['settingsManager'].loadDriver(
|
|
|
|
self.env['runtime']['settingsManager'].getSetting('speech', 'driver'), 'speechDriver')
|
|
|
|
self.env['runtime']['settingsManager'].loadDriver(
|
|
|
|
self.env['runtime']['settingsManager'].getSetting('sound', 'driver'), 'soundDriver')
|
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
def shutdown(self):
|
|
|
|
self.env['runtime']['settingsManager'].shutdownDriver('soundDriver')
|
|
|
|
self.env['runtime']['settingsManager'].shutdownDriver('speechDriver')
|
|
|
|
|
2024-12-08 06:43:22 -05:00
|
|
|
def presentText(self, text, interrupt=True, soundIcon='', ignorePunctuation=False, announceCapital=False, flush=True):
|
2018-03-21 11:10:28 +01:00
|
|
|
if text == '':
|
|
|
|
return
|
2024-03-13 17:15:14 -04:00
|
|
|
if self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'readNumbersAsDigits') and len(text.strip()) > 1:
|
2024-03-13 00:56:53 -04:00
|
|
|
text = re.sub(r"(\d)", r"\1 ", text).rstrip()
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("presentText:\nsoundIcon:'"+soundIcon+"'\nText:\n" + text, debug.debugLevel.INFO)
|
2018-03-21 11:10:28 +01:00
|
|
|
if self.playSoundIcon(soundIcon, interrupt):
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("soundIcon found", debug.debugLevel.INFO)
|
2018-03-21 11:10:28 +01:00
|
|
|
return
|
|
|
|
if (len(text) > 1) and (text.strip(string.whitespace) == ''):
|
|
|
|
return
|
|
|
|
toAnnounceCapital = announceCapital and text[0].isupper()
|
|
|
|
if toAnnounceCapital:
|
|
|
|
if self.playSoundIcon('capital', False):
|
2021-05-19 11:06:44 +02:00
|
|
|
toAnnounceCapital = False
|
2018-03-21 11:10:28 +01:00
|
|
|
self.lastEcho = text
|
2024-12-08 06:43:22 -05:00
|
|
|
self.speakText(text, interrupt, ignorePunctuation, toAnnounceCapital)
|
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
def getLastEcho(self):
|
|
|
|
return self.lastEcho
|
2024-12-08 06:43:22 -05:00
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
def speakText(self, text, interrupt=True, ignorePunctuation=False, announceCapital=False):
|
|
|
|
if not self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled'):
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Speech disabled in outputManager.speakText", debug.debugLevel.INFO)
|
2018-03-21 11:10:28 +01:00
|
|
|
return
|
|
|
|
if self.env['runtime']['speechDriver'] == None:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("No speechDriver in outputManager.speakText", debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
return
|
|
|
|
if interrupt:
|
|
|
|
self.interruptOutput()
|
|
|
|
try:
|
|
|
|
self.env['runtime']['speechDriver'].setLanguage(self.env['runtime']['settingsManager'].getSetting('speech', 'language'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("setting speech language in outputManager.speakText", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
self.env['runtime']['speechDriver'].setVoice(self.env['runtime']['settingsManager'].getSetting('speech', 'voice'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Error while setting speech voice in outputManager.speakText", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
if announceCapital:
|
|
|
|
self.env['runtime']['speechDriver'].setPitch(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'capitalPitch'))
|
|
|
|
else:
|
|
|
|
self.env['runtime']['speechDriver'].setPitch(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'pitch'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("setting speech pitch in outputManager.speakText", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
self.env['runtime']['speechDriver'].setRate(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'rate'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("setting speech rate in outputManager.speakText", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
self.env['runtime']['speechDriver'].setModule(self.env['runtime']['settingsManager'].getSetting('speech', 'module'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("setting speech module in outputManager.speakText", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
2021-05-19 11:06:44 +02:00
|
|
|
try:
|
2018-03-21 11:10:28 +01:00
|
|
|
self.env['runtime']['speechDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('speech', 'volume'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("setting speech volume in outputManager.speakText ", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
try:
|
2018-05-17 23:49:32 +02:00
|
|
|
if self.env['runtime']['settingsManager'].getSettingAsBool('general', 'newLinePause'):
|
2024-12-08 06:43:22 -05:00
|
|
|
cleanText = text.replace('\n', ' , ')
|
2018-05-17 23:49:32 +02:00
|
|
|
else:
|
2024-12-08 06:43:22 -05:00
|
|
|
cleanText = text.replace('\n', ' ')
|
2018-05-17 23:49:32 +02:00
|
|
|
|
2018-06-10 14:44:54 +02:00
|
|
|
cleanText = self.env['runtime']['textManager'].replaceHeadLines(cleanText)
|
2018-03-21 11:10:28 +01:00
|
|
|
cleanText = self.env['runtime']['punctuationManager'].proceedPunctuation(cleanText, ignorePunctuation)
|
2024-12-08 06:43:22 -05:00
|
|
|
cleanText = re.sub(' +$', ' ', cleanText)
|
2018-03-21 11:10:28 +01:00
|
|
|
self.env['runtime']['speechDriver'].speak(cleanText)
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Speak: "+ cleanText, debug.debugLevel.INFO)
|
2018-03-21 11:10:28 +01:00
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("\"speak\" in outputManager.speakText ", debug.debugLevel.ERROR)
|
|
|
|
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
2018-03-21 11:10:28 +01:00
|
|
|
|
|
|
|
def interruptOutput(self):
|
2018-05-11 23:16:18 +02:00
|
|
|
try:
|
|
|
|
self.env['runtime']['speechDriver'].cancel()
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Interrupt speech", debug.debugLevel.INFO)
|
2018-05-11 23:16:18 +02:00
|
|
|
except:
|
2021-05-19 11:06:44 +02:00
|
|
|
pass
|
2018-03-21 11:10:28 +01:00
|
|
|
|
2024-12-08 06:43:22 -05:00
|
|
|
def playSoundIcon(self, soundIcon='', interrupt=True):
|
2018-03-21 11:10:28 +01:00
|
|
|
if soundIcon == '':
|
|
|
|
return False
|
|
|
|
soundIcon = soundIcon.upper()
|
|
|
|
if not self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled'):
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playSoundIcon", debug.debugLevel.INFO)
|
2018-03-21 11:10:28 +01:00
|
|
|
return False
|
2019-02-18 10:09:17 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
e = self.env['soundIcons'][soundIcon]
|
|
|
|
except:
|
2021-05-19 11:06:44 +02:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("SoundIcon doesnt exist: " + soundIcon, debug.debugLevel.WARNING)
|
|
|
|
return False
|
2019-02-18 10:09:17 +01:00
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
if self.env['runtime']['soundDriver'] == None:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playSoundIcon: soundDriver not loaded", debug.debugLevel.ERROR)
|
2019-02-18 10:09:17 +01:00
|
|
|
return False
|
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
try:
|
|
|
|
self.env['runtime']['soundDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('sound', 'volume'))
|
2019-02-18 10:09:17 +01:00
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e), debug.debugLevel.ERROR)
|
2019-02-18 10:09:17 +01:00
|
|
|
|
|
|
|
try:
|
2018-03-21 11:10:28 +01:00
|
|
|
self.env['runtime']['soundDriver'].playSoundFile(self.env['soundIcons'][soundIcon], interrupt)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e), debug.debugLevel.ERROR)
|
2019-02-18 10:09:17 +01:00
|
|
|
return False
|
|
|
|
|
2018-03-21 11:10:28 +01:00
|
|
|
return False
|
2021-05-19 11:06:44 +02:00
|
|
|
|
|
|
|
def playFrequence(self, frequence, duration, interrupt=True):
|
|
|
|
if not self.env['runtime']['settingsManager'].getSettingAsBool('sound', 'enabled'):
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("Sound disabled in outputManager.playFrequence", debug.debugLevel.INFO)
|
2021-05-19 11:06:44 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
if frequence < 1 or frequence > 20000:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("outputManager.playFrequence::Filefrequence is out of range:" + str(frequence), debug.debugLevel.INFO)
|
2021-05-19 11:06:44 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
if self.env['runtime']['soundDriver'] == None:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("No soundDriver in outputManager.playFrequence: soundDriver not loaded", debug.debugLevel.ERROR)
|
2021-05-19 11:06:44 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.env['runtime']['soundDriver'].setVolume(self.env['runtime']['settingsManager'].getSettingAsFloat('sound', 'volume'))
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::setVolume: " + str(e), debug.debugLevel.ERROR)
|
2021-05-19 11:37:03 +02:00
|
|
|
adjustVolume = 0.0
|
|
|
|
try:
|
|
|
|
adjustVolume = 1.0 - (frequence / 20000)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if adjustVolume > 9.0:
|
|
|
|
adjustVolume = 9.0
|
2021-05-19 11:06:44 +02:00
|
|
|
|
|
|
|
try:
|
2021-05-19 11:37:03 +02:00
|
|
|
self.env['runtime']['soundDriver'].playFrequence(frequence, duration, adjustVolume, interrupt)
|
2021-05-19 11:06:44 +02:00
|
|
|
return True
|
|
|
|
except Exception as e:
|
2024-12-08 06:43:22 -05:00
|
|
|
self.env['runtime']['debug'].writeDebugOut("outputManager.playSoundIcon::playSoundFile: " + str(e), debug.debugLevel.ERROR)
|
2021-05-19 11:06:44 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2019-04-05 21:12:07 +02:00
|
|
|
def tempDisableSpeech(self):
|
|
|
|
if self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled'):
|
|
|
|
self.presentText(_("speech temporary disabled"), soundIcon='SpeechOff', interrupt=True)
|
|
|
|
self.env['commandBuffer']['enableSpeechOnKeypress'] = True
|
|
|
|
self.env['runtime']['settingsManager'].setSetting('speech', 'enabled', str(not self.env['runtime']['settingsManager'].getSettingAsBool('speech', 'enabled')))
|
|
|
|
self.interruptOutput()
|
2024-12-08 06:43:22 -05:00
|
|
|
|
2021-05-19 11:06:44 +02:00
|
|
|
def announceActiveCursor(self, interrupt_p=False):
|
2018-03-21 11:10:28 +01:00
|
|
|
if self.env['runtime']['cursorManager'].isReviewMode():
|
2021-05-19 11:06:44 +02:00
|
|
|
self.presentText(' review cursor ', interrupt=interrupt_p)
|
2018-03-21 11:10:28 +01:00
|
|
|
else:
|
2021-05-19 11:06:44 +02:00
|
|
|
self.presentText(' text cursor ', interrupt=interrupt_p)
|