[tts.mimic3] Plugin rewrite.

The plugin now leverages the `sound` plugin for playback, like all other
`tts` plugins now do, instead of an external `media` plugin.

This also removes the need for the `/tts/mimic3/say` endpoint.
This commit is contained in:
Fabio Manganiello 2023-10-31 01:45:03 +01:00
parent f960ec4bf4
commit d9c4634ce8
2 changed files with 58 additions and 97 deletions

View file

@ -1,49 +0,0 @@
import requests
from urllib.parse import urljoin
from flask import abort, request, Blueprint
from platypush.backend.http.app import template_folder
# Upstream /api/tts response timeout, in seconds
_default_timeout = 30
mimic3 = Blueprint('mimic3', __name__, template_folder=template_folder)
# Declare routes list
__routes__ = [
mimic3,
]
@mimic3.route('/tts/mimic3/say', methods=['GET'])
def proxy_tts_request():
"""
This route is used to proxy the POST request to the Mimic3 TTS server
through a GET, so it can be easily processed as a URL through a media
plugin.
"""
required_args = {
'text',
'server_url',
'voice',
}
missing_args = required_args.difference(set(request.args.keys()))
if missing_args:
abort(400, f'Missing parameters: {missing_args}')
args = {arg: request.args[arg] for arg in required_args}
rs = requests.post(
urljoin(args['server_url'], '/api/tts'),
data=args['text'],
timeout=int(request.args.get('timeout', _default_timeout)),
params={
'voice': args['voice'],
},
)
return rs.content
# vim:sw=4:ts=4:et:

View file

@ -1,10 +1,11 @@
from typing import Optional
from urllib.parse import urljoin, urlencode
from contextlib import contextmanager
import os
import tempfile
from typing import Generator, Optional
from urllib.parse import urljoin
import requests
from platypush.backend.http.app.utils import get_local_base_url
from platypush.context import get_backend
from platypush.plugins import action
from platypush.plugins.tts import TtsPlugin
from platypush.schemas.tts.mimic3 import Mimic3VoiceSchema
@ -27,44 +28,65 @@ class TtsMimic3Plugin(TtsPlugin):
-v "%h/.local/share/mycroft/mimic3:/home/mimic3/.local/share/mycroft/mimic3" \
'mycroftai/mimic3'
Requires:
* At least a *media plugin* (see
:class:`platypush.plugins.media.MediaPlugin`) enabled/configured -
used for speech playback.
* The ``http`` backend (:class:`platypush.backend.http.HttpBackend`)
enabled - used for proxying the API calls.
"""
def __init__(
self,
server_url: str,
voice: str = 'en_UK/apope_low',
media_plugin: Optional[str] = None,
player_args: Optional[dict] = None,
voice: str = 'en_US/vctk_low',
**kwargs,
):
"""
:param server_url: Base URL of the web server that runs the Mimic3 engine.
:param voice: Default voice to be used (default: ``en_UK/apope_low``).
:param voice: Default voice to be used (default: ``en_US/vctk_low``).
You can get a full list of the voices available on the server
through :meth:`.voices`.
:param media_plugin: Media plugin to be used for audio playback. Supported:
- ``media.gstreamer``
- ``media.omxplayer``
- ``media.mplayer``
- ``media.mpv``
- ``media.vlc``
:param player_args: Optional arguments that should be passed to the player plugin's
:meth:`platypush.plugins.media.MediaPlugin.play` method.
"""
super().__init__(media_plugin=media_plugin, player_args=player_args, **kwargs)
super().__init__(**kwargs)
self.server_url = server_url
self.voice = voice
self.player_args.update(
{
'channels': 1,
'sample_rate': 22050,
'dtype': 'int16',
}
)
@staticmethod
@contextmanager
def _save_audio(
text: str,
server_url: str,
voice: str,
timeout: Optional[float] = None,
) -> Generator[str, None, None]:
"""
Saves the raw audio stream from the Mimic3 server to an audio file for
playback.
:param text: Text to be spoken.
:param server_url: Base URL of the Mimic3 server.
:param voice: Voice to be used.
:param timeout: Timeout for the audio stream retrieval.
"""
rs = requests.post(
urljoin(server_url, '/api/tts'),
data=text,
timeout=timeout,
params={
'voice': voice,
},
)
rs.raise_for_status()
tmp_file = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
tmp_file.write(rs.content)
yield tmp_file.name
tmp_file.close()
os.unlink(tmp_file.name)
@action
def say(
@ -73,7 +95,7 @@ class TtsMimic3Plugin(TtsPlugin):
*_,
server_url: Optional[str] = None,
voice: Optional[str] = None,
player_args: Optional[dict] = None,
**player_args,
):
"""
Say some text.
@ -81,28 +103,16 @@ class TtsMimic3Plugin(TtsPlugin):
:param text: Text to say.
:param server_url: Default ``server_url`` override.
:param voice: Default ``voice`` override.
:param player_args: Default ``player_args`` override.
:param player_args: Extends the additional arguments to be passed to
:meth:`platypush.plugins.sound.SoundPlugin.play` (like volume,
duration, channels etc.).
"""
server_url = server_url or self.server_url
voice = voice or self.voice
player_args = player_args or self.player_args
http = get_backend('http')
assert http, 'http backend not configured'
assert self.media_plugin, 'No media plugin configured'
url = (
urljoin(get_local_base_url(), '/tts/mimic3/say')
+ '?'
+ urlencode(
{
'text': text,
'server_url': server_url,
'voice': voice,
}
)
)
self.media_plugin.play(url, **player_args)
with self._save_audio(text, server_url, voice) as audio_file:
self._playback(audio_file, join=True, **player_args)
@action
def voices(self, server_url: Optional[str] = None):
@ -113,7 +123,7 @@ class TtsMimic3Plugin(TtsPlugin):
:return: .. schema:: tts.mimic3.Mimic3VoiceSchema(many=True)
"""
server_url = server_url or self.server_url
rs = requests.get(urljoin(server_url, '/api/voices'))
rs = requests.get(urljoin(server_url, '/api/voices'), timeout=10)
rs.raise_for_status()
return Mimic3VoiceSchema().dump(rs.json(), many=True)