Skip to content

Commit

Permalink
feat/track_speaking+recording_per_session (#93)
Browse files Browse the repository at this point in the history
keep track of is_speaking and is_recording per session

brings back `is_speaking` and `wait_while_speaking` per Session, allowing HiveMind integration
  • Loading branch information
JarbasAl authored May 3, 2024
1 parent c6d089e commit e67b630
Showing 1 changed file with 115 additions and 10 deletions.
125 changes: 115 additions & 10 deletions ovos_bus_client/session.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import enum
import time
from threading import Lock
from threading import Lock, Event
from typing import Optional, List, Tuple, Union, Iterable, Dict
from uuid import uuid4

Expand Down Expand Up @@ -274,7 +274,9 @@ def __init__(self, session_id: str = None,
location_prefs: Dict = None,
system_unit: str = None,
time_format: str = None,
date_format: str = None):
date_format: str = None,
is_speaking: bool = False,
is_recording: bool = False):
"""
Construct a session identifier
@param session_id: string UUID for the session
Expand All @@ -295,6 +297,8 @@ def __init__(self, session_id: str = None,
self.date_format = date_format or Configuration().get("date_format", "DMY")
self.time_format = time_format or Configuration().get("time_format", "full")

self.is_recording = is_recording
self.is_speaking = is_speaking
self.site_id = site_id or Configuration().get("site_id") or "unknown" # indoors placement info

self.active_skills = active_skills or [] # [skill_id , timestamp]# (Message , timestamp)
Expand Down Expand Up @@ -418,7 +422,9 @@ def serialize(self) -> dict:
"location": self.location_preferences,
"system_unit": self.system_unit,
"time_format": self.time_format,
"date_format": self.date_format
"date_format": self.date_format,
"is_speaking": self.is_speaking,
"is_recording": self.is_recording
}

def update_history(self, message: Message = None):
Expand Down Expand Up @@ -447,6 +453,8 @@ def deserialize(data: Dict):
system_unit = data.get("system_unit")
date_format = data.get("date_format")
time_format = data.get("time_format")
is_recording = data.get("is_recording", False)
is_speaking = data.get("is_speaking", False)
return Session(uid,
active_skills=active,
utterance_states=states,
Expand All @@ -457,7 +465,9 @@ def deserialize(data: Dict):
location_prefs=location,
system_unit=system_unit,
date_format=date_format,
time_format=time_format)
time_format=time_format,
is_recording=is_recording,
is_speaking=is_speaking)

@staticmethod
def from_message(message: Message = None):
Expand Down Expand Up @@ -508,14 +518,13 @@ def sync(cls, message=None):
@classmethod
def connect_to_bus(cls, bus):
cls.bus = bus
cls.bus.on("ovos.session.sync",
cls.handle_default_session_request)
cls.bus.on("recognizer_loop:record_begin", cls.handle_recording_start)
cls.bus.on("recognizer_loop:record_end", cls.handle_recording_end)
cls.bus.on("recognizer_loop:audio_output_start", cls.handle_audio_output_start)
cls.bus.on("recognizer_loop:audio_output_end", cls.handle_audio_output_end)
cls.bus.on("ovos.session.sync", cls.handle_default_session_request)
cls.sync()

@classmethod
def handle_default_session_request(cls, message=None):
cls.sync(message)

@staticmethod
def prune_sessions():
"""
Expand Down Expand Up @@ -595,3 +604,99 @@ def touch(message: Message = None):
"""
sess = SessionManager.get(message)
sess.touch()

##############################
# util methods for skill consumption
@classmethod
def is_speaking(cls, session: Session = None):
session = session or SessionManager.get()
return session.is_speaking

@classmethod
def wait_while_speaking(cls, timeout=15, session: Session = None):
""" wait until audio service reports end of audio output """
if not cls.bus:
LOG.error("SessionManager not connected to bus, can not monitor speech state")
return

session = session or SessionManager.get()
if not cls.is_speaking(session):
return

# wait until end of speech
event = Event()
sessid = session.session_id

def handle_output_end(msg):
nonlocal sessid, event
sess = SessionManager.get(msg)
if sessid == sess.session_id:
event.set()

cls.bus.on("recognizer_loop:audio_output_end", handle_output_end)
event.wait(timeout=timeout)
cls.bus.remove("recognizer_loop:audio_output_end", handle_output_end)

@classmethod
def is_recording(cls, session: Session = None):
session = session or SessionManager.get()
return session.is_recording

@classmethod
def wait_while_recording(cls, timeout=45, session: Session = None):
""" wait until listener service reports end of recording"""
if not cls.bus:
LOG.error("SessionManager not connected to bus, can not monitor recording state")
return

session = session or SessionManager.get()
if not cls.is_recording(session):
return

# wait until end of recording
event = Event()
sessid = session.session_id

def handle_rec_end(msg):
nonlocal sessid, event
sess = SessionManager.get(msg)
if sessid == sess.session_id:
event.set()

cls.bus.on("recognizer_loop:record_end", handle_rec_end)
event.wait(timeout=timeout)
cls.bus.remove("recognizer_loop:record_end", handle_rec_end)

###############################
# State tracking events
@classmethod
def handle_recording_start(cls, message):
"""track when a session is recording audio"""
sess = cls.get(message)
sess.is_recording = True
cls.update(sess)

@classmethod
def handle_recording_end(cls, message):
"""track when a session stops recording audio"""
sess = cls.get(message)
sess.is_recording = False
cls.update(sess)

@classmethod
def handle_audio_output_start(cls, message):
"""track when a session is outputting audio"""
sess = cls.get(message)
sess.is_speaking = True
cls.update(sess)

@classmethod
def handle_audio_output_end(cls, message):
"""track when a session stops outputting audio"""
sess = cls.get(message)
sess.is_speaking = False
cls.update(sess)

@classmethod
def handle_default_session_request(cls, message=None):
cls.sync(message)

0 comments on commit e67b630

Please sign in to comment.