Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor/drop audio service #49

Merged
merged 2 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 2 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The "mouth" of the OVOS assistant!

Handles TTS generation and audio playback
Handles TTS generation and sounds playback

## Install

Expand Down Expand Up @@ -51,27 +51,6 @@ under mycroft.conf

// Mechanism used to play OGG audio files
// Override: SYSTEM
"play_ogg_cmdline": "ogg123 -q %1",

"Audio": {
// message.context may contains a source and destination
// native audio (playback / TTS) will only be played if a
// message destination is a native_source or if missing (considered a broadcast)
"native_sources": ["debug_cli", "audio"],

"backends": {
"OCP": {
"type": "ovos_common_play",
"active": true
},
"simple": {
"type": "ovos_audio_simple",
"active": true
},
"vlc": {
"type": "ovos_vlc",
"active": true
}
}
"play_ogg_cmdline": "ogg123 -q %1"
}
```
72 changes: 36 additions & 36 deletions ovos_audio/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
from ovos_bus_client import Message, MessageBusClient
from ovos_bus_client.session import SessionManager
from ovos_config.config import Configuration
from ovos_plugin_manager.audio import get_audio_service_configs
from ovos_plugin_manager.g2p import get_g2p_lang_configs, get_g2p_supported_langs, get_g2p_module_configs
from ovos_plugin_manager.tts import TTS
from ovos_plugin_manager.tts import get_tts_supported_langs, get_tts_lang_configs, get_tts_module_configs
from ovos_utils.file_utils import resolve_resource_file
from ovos_utils.log import LOG
from ovos_utils.log import LOG, deprecated
from ovos_utils.metrics import Stopwatch
from ovos_utils.process_utils import ProcessStatus, StatusCallbackMap
from ovos_utils.sound import play_audio
Expand All @@ -29,23 +28,23 @@


def on_ready():
LOG.info('Audio service is ready.')
LOG.info('TTS service is ready.')


def on_alive():
LOG.info('Audio service is alive.')
LOG.info('TTS service is alive.')


def on_started():
LOG.info('Audio service started.')
LOG.info('TTS service started.')


def on_error(e='Unknown'):
LOG.error(f'Audio service failed to launch ({e}).')
LOG.error(f'TTS service failed to launch ({e}).')


def on_stopping():
LOG.info('Audio service is shutting down...')
LOG.info('TTS service is shutting down...')


class PlaybackService(Thread):
Expand Down Expand Up @@ -93,11 +92,13 @@ def __init__(self, ready_hook=on_ready, error_hook=on_error,
LOG.exception(e)
self.status.set_error(e)

try:
self.audio = AudioService(self.bus, disable_ocp=disable_ocp, validate_source=validate_source)
except Exception as e:
LOG.exception(e)
self.status.set_error(e)
self.audio = None
self.audio_enabled = self.config.get("enable_old_audioservice", True) # TODO default to False soon
if self.audio_enabled:
try:
self.audio = AudioService(self.bus, disable_ocp=disable_ocp, validate_source=validate_source)
except Exception as e:
LOG.exception(e)

@staticmethod
def get_tts_lang_options(lang, blacklist=None):
Expand Down Expand Up @@ -157,6 +158,7 @@ def get_g2p_lang_options(lang, blacklist=None):
return opts

@staticmethod
@deprecated("audio service moved to ovos-media", "0.1.0")
def get_audio_options(blacklist=None):
""" returns a list of options to be consumed by an external UI
each dict contains metadata about the plugins
Expand All @@ -166,17 +168,7 @@ def get_audio_options(blacklist=None):
"active": True,
"plugin_name": 'Ovos Common Play'}]
"""
blacklist = blacklist or []
opts = []
cfgs = get_audio_service_configs()
for name, config in cfgs.items():
engine = config["type"]
if engine in blacklist:
continue
# For Display purposes, we want to show the engine name without the underscore or dash and capitalized all
plugin_display_name = engine.replace("_", " ").replace("-", " ").title()
config["plugin_name"] = plugin_display_name
opts.append(config)
return opts

def handle_opm_tts_query(self, message):
Expand Down Expand Up @@ -229,6 +221,7 @@ def handle_opm_g2p_query(self, message):
}
self.bus.emit(message.response(data))

@deprecated("audio service moved to ovos-media", "0.1.0")
def handle_opm_audio_query(self, message):
""" Responds to opm.audio.query with data about installed plugins

Expand All @@ -237,21 +230,23 @@ def handle_opm_audio_query(self, message):
"configs" - {backend_name: backend_cfg}}
"options" - {lang: [list_of_valid_ui_metadata]}
"""
cfgs = get_audio_service_configs()
data = {
"plugins": list(cfgs.keys()),
"configs": cfgs,
"options": self.get_audio_options()
"plugins": [],
"configs": {},
"options": {}
}
self.bus.emit(message.response(data))

def run(self):
self.status.set_alive()
if self.audio.wait_for_load():
if len(self.audio.service) == 0:
LOG.warning('No audio backends loaded! '
'Audio playback is not available')
LOG.info("Running audio service in TTS only mode")
if self.audio_enabled:
LOG.warning("audio service has moved to ovos-media, if you already migrated to ovos-media "
'set "enable_old_audioservice": false in mycroft.conf')
if self.audio.wait_for_load():
if len(self.audio.service) == 0:
LOG.warning('No audio backends loaded! '
'Audio playback is not available')
LOG.info("Running audio service in TTS only mode")
# If at least TTS exists, report ready
if self.tts:
self.status.set_ready()
Expand Down Expand Up @@ -492,14 +487,18 @@ def handle_instant_play(self, message):
volume_changed = True
elif muted:
self.bus.emit(Message("mycroft.volume.unmute"))
if self.audio.current and not duck_pulse_handled:
self.audio.current.lower_volume()

if self.audio:
if self.audio.current and not duck_pulse_handled:
self.audio.current.lower_volume()

play_audio(audio_file).wait()

# return to previous state
if self.audio.current and not duck_pulse_handled:
self.audio.current.restore_volume()
if self.audio:
if self.audio.current and not duck_pulse_handled:
self.audio.current.restore_volume()

if ensure_volume:
if volume_changed:
self.bus.emit(Message("mycroft.volume.set", {"percent": volume,
Expand Down Expand Up @@ -528,7 +527,8 @@ def shutdown(self):
if self.tts.playback:
self.tts.playback.shutdown()
self.tts.playback.join()
self.audio.shutdown()
if self.audio:
self.audio.shutdown()

def init_messagebus(self):
"""
Expand Down
5 changes: 3 additions & 2 deletions ovos_audio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
#
import time

from ovos_utils.log import deprecated
from ovos_utils.signal import check_for_signal

from ovos_bus_client.send_func import send
from ovos_config import Configuration
from ovos_utils.log import LOG, deprecated
from ovos_utils.signal import check_for_signal


def validate_message_context(message, native_sources=None):
Expand Down
10 changes: 1 addition & 9 deletions requirements/extras.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
ovos_plugin_common_play~=0.0, >=0.0.6a11

ovos-tts-plugin-server

# ovos-ocp-youtube-plugin
ovos-ocp-m3u-plugin
ovos-ocp-rss-plugin
ovos-ocp-files-plugin
ovos-ocp-news-plugin
ovos-tts-plugin-server
16 changes: 3 additions & 13 deletions test/unittests/test_speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,27 +286,17 @@ def rcvm(msg):

speech.handle_opm_g2p_query(Message("opm.g2p.query"))

@mock.patch('ovos_audio.service.get_audio_service_configs')
def test_opm_audio(self, mock_get_configs, tts_factory_mock, config_mock):
def test_opm_audio(self, tts_factory_mock, config_mock):
setup_mocks(config_mock, tts_factory_mock)

ocp = {"type": "ovos_common_play", "active": True}
p = {"type": "ovos_badass_player", "active": True}

# per module configs, mocking same return val for all plugin inputs (!)
mock_get_configs.return_value = {"ocp": ocp, "badass": p}

bus = FakeBus()
speech = PlaybackService(bus=bus)

def rcvm(msg):
msg = json.loads(msg)
self.assertEqual(msg["type"], "opm.audio.query.response")
self.assertEqual(msg["data"]["plugins"], ["ocp", "badass"])
self.assertEqual(msg["data"]["configs"], {"ocp": ocp, "badass": p})
ocp["plugin_name"] = 'Ovos Common Play'
p["plugin_name"] = 'Ovos Badass Player'
self.assertEqual(msg["data"]["options"], [ocp, p])
self.assertEqual(msg["data"]["plugins"], [])
self.assertEqual(msg["data"]["configs"], {})

bus.on("message", rcvm)

Expand Down
Loading