diff --git a/docker/mimic3-client b/docker/mimic3-client
new file mode 100755
index 0000000..5779655
--- /dev/null
+++ b/docker/mimic3-client
@@ -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 .
+#
+
+# 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[@]}"
diff --git a/docker/mimic3-download b/docker/mimic3-download
index 749bf60..86b2d48 100755
--- a/docker/mimic3-download
+++ b/docker/mimic3-download
@@ -73,5 +73,5 @@ fi
--user "$(id -u):$(id -g)" "${docker_run_args[@]}" \
--entrypoint '/home/mimic3/app/.venv/bin/python3' \
"${repo}:${tag}" \
- -m mimic3_download \
+ -m mimic3_tts.download \
"${args[@]}"
diff --git a/mimic3-http/mimic3_http/client.py b/mimic3-http/mimic3_http/client.py
index 72b924e..216ff73 100644
--- a/mimic3-http/mimic3_http/client.py
+++ b/mimic3-http/mimic3_http/client.py
@@ -15,6 +15,9 @@
# along with this program. If not, see .
#
import argparse
+import subprocess
+import shlex
+import shutil
import logging
import os
import sys
@@ -28,6 +31,8 @@ _PACKAGE = "mimic3_http.client"
_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")
sys.stdout.buffer.write(wav_bytes)
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)",
)
parser.add_argument(
- "--voice",
- "-v",
- help="Name of voice (expected in /)",
+ "--voice", "-v", help="Name of voice (expected in /)",
)
parser.add_argument(
- "--output",
- "-o",
- help="Path to write WAV file (default: play audio)",
+ "--output", "-o", help="Path to write WAV file (default: play audio)",
)
parser.add_argument(
- "--stdout",
- action="store_true",
- help="Write WAV data to stdout",
+ "--stdout", action="store_true", help="Write WAV data to stdout",
)
parser.add_argument(
- "--noise-scale",
- type=float,
- help="Noise scale [0-1], default is 0.667",
+ "--noise-scale", type=float, help="Noise scale [0-1], default is 0.667",
)
parser.add_argument(
"--length-scale",
@@ -126,9 +135,13 @@ def get_args() -> argparse.Namespace:
help="Length scale (1.0 is default speed, 0.5 is 2x faster)",
)
parser.add_argument(
- "--noise-w",
- type=float,
- help="Variation in cadence [0-1], default is 0.8",
+ "--noise-w", 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(
diff --git a/mimic3-http/requirements.txt b/mimic3-http/requirements.txt
index ed78b9c..7dd160a 100644
--- a/mimic3-http/requirements.txt
+++ b/mimic3-http/requirements.txt
@@ -1,5 +1,4 @@
mimic3-tts<1.0
-playsound~=1.3.0
quart>=0.16,<1.0
quart-cors
requests>=2,<3
diff --git a/mimic3-http/setup.py b/mimic3-http/setup.py
index 8e4b749..38dc88b 100644
--- a/mimic3-http/setup.py
+++ b/mimic3-http/setup.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
+from collections import defaultdict
from pathlib import Path
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(
name="mimic3_http",
version=version,
@@ -53,7 +82,7 @@ setup(
packages=setuptools.find_packages(),
package_data={"mimic3_http": ["VERSION", "py.typed", "templates", "css", "img"]},
install_requires=requirements,
- extras_require={':python_version<"3.9"': ["importlib_resources"]},
+ extras_require={':python_version<"3.9"': ["importlib_resources"], **extras_require},
entry_points={
"console_scripts": [
"mimic3-server = mimic3_http.__main__:main",
diff --git a/mimic3-tts/mimic3_tts/__main__.py b/mimic3-tts/mimic3_tts/__main__.py
index e06b61d..e679ae3 100644
--- a/mimic3-tts/mimic3_tts/__main__.py
+++ b/mimic3-tts/mimic3_tts/__main__.py
@@ -485,7 +485,7 @@ def get_args():
parser.add_argument(
"--interactive",
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(
diff --git a/mimic3-tts/mimic3_tts/tts.py b/mimic3-tts/mimic3_tts/tts.py
index 2c02842..3cdbd00 100644
--- a/mimic3-tts/mimic3_tts/tts.py
+++ b/mimic3-tts/mimic3_tts/tts.py
@@ -262,7 +262,7 @@ class Mimic3TextToSpeechSystem(TextToSpeechSystem):
# Clear speaker on voice change
self.speaker = None
- self.settings.voice = new_voice
+ self.settings.voice = new_voice or DEFAULT_VOICE
if "#" in self.settings.voice:
# Split
diff --git a/mimic3-tts/setup.py b/mimic3-tts/setup.py
index eff0a44..0548f72 100644
--- a/mimic3-tts/setup.py
+++ b/mimic3-tts/setup.py
@@ -53,9 +53,7 @@ for lang in [
"fr",
"it",
"nl",
- "pt",
"ru",
- "sv",
"sw",
]:
extras[f"gruut[{lang}]"] = [lang]