[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:
parent
f960ec4bf4
commit
d9c4634ce8
2 changed files with 58 additions and 97 deletions
|
@ -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:
|
|
|
@ -1,10 +1,11 @@
|
||||||
from typing import Optional
|
from contextlib import contextmanager
|
||||||
from urllib.parse import urljoin, urlencode
|
import os
|
||||||
|
import tempfile
|
||||||
|
from typing import Generator, Optional
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import requests
|
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 import action
|
||||||
from platypush.plugins.tts import TtsPlugin
|
from platypush.plugins.tts import TtsPlugin
|
||||||
from platypush.schemas.tts.mimic3 import Mimic3VoiceSchema
|
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" \
|
-v "%h/.local/share/mycroft/mimic3:/home/mimic3/.local/share/mycroft/mimic3" \
|
||||||
'mycroftai/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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
server_url: str,
|
server_url: str,
|
||||||
voice: str = 'en_UK/apope_low',
|
voice: str = 'en_US/vctk_low',
|
||||||
media_plugin: Optional[str] = None,
|
|
||||||
player_args: Optional[dict] = None,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param server_url: Base URL of the web server that runs the Mimic3 engine.
|
: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
|
You can get a full list of the voices available on the server
|
||||||
through :meth:`.voices`.
|
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.server_url = server_url
|
||||||
self.voice = voice
|
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
|
@action
|
||||||
def say(
|
def say(
|
||||||
|
@ -73,7 +95,7 @@ class TtsMimic3Plugin(TtsPlugin):
|
||||||
*_,
|
*_,
|
||||||
server_url: Optional[str] = None,
|
server_url: Optional[str] = None,
|
||||||
voice: Optional[str] = None,
|
voice: Optional[str] = None,
|
||||||
player_args: Optional[dict] = None,
|
**player_args,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Say some text.
|
Say some text.
|
||||||
|
@ -81,28 +103,16 @@ class TtsMimic3Plugin(TtsPlugin):
|
||||||
:param text: Text to say.
|
:param text: Text to say.
|
||||||
:param server_url: Default ``server_url`` override.
|
:param server_url: Default ``server_url`` override.
|
||||||
:param voice: Default ``voice`` 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
|
server_url = server_url or self.server_url
|
||||||
voice = voice or self.voice
|
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 = (
|
with self._save_audio(text, server_url, voice) as audio_file:
|
||||||
urljoin(get_local_base_url(), '/tts/mimic3/say')
|
self._playback(audio_file, join=True, **player_args)
|
||||||
+ '?'
|
|
||||||
+ urlencode(
|
|
||||||
{
|
|
||||||
'text': text,
|
|
||||||
'server_url': server_url,
|
|
||||||
'voice': voice,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.media_plugin.play(url, **player_args)
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def voices(self, server_url: Optional[str] = None):
|
def voices(self, server_url: Optional[str] = None):
|
||||||
|
@ -113,7 +123,7 @@ class TtsMimic3Plugin(TtsPlugin):
|
||||||
:return: .. schema:: tts.mimic3.Mimic3VoiceSchema(many=True)
|
:return: .. schema:: tts.mimic3.Mimic3VoiceSchema(many=True)
|
||||||
"""
|
"""
|
||||||
server_url = server_url or self.server_url
|
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()
|
rs.raise_for_status()
|
||||||
return Mimic3VoiceSchema().dump(rs.json(), many=True)
|
return Mimic3VoiceSchema().dump(rs.json(), many=True)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue