Add an asyncio tcp server
ci/woodpecker/push/build Pipeline failed
Details
ci/woodpecker/push/build Pipeline failed
Details
This commit is contained in:
parent
4590e5b4e7
commit
f98b0f6fb1
|
@ -19,8 +19,19 @@ This module contains all the CLI subcommands and interfaces that Meissa
|
||||||
provides for user interaction
|
provides for user interaction
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import yaml
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
|
from meissa import utils
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_MODEL = (
|
||||||
|
"https://coqui.gateway.scarf.sh/english/coqui/v1.0.0-huge-vocab/model.tflite"
|
||||||
|
)
|
||||||
|
DEFAULT_SCORER = "https://coqui.gateway.scarf.sh/english/coqui/v1.0.0-huge-vocab/huge-vocabulary.scorer"
|
||||||
|
|
||||||
|
|
||||||
@click.group("CLI")
|
@click.group("CLI")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
|
@ -30,8 +41,27 @@ def cli(ctx):
|
||||||
|
|
||||||
@cli.command(help="Seutp the environment")
|
@cli.command(help="Seutp the environment")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def setup(ctx, f, packages, arch):
|
def setup(ctx):
|
||||||
click.echo("here")
|
config = utils.config(ctx)
|
||||||
|
home = utils.home(ctx)
|
||||||
|
|
||||||
|
model = config.get("model", DEFAULT_MODEL)
|
||||||
|
scorer = config.get("scorer", DEFAULT_SCORER)
|
||||||
|
|
||||||
|
# TODO: Add support for multiple profiles
|
||||||
|
utils.info(f"Downloading the model: {model}")
|
||||||
|
utils.download(model, home, "model")
|
||||||
|
utils.info(f"Downloading the scorer: {scorer}")
|
||||||
|
utils.download(scorer, home, "scorer")
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(help="Starts the Meissa server")
|
||||||
|
@click.pass_context
|
||||||
|
def start(ctx):
|
||||||
|
try:
|
||||||
|
asyncio.run(start(config, synth(conf)))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -39,13 +69,21 @@ def main():
|
||||||
The main entry point for Faraday
|
The main entry point for Faraday
|
||||||
"""
|
"""
|
||||||
user_home = os.environ.get("HOME")
|
user_home = os.environ.get("HOME")
|
||||||
meissa_home = os.path.join(user_home, ".meissa")
|
meissa_home = Path(user_home) / ".meissa"
|
||||||
|
|
||||||
os.makedirs(meissa_home, exist_ok=True)
|
meissa_home.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
config_path = meissa_home / "config.yml"
|
||||||
|
|
||||||
|
config = {}
|
||||||
|
if config_path.exists():
|
||||||
|
with open(config_path, "r") as stream:
|
||||||
|
config = yaml.safe_load(stream)
|
||||||
|
|
||||||
cli(
|
cli(
|
||||||
obj={
|
obj={
|
||||||
"home": meissa_home,
|
"home": meissa_home,
|
||||||
|
"config": config,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Meissa - A trainable and simple text to speech server
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# pylint: disable=redefined-outer-name, unused-argument
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
import simpleaudio as sa
|
||||||
|
|
||||||
|
from TTS.utils.manage import ModelManager
|
||||||
|
from TTS.utils.synthesizer import Synthesizer
|
||||||
|
|
||||||
|
from meissa import utils
|
||||||
|
|
||||||
|
|
||||||
|
def to_wav_data(wav):
|
||||||
|
wav_norm = np.array(wav) * (32767 / max(0.01, np.max(np.abs(wav))))
|
||||||
|
return wav_norm.astype(np.int16)
|
||||||
|
|
||||||
|
|
||||||
|
def play(wav):
|
||||||
|
try:
|
||||||
|
play = sa.play_buffer(to_wav_data(wav), 1, 2, 22050)
|
||||||
|
# Wait for audio playback to finish before exiting
|
||||||
|
play.wait_done()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
play.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def synth(ctx):
|
||||||
|
path = Path(__file__).parent / ".models.json"
|
||||||
|
manager = ModelManager(path)
|
||||||
|
|
||||||
|
language_ids_file_path = None
|
||||||
|
vocoder_path = None
|
||||||
|
vocoder_config_path = None
|
||||||
|
encoder_path = None
|
||||||
|
encoder_config_path = None
|
||||||
|
|
||||||
|
model_path, config_path, model_item = manager.download_model(
|
||||||
|
ctx.get("MODEL_NAME", "tts_models/en/ljspeech/tacotron2-DDC")
|
||||||
|
)
|
||||||
|
vocoder_name = model_item["default_vocoder"]
|
||||||
|
vocoder_path, vocoder_config_path, _ = manager.download_model(vocoder_name)
|
||||||
|
speaker_idx = ctx.get("SPEAKER_IDX")
|
||||||
|
|
||||||
|
# load models
|
||||||
|
synthesizer = Synthesizer(
|
||||||
|
model_path,
|
||||||
|
config_path,
|
||||||
|
None,
|
||||||
|
language_ids_file_path,
|
||||||
|
vocoder_path,
|
||||||
|
vocoder_config_path,
|
||||||
|
encoder_path,
|
||||||
|
encoder_config_path,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def tcp_handler(reader, writer):
|
||||||
|
while True:
|
||||||
|
# Read till EOF
|
||||||
|
data = await reader.read(4)
|
||||||
|
msg_len = int.from_bytes(data, "big")
|
||||||
|
|
||||||
|
if msg_len != 0:
|
||||||
|
data = await reader.read(msg_len)
|
||||||
|
message = data.decode()
|
||||||
|
|
||||||
|
print(f"Received {message!r}")
|
||||||
|
|
||||||
|
if message == "//close":
|
||||||
|
break
|
||||||
|
|
||||||
|
wav = synthesizer.tts(message, speaker_idx, "None", None)
|
||||||
|
synthesizer.save_wav(wav, "/tmp/blah.wav")
|
||||||
|
play(wav)
|
||||||
|
|
||||||
|
writer.write(b"Ok")
|
||||||
|
await writer.drain()
|
||||||
|
writer.close()
|
||||||
|
|
||||||
|
return tcp_handler
|
||||||
|
|
||||||
|
|
||||||
|
async def start(ctx, fn):
|
||||||
|
"""
|
||||||
|
Start a TCP socket and pass the given connection handler function `fn`
|
||||||
|
to it. It uses the `host` and `port` in the config file for the server.
|
||||||
|
"""
|
||||||
|
host = utils.config(ctx).get("host", "127.0.0.1")
|
||||||
|
port = utils.config(ctx).get("port", 6666)
|
||||||
|
|
||||||
|
server = await asyncio.start_server(fn, host, port)
|
||||||
|
|
||||||
|
addrs = ", ".join(str(sock.getsockname()) for sock in server.sockets)
|
||||||
|
|
||||||
|
utils.info(f"Serving on {addrs}")
|
||||||
|
|
||||||
|
async with server:
|
||||||
|
await server.serve_forever()
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Meissa - A trainable and simple text to speech server
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
def log(level: str, msg: str):
|
||||||
|
click.echo(f"[{level}]: {msg}")
|
||||||
|
|
||||||
|
|
||||||
|
def info(msg: str):
|
||||||
|
log(click.style("INFO", fg="green"), msg)
|
||||||
|
|
||||||
|
|
||||||
|
def warn(msg: str):
|
||||||
|
log(click.style("WARN", fg="yellow"), msg)
|
||||||
|
|
||||||
|
|
||||||
|
def error(msg: str):
|
||||||
|
log(click.style("ERR", fg="red"), msg)
|
||||||
|
|
||||||
|
|
||||||
|
def home(ctx) -> Path:
|
||||||
|
return Path(ctx.obj["home"])
|
||||||
|
|
||||||
|
|
||||||
|
def config(ctx) -> dict:
|
||||||
|
return ctx.obj["config"]
|
||||||
|
|
||||||
|
|
||||||
|
def download(url: str, path: Path, fname: str = None) -> Path:
|
||||||
|
local_filename = url.split("/")[-1] if not fname else fname
|
||||||
|
|
||||||
|
output = path / local_filename
|
||||||
|
with requests.get(url, stream=True, timeout=120) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
with open(output, "wb") as f:
|
||||||
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
|
if chunk:
|
||||||
|
f.write(chunk)
|
||||||
|
return output
|
|
@ -2723,6 +2723,23 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-g
|
||||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simpleaudio"
|
||||||
|
version = "1.0.4"
|
||||||
|
description = "Simple, asynchronous audio playback for Python 3."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "simpleaudio-1.0.4-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:05b63da515f5fc7c6f40e4d9673d22239c5e03e2bda200fc09fd21c185d73713"},
|
||||||
|
{file = "simpleaudio-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:f1a4fe3358429b2ea3181fd782e4c4fff5c123ca86ec7fc29e01ee9acd8a227a"},
|
||||||
|
{file = "simpleaudio-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:86f1b0985629852afe67259ac6c24905ca731cb202a6e96b818865c56ced0c27"},
|
||||||
|
{file = "simpleaudio-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f68820297ad51577e3a77369e7e9b23989d30d5ae923bf34c92cf983c04ade04"},
|
||||||
|
{file = "simpleaudio-1.0.4-cp38-cp38-win32.whl", hash = "sha256:67348e3d3ccbae73bd126beed7f1e242976889620dbc6974c36800cd286430fc"},
|
||||||
|
{file = "simpleaudio-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:f346a4eac9cdbb1b3f3d0995095b7e86c12219964c022f4d920c22f6ca05fb4c"},
|
||||||
|
{file = "simpleaudio-1.0.4.tar.gz", hash = "sha256:691c88649243544db717e7edf6a9831df112104e1aefb5f6038a5d071e8cf41d"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -3424,4 +3441,4 @@ multidict = ">=4.0"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.10,<3.11"
|
python-versions = ">=3.10,<3.11"
|
||||||
content-hash = "bc0667c6cd290a2486cc1ffa2e92374d97f68692666b1559be91634de602e0ee"
|
content-hash = "8d72955bdf3e95e400eb660e7f877c3e2abd29e209a8864979cc0981ad0fda3a"
|
||||||
|
|
|
@ -11,6 +11,7 @@ python = ">=3.10,<3.11"
|
||||||
tts = "^0.13.3"
|
tts = "^0.13.3"
|
||||||
click = "^8.1.3"
|
click = "^8.1.3"
|
||||||
msgpack = "^1.0.5"
|
msgpack = "^1.0.5"
|
||||||
|
simpleaudio = "^1.0.4"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|
Loading…
Reference in New Issue