Skip to content

Commit

Permalink
fix timer and modular core
Browse files Browse the repository at this point in the history
  • Loading branch information
C-Loftus committed Dec 31, 2023
1 parent bff9934 commit 9343c0b
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 106 deletions.
111 changes: 11 additions & 100 deletions core/core-integration.py → core/core-agnostic.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from typing import Optional
from talon import Module, actions, Context, settings, cron, ui, registry, scope, clip, app
import os, subprocess
from ..utils.utils import remove_special
from .sapi import SAPI5
"""
This file contains actions that are core to the talon tts system
and are agnostic to the tts engine being used or the operating system
"""

if os.name == 'nt':
speaker = SAPI5()
from typing import Optional
from talon import Module, actions, Context, settings, app


mod = Module()
Expand Down Expand Up @@ -108,98 +107,10 @@ def base_win_tts(text: str):
so we can share the speaker object across files since
it won't get overridden by the other tts functions"""


ctxWindows = Context()
ctxWindows.matches = r"""
os: windows
"""

@ctxWindows.action_class('user')
class UserActions:
def base_win_tts(text: str):
"""Base function for windows tts. We expose this
so we can share the speaker object across files. We don't want
it to get overridden by the other tts functions"""
speaker.set_rate(settings.get("user.tts_speed", 0))
speaker.set_volume(settings.get("user.tts_volume", 50))
speaker.speak(text, interrupt=True)

def tts(text: str):
"""text to speech with windows voice"""
actions.user.base_win_tts(text)

def toggle_reader():
"""Toggles the screen reader on and off"""
actions.user.toggle_nvda()


ctxLinux = Context()
ctxLinux.matches = r"""
os: linux
"""


@ctxLinux.action_class('user')
class UserActions:

def toggle_reader():
"""Toggles the screen reader on and off"""
actions.user.toggle_orca()

def espeak(text: str):
"""Text to speech with a robotic/narrator voice"""
rate = settings.get("user.tts_speed", 0)
# convert -5 to 5 to -100 to 100
rate = rate * 20
text = remove_special(text)

proc = subprocess.Popen(["spd-say", text, "--rate", str(rate)])
actions.user.set_cancel_callback(proc.kill)


def tts(text: str):
"""Text to speech with a robotic/narrator voice"""
# change the directory to the directory of this file
# so we can run the command from the correct directory
model_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "additional_voices", "models")
# You have to install piper with pipx
piper = os.path.expanduser("~/.local/bin/piper")

os.chdir(model_dir)

modes = ['en_US-amy-low.onnx', 'en_US-lessac-medium.onnx']
# high = 22050
# Hz for playback in low quality
low = 16000

# we need this more verbose representation here so we don't use the
# shell and have risks of shell expansion
command1 = ["echo", f"{text}"]

command2 = [piper, "--model", modes[0], "--length_scale", "0.5", "--output_raw"]

command3 = ["aplay", "-r", str(low), "-c", "1", "-f", "S16_LE", "-t", "raw"]

echo = subprocess.Popen(command1, stdout=subprocess.PIPE)
piper = subprocess.Popen(command2, stdin=echo.stdout, stdout=subprocess.PIPE)
echo.stdout.close()
aplay = subprocess.Popen(command3, stdin=piper.stdout)
piper.stdout.close()
actions.user.set_cancel_callback(aplay.kill)


ctxMac = Context()
ctxMac.matches = r"""
os: mac
"""

@ctxMac.action_class('user')
class UserActions:
def tts(text: str):
"""Text to speech with a robotic/narrator voice"""
# We can't really schedule this since it is a system command, so we
# have to spawn a new process each time unfortunately
proc = subprocess.Popen(["say", text])
actions.user.set_cancel_callback(proc.kill)
def switch_voice():
"""Switches the tts voice"""
actions.user.tts("Switching Not Supported In This Context")


def piper(text: str):
"""Text to speech with a piper model"""
79 changes: 79 additions & 0 deletions core/core-linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from talon import Context, actions, settings
import os, subprocess

ctxLinux = Context()
ctxLinux.matches = r"""
os: linux
"""

from typing import Literal
speaker: Literal["espeak", "piper"] = "espeak"


@ctxLinux.action_class('user')
class UserActions:

def toggle_reader():
"""Toggles the screen reader on and off"""
actions.user.toggle_orca()

def switch_voice():
"""Switches the tts voice"""
global speaker
if speaker == "espeak":
speaker = "piper"
actions.user.tts("Switched to piper")
else:
speaker = "espeak"
actions.user.tts("Switched to espeak")

