speechd-dispatcher example
This commit is contained in:
parent
50bc04b987
commit
ef85fc4c5c
5 changed files with 195 additions and 7 deletions
9
mimic3-cli/examples/oz/book.txt
Normal file
9
mimic3-cli/examples/oz/book.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Dorothy lived in the midst of the great Kansas prairies, with Uncle Henry, who was a farmer, and Aunt Em, who was the farmer’s wife. Their house was small, for the lumber to build it had to be carried by wagon many miles. There were four walls, a floor and a roof, which made one room; and this room contained a rusty looking cookstove, a cupboard for the dishes, a table, three or four chairs, and the beds. Uncle Henry and Aunt Em had a big bed in one corner, and Dorothy a little bed in another corner. There was no garret at all, and no cellar—except a small hole dug in the ground, called a cyclone cellar, where the family could go in case one of those great whirlwinds arose, mighty enough to crush any building in its path. It was reached by a trap door in the middle of the floor, from which a ladder led down into the small, dark hole.
|
||||
|
||||
When Dorothy stood in the doorway and looked around, she could see nothing but the great gray prairie on every side. Not a tree nor a house broke the broad sweep of flat country that reached to the edge of the sky in all directions. The sun had baked the plowed land into a gray mass, with little cracks running through it. Even the grass was not green, for the sun had burned the tops of the long blades until they were the same gray color to be seen everywhere. Once the house had been painted, but the sun blistered the paint and the rains washed it away, and now the house was as dull and gray as everything else.
|
||||
|
||||
When Aunt Em came there to live she was a young, pretty wife. The sun and wind had changed her, too. They had taken the sparkle from her eyes and left them a sober gray; they had taken the red from her cheeks and lips, and they were gray also. She was thin and gaunt, and never smiled now. When Dorothy, who was an orphan, first came to her, Aunt Em had been so startled by the child’s laughter that she would scream and press her hand upon her heart whenever Dorothy’s merry voice reached her ears; and she still looked at the little girl with wonder that she could find anything to laugh at.
|
||||
|
||||
Uncle Henry never laughed. He worked hard from morning till night and did not know what joy was. He was gray also, from his long beard to his rough boots, and he looked stern and solemn, and rarely spoke.
|
||||
|
||||
It was Toto that made Dorothy laugh, and saved her from growing as gray as her other surroundings. Toto was not gray; he was a little black dog, with long silky hair and small black eyes that twinkled merrily on either side of his funny, wee nose. Toto played all day long, and Dorothy played with him, and loved him dearly
|
||||
20
mimic3-http/client.sh
Executable file
20
mimic3-http/client.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
# Directory of *this* script
|
||||
this_dir="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
# Kebab to snake case
|
||||
module_name="$(basename "${this_dir}" | sed -e 's/-/_/g')"
|
||||
src_dir="${this_dir}/${module_name}"
|
||||
|
||||
# Path to virtual environment
|
||||
: "${venv:=${this_dir}/.venv}"
|
||||
|
||||
if [ -d "${venv}" ]; then
|
||||
# Activate virtual environment if available
|
||||
source "${venv}/bin/activate"
|
||||
fi
|
||||
|
||||
export PYTHONPATH="${this_dir}"
|
||||
python3 -m "${module_name}.client" "$@"
|
||||
|
|
@ -18,7 +18,7 @@ import asyncio
|
|||
import dataclasses
|
||||
import io
|
||||
import logging
|
||||
import sys
|
||||
import hashlib
|
||||
import tempfile
|
||||
import typing
|
||||
import wave
|
||||
|
|
@ -64,13 +64,19 @@ parser.add_argument(
|
|||
)
|
||||
parser.add_argument("--speaker", type=int, help="Default speaker to use (name or id)")
|
||||
parser.add_argument(
|
||||
"--length-scale", type=float, default=1.0, help="Speed of speech (> 1 is slower)"
|
||||
"--noise-scale",
|
||||
type=float,
|
||||
help="Noise scale [0-1], default is 0.667",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--noise-scale", type=float, default=0.333, help="Noise source for audio (0-1)"
|
||||
"--length-scale",
|
||||
type=float,
|
||||
help="Length scale (1.0 is default speed, 0.5 is 2x faster)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--noise-w", type=float, default=1.0, help="Variation in cadence (0-1)"
|
||||
"--noise-w",
|
||||
type=float,
|
||||
help="Variation in cadence [0-1], default is 0.8",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cache-dir",
|
||||
|
|
@ -121,6 +127,10 @@ class TextToWavParams:
|
|||
ssml: bool = False
|
||||
text_language: typing.Optional[str] = None
|
||||
|
||||
@property
|
||||
def cache_key(self) -> str:
|
||||
return hashlib.md5(repr(self).encode()).hexdigest()
|
||||
|
||||
|
||||
# params -> Path
|
||||
_WAV_CACHE: typing.Dict[TextToWavParams, Path] = {}
|
||||
|
|
@ -157,7 +167,7 @@ def text_to_wav(params: TextToWavParams, no_cache: bool = False) -> bytes:
|
|||
|
||||
if _TEMP_DIR and (not no_cache):
|
||||
# Look up in cache
|
||||
maybe_wav_path = _TEMP_DIR / f"{hash(params)}.wav"
|
||||
maybe_wav_path = _TEMP_DIR / f"{params.cache_key}.wav"
|
||||
if maybe_wav_path.is_file():
|
||||
_LOGGER.debug("Loading WAV from cache: %s", maybe_wav_path)
|
||||
wav_bytes = maybe_wav_path.read_bytes()
|
||||
|
|
@ -190,7 +200,16 @@ def text_to_wav(params: TextToWavParams, no_cache: bool = False) -> bytes:
|
|||
|
||||
wav_file.writeframes(result.audio_bytes)
|
||||
|
||||
return wav_io.getvalue()
|
||||
wav_bytes = wav_io.getvalue()
|
||||
|
||||
if _TEMP_DIR and (not no_cache):
|
||||
# Store in cache
|
||||
wav_path = _TEMP_DIR / f"{params.cache_key}.wav"
|
||||
wav_path.write_bytes(wav_bytes)
|
||||
|
||||
_LOGGER.debug("Cached WAV at %s", wav_path.absolute())
|
||||
|
||||
return wav_bytes
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
@ -239,7 +258,7 @@ async def app_tts() -> Response:
|
|||
"""Speak text to WAV."""
|
||||
tts_args: typing.Dict[str, typing.Any] = {}
|
||||
|
||||
_LOGGER.debug(request.args)
|
||||
_LOGGER.debug("Request args: %s", request.args)
|
||||
|
||||
voice = request.args.get("voice")
|
||||
if voice is not None:
|
||||
|
|
@ -258,9 +277,12 @@ async def app_tts() -> Response:
|
|||
if length_scale is not None:
|
||||
tts_args["length_scale"] = float(length_scale)
|
||||
|
||||
# Set SSML flag either from arg or content type
|
||||
ssml_str = request.args.get("ssml")
|
||||
if ssml_str is not None:
|
||||
tts_args["ssml"] = _to_bool(ssml_str)
|
||||
elif request.content_type == "application/ssml+xml":
|
||||
tts_args["ssml"] = True
|
||||
|
||||
text_language = request.args.get("textLanguage")
|
||||
if text_language is not None:
|
||||
|
|
|
|||
135
mimic3-http/mimic3_http/client.py
Normal file
135
mimic3-http/mimic3_http/client.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import typing
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
_PACKAGE = "mimic3_http.client"
|
||||
|
||||
_LOGGER = logging.getLogger(_PACKAGE)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def main():
|
||||
args = get_args()
|
||||
|
||||
if args.output:
|
||||
args.output = Path(args.output)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if args.ssml:
|
||||
headers = {"Content-Type": "application/ssml+xml"}
|
||||
else:
|
||||
headers = {"Content-Type": "text/plain"}
|
||||
|
||||
params: typing.Dict[str, str] = {}
|
||||
|
||||
if args.voice:
|
||||
params["voice"] = args.voice
|
||||
|
||||
if args.length_scale:
|
||||
params["lengthScale"] = args.length_scale
|
||||
|
||||
if args.noise_scale:
|
||||
params["noiseScale"] = args.noise_scale
|
||||
|
||||
if args.noise_w:
|
||||
params["noiseW"] = args.noise_w
|
||||
|
||||
if args.text:
|
||||
data = "\n".join(args.text)
|
||||
else:
|
||||
if os.isatty(sys.stdin.fileno()):
|
||||
print("Reading text from stdin...", file=sys.stderr)
|
||||
|
||||
data = sys.stdin.read()
|
||||
|
||||
wav_bytes = requests.post(
|
||||
args.url, headers=headers, params=params, data=data
|
||||
).content
|
||||
|
||||
if args.output:
|
||||
args.output.write_bytes(wav_bytes)
|
||||
_LOGGER.info("Wrote WAV data to %s", args.output)
|
||||
elif args.stdout:
|
||||
_LOGGER.debug("Writing WAV data to stdout")
|
||||
sys.stdout.buffer.write(wav_bytes)
|
||||
else:
|
||||
from playsound import playsound
|
||||
|
||||
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 get_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(prog=_PACKAGE)
|
||||
parser.add_argument(
|
||||
"text", nargs="*", help="Text to convert to speech (default: stdin)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--url",
|
||||
"-u",
|
||||
default="http://localhost:59125/api/tts",
|
||||
help="URL of mimic3 HTTP server (default: http://localhost:59125/api/tts)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--voice",
|
||||
"-v",
|
||||
help="Name of voice (expected in <voices-dir>/<language>)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
"-o",
|
||||
help="Path to write WAV file (default: play audio)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--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",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--length-scale",
|
||||
type=float,
|
||||
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",
|
||||
)
|
||||
parser.add_argument("--ssml", action="store_true", help="Input text is SSML")
|
||||
parser.add_argument(
|
||||
"--debug", action="store_true", help="Print DEBUG messages to the console"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
mimic3-tts<1.0
|
||||
playsound~=1.3.0
|
||||
quart>=0.16,<1.0
|
||||
quart-cors
|
||||
requests>=2,<3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue