Add mimic3-client script

This commit is contained in:
Michael Hansen 2022-03-31 12:23:14 -04:00
commit b1bb2b3691
8 changed files with 149 additions and 28 deletions

82
docker/mimic3-client Executable file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
# Copyright 2022 Mycroft AI Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Run mimic3-client --update to update Docker image
args=()
repo='mycroftai/mimic3'
tag='latest'
docker='docker'
port='59125'
while [[ -n "$1" ]]; do
if [[ "$1" == '--update' ]]; then
# Update Docker image
update='1'
elif [[ "$1" == '--port' ]]; then
port="$2"
args+=('--port' "${port}")
shift 1
else
args+=("$1")
fi
shift 1
done
if [[ -n "${update}" ]]; then
docker pull "${repo}:${tag}"
fi
docker_run_args=()
if [[ -d /etc/ssl/certs ]]; then
# This directory seems to usually have symlinks to other directories
docker_run_args+=('-v' '/etc/ssl/certs:/etc/ssl/certs:ro')
# Create temp file with all certificate directories found
cert_dirs_file="$(mktemp)"
function finish {
rm -rf "${cert_dirs_file}"
}
trap finish EXIT
while read -r cert_path; do
# Follow symlinks and record directory paths
cert_path="$(readlink -f "${cert_path}")"
cert_dir="$(dirname "${cert_path}")"
echo $cert_dir >> "${cert_dirs_file}"
done < <(find /etc/ssl/certs -name '*.pem' -type l)
# Map unique certificate directories
while read -r cert_dir; do
docker_run_args+=('-v' "${cert_dir}:${cert_dir}:ro")
done < <(sort < "${cert_dirs_file}" | uniq)
fi
"${docker}" run \
-i \
--network host \
-e "HOME=${HOME}" \
-v "$HOME:${HOME}" \
-w "${PWD}" \
--user "$(id -u):$(id -g)" "${docker_run_args[@]}" \
--entrypoint '/home/mimic3/app/.venv/bin/python3' \
"${repo}:${tag}" \
-m mimic3_http.client \
--stdout \
"${args[@]}"

View file

@ -73,5 +73,5 @@ fi
--user "$(id -u):$(id -g)" "${docker_run_args[@]}" \ --user "$(id -u):$(id -g)" "${docker_run_args[@]}" \
--entrypoint '/home/mimic3/app/.venv/bin/python3' \ --entrypoint '/home/mimic3/app/.venv/bin/python3' \
"${repo}:${tag}" \ "${repo}:${tag}" \
-m mimic3_download \ -m mimic3_tts.download \
"${args[@]}" "${args[@]}"

View file

@ -15,6 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import argparse import argparse
import subprocess
import shlex
import shutil
import logging import logging
import os import os
import sys import sys
@ -28,6 +31,8 @@ _PACKAGE = "mimic3_http.client"
_LOGGER = logging.getLogger(_PACKAGE) _LOGGER = logging.getLogger(_PACKAGE)
_DEFAULT_PLAY_PROGRAMS = ["paplay", "play -q", "aplay -q"]
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -76,14 +81,26 @@ def main():
_LOGGER.debug("Writing WAV data to stdout") _LOGGER.debug("Writing WAV data to stdout")
sys.stdout.buffer.write(wav_bytes) sys.stdout.buffer.write(wav_bytes)
else: else:
from playsound import playsound play_wav_bytes(args, wav_bytes)
with tempfile.NamedTemporaryFile(mode="wb+", suffix=".wav") as wav_file:
wav_file.write(wav_bytes)
wav_file.seek(0)
_LOGGER.debug("Playing WAV file: %s", wav_file.name) # -----------------------------------------------------------------------------
playsound(wav_file.name)
def play_wav_bytes(args: argparse.Namespace, wav_bytes: bytes):
with tempfile.NamedTemporaryFile(mode="wb+", suffix=".wav") as wav_file:
wav_file.write(wav_bytes)
wav_file.seek(0)
for play_program in reversed(args.play_program):
play_cmd = shlex.split(play_program)
if not shutil.which(play_cmd[0]):
continue
play_cmd.append(wav_file.name)
_LOGGER.debug("Playing WAV file: %s", play_cmd)
subprocess.check_output(play_cmd)
break
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -101,24 +118,16 @@ def get_args() -> argparse.Namespace:
help="URL of mimic3 HTTP server (default: http://localhost:59125/api/tts)", help="URL of mimic3 HTTP server (default: http://localhost:59125/api/tts)",
) )
parser.add_argument( parser.add_argument(
"--voice", "--voice", "-v", help="Name of voice (expected in <voices-dir>/<language>)",
"-v",
help="Name of voice (expected in <voices-dir>/<language>)",
) )
parser.add_argument( parser.add_argument(
"--output", "--output", "-o", help="Path to write WAV file (default: play audio)",
"-o",
help="Path to write WAV file (default: play audio)",
) )
parser.add_argument( parser.add_argument(
"--stdout", "--stdout", action="store_true", help="Write WAV data to stdout",
action="store_true",
help="Write WAV data to stdout",
) )
parser.add_argument( parser.add_argument(
"--noise-scale", "--noise-scale", type=float, help="Noise scale [0-1], default is 0.667",
type=float,
help="Noise scale [0-1], default is 0.667",
) )
parser.add_argument( parser.add_argument(
"--length-scale", "--length-scale",
@ -126,9 +135,13 @@ def get_args() -> argparse.Namespace:
help="Length scale (1.0 is default speed, 0.5 is 2x faster)", help="Length scale (1.0 is default speed, 0.5 is 2x faster)",
) )
parser.add_argument( parser.add_argument(
"--noise-w", "--noise-w", type=float, help="Variation in cadence [0-1], default is 0.8",
type=float, )
help="Variation in cadence [0-1], default is 0.8", parser.add_argument(
"--play-program",
action="append",
default=_DEFAULT_PLAY_PROGRAMS,
help="Program(s) used to play WAV files",
) )
parser.add_argument("--ssml", action="store_true", help="Input text is SSML") parser.add_argument("--ssml", action="store_true", help="Input text is SSML")
parser.add_argument( parser.add_argument(

View file

@ -1,5 +1,4 @@
mimic3-tts<1.0 mimic3-tts<1.0
playsound~=1.3.0
quart>=0.16,<1.0 quart>=0.16,<1.0
quart-cors quart-cors
requests>=2,<3 requests>=2,<3

View file

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections import defaultdict
from pathlib import Path from pathlib import Path
import setuptools import setuptools
@ -42,6 +43,34 @@ with open(version_path, "r", encoding="utf-8") as version_file:
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# dependency => [tags]
extras = {}
# Create language-specific extras
for lang in [
"de",
"es",
"fr",
"it",
"nl",
"ru",
"sw",
]:
extras[f"gruut[{lang}]"] = [lang]
# Add "all" tag
for tags in extras.values():
tags.append("all")
# Invert for setup
extras_require = defaultdict(list)
for dep, tags in extras.items():
for tag in tags:
extras_require[tag].append(dep)
# -----------------------------------------------------------------------------
setup( setup(
name="mimic3_http", name="mimic3_http",
version=version, version=version,
@ -53,7 +82,7 @@ setup(
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
package_data={"mimic3_http": ["VERSION", "py.typed", "templates", "css", "img"]}, package_data={"mimic3_http": ["VERSION", "py.typed", "templates", "css", "img"]},
install_requires=requirements, install_requires=requirements,
extras_require={':python_version<"3.9"': ["importlib_resources"]}, extras_require={':python_version<"3.9"': ["importlib_resources"], **extras_require},
entry_points={ entry_points={
"console_scripts": [ "console_scripts": [
"mimic3-server = mimic3_http.__main__:main", "mimic3-server = mimic3_http.__main__:main",

View file

@ -485,7 +485,7 @@ def get_args():
parser.add_argument( parser.add_argument(
"--interactive", "--interactive",
action="store_true", action="store_true",
help="Play audio after each input line (see --play-command)", help="Play audio after each input line (see --play-program)",
) )
parser.add_argument("--csv", action="store_true", help="Input format is id|text") parser.add_argument("--csv", action="store_true", help="Input format is id|text")
parser.add_argument( parser.add_argument(

View file

@ -262,7 +262,7 @@ class Mimic3TextToSpeechSystem(TextToSpeechSystem):
# Clear speaker on voice change # Clear speaker on voice change
self.speaker = None self.speaker = None
self.settings.voice = new_voice self.settings.voice = new_voice or DEFAULT_VOICE
if "#" in self.settings.voice: if "#" in self.settings.voice:
# Split # Split

View file

@ -53,9 +53,7 @@ for lang in [
"fr", "fr",
"it", "it",
"nl", "nl",
"pt",
"ru", "ru",
"sv",
"sw", "sw",
]: ]:
extras[f"gruut[{lang}]"] = [lang] extras[f"gruut[{lang}]"] = [lang]