def tts(text: str):
"""Text to speech with a robotic/narrator voice"""
match speaker:
case "espeak":
actions.user.espeak(text)
case "piper":
actions.user.piper(text)
case _:
raise ValueError(f"Unknown speaker {speaker}")

def espeak(text: str):
"""Text to speech with a robotic/narrator voice"""
rate = settings.get("user.tts_speed", 0)
# convert -5 to 5 to -100 to 100
rate = rate * 10
# text = remove_special(text)

proc = subprocess.Popen(["spd-say", text, "--rate", str(rate)])
actions.user.set_cancel_callback(proc.kill)


def piper(text: str):
"""Text to speech with a robotic/narrator voice"""
# change the directory to the directory of this file
# so we can run the command from the correct directory
model_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "additional_voices", "models")
# You have to install piper with pipx
piper = os.path.expanduser("~/.local/bin/piper")

os.chdir(model_dir)

modes = ['en_US-amy-low.onnx', 'en_US-lessac-medium.onnx']
# high = 22050
# Hz for playback in low quality
low = 16000

# we need this more verbose representation here so we don't use the
# shell and have risks of shell expansion
command1 = ["echo", f"{text}"]

command2 = [piper, "--model", modes[0], "--length_scale", "0.5", "--output_raw"]

command3 = ["aplay", "-r", str(low), "-c", "1", "-f", "S16_LE", "-t", "raw"]

echo = subprocess.Popen(command1, stdout=subprocess.PIPE)
piper = subprocess.Popen(command2, stdin=echo.stdout, stdout=subprocess.PIPE)
echo.stdout.close()
aplay = subprocess.Popen(command3, stdin=piper.stdout)
piper.stdout.close()
actions.user.set_cancel_callback(aplay.kill)
18 changes: 18 additions & 0 deletions core/core-mac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from talon import Context, actions
import subprocess

ctxMac = Context()
ctxMac.matches = r"""
os: mac
"""

@ctxMac.action_class('user')
class UserActions:
def tts(text: str):
"""Text to speech with a robotic/narrator voice"""
# We can't really schedule this since it is a system command, so we
# have to spawn a new process each time unfortunately
proc = subprocess.Popen(["say", text])
actions.user.set_cancel_callback(proc.kill)


31 changes: 31 additions & 0 deletions core/core-windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from talon import Context, settings
import os

from .sapi import SAPI5

if os.name == 'nt':
speaker = SAPI5()


ctxWindows = Context()
ctxWindows.matches = r"""
os: windows
"""

@ctxWindows.action_class('user')
class UserActions:
def base_win_tts(text: str):
"""Base function for windows tts. We expose this
so we can share the speaker object across files. We don't want
it to get overridden by the other tts functions"""
speaker.set_rate(settings.get("user.tts_speed", 0))
speaker.set_volume(settings.get("user.tts_volume", 50))
speaker.speak(text, interrupt=True)

def tts(text: str):
"""text to speech with windows voice"""
actions.user.base_win_tts(text)

def toggle_reader():
"""Toggles the screen reader on and off"""
actions.user.toggle_nvda()
3 changes: 3 additions & 0 deletions sight-free-global.talon
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ toggle [screen] reader:

toggle (key | keypress) sound:
user.toggle_keypress_sound()

swtich voice:
user.switch_voice()
2 changes: 1 addition & 1 deletion sight-free-settings.talon
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ settings():

# How fast to play back text-to-speech -10 to 10
# Ignored if using screenreader tts
user.tts_speed = 6
user.tts_speed = 8

# How loud to play back text-to-speech from 0 to 100
# Ignored if using screenreader tts
Expand Down
13 changes: 8 additions & 5 deletions utils/timer.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
from talon import cron, Module, actions, app, settings
from talon import cron, Module, actions, app, settings, app
import os
if os.name == "nt":
import ctypes


"""
Every certain amount of minutes, remind the user to take a break
to rest their eyes by looking at something 20 feet away
for at least 20 seconds.
"""

#Convert minutes to seconds
ten_min = cron.seconds_to_timespec(settings.get("user.min_until_break") * 60)

# Defaults to Andreas' notification system, but falls back to Talon's
def notify(message: str):
Expand Down Expand Up @@ -43,5 +40,11 @@ def eye_break_callback():
else:
notify("Elapsed")

cron.interval(ten_min, break_wrapper)

def set_timer():
ten_min = cron.seconds_to_timespec(settings.get("user.min_until_break") * 60)
cron.interval(ten_min, break_wrapper)

app.register("ready", set_timer)


0 comments on commit 9343c0b

Please sign in to comment.