Skip to content

Commit

Permalink
Cancel any current phrase when Talon is waking (talonhub#1435)
Browse files Browse the repository at this point in the history
When Talon wakes we set a timestamp threshold. On the next command we
will compare the phrase timestamp to the threshold and cancel any phrase
started before wakeup. This is to prevent commands from being executed
if the user wakes Talon using a noise or keypress.

---------

Co-authored-by: Nicholas Riley <com-github@sabi.net>
  • Loading branch information
AndreasArvidsson and nriley authored Jun 22, 2024
1 parent 8a05482 commit d91cf83
Showing 1 changed file with 60 additions and 8 deletions.
68 changes: 60 additions & 8 deletions plugin/cancel/cancel.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,72 @@
# to disable command cancellation, comment out this entire file.
# you may also wish to adjust the commands in misc/cancel.talon.
from talon import actions, speech_system

import time

from talon import Context, Module, actions, speech_system
from talon.grammar import Phrase

# To change the phrase used to cancel commands, you must also adjust misc/cancel.talon
cancel_phrase = "cancel cancel".split()

mod = Module()
ctx = Context()

def pre_phrase(d):
n = len(cancel_phrase)
if "text" in d and "parsed" in d:
before, after = d["text"][:-n], d["text"][-n:]
if after != cancel_phrase:
ts_threshold: float = 0


@ctx.action_class("speech")
class SpeechActions:
# When Talon wakes we set the timestamp threshold. On the next command we
# will compare the phrase timestamp to the threshold and cancel any phrase
# started before wakeup. This is to prevent speech said before wake-up to
# be interpreted as a command if the user wakes Talon using a noise or
# keypress.
def enable():
actions.user.cancel_current_phrase()
actions.next()


@mod.action_class
class Actions:
def cancel_current_phrase():
"""Cancel/abort current spoken phrase"""
global ts_threshold
ts_threshold = time.perf_counter()


def pre_phrase(phrase: Phrase):
global ts_threshold

words = phrase["phrase"]

if not words:
return

# Check if the phrase is before the threshold
if ts_threshold != 0:
start = getattr(words[0], "start", phrase["_ts"])
phrase_starts_before_threshold = start < ts_threshold
ts_threshold = 0
# Start of phrase is before threshold timestamp
if phrase_starts_before_threshold:
print(f"Canceled phrase: {' '.join(words)}")
cancel_entire_phrase(phrase)
return
# cancel the command
d["parsed"]._sequence = []

# Check if the phrase is a cancel command
n = len(cancel_phrase)
before, after = words[:-n], words[-n:]
if after == cancel_phrase:
actions.app.notify(f"Command canceled: {' '.join(before)!r}")
cancel_entire_phrase(phrase)
return


def cancel_entire_phrase(phrase: Phrase):
phrase["phrase"] = []
if "parsed" in phrase:
phrase["parsed"]._sequence = []


speech_system.register("pre:phrase", pre_phrase)

0 comments on commit d91cf83

Please sign in to comment.