2022-04-15 14:35:30 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
# Orion Rigel --- Text to Speech engine
|
|
|
|
#
|
|
|
|
# Copyright (c) 2022 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, either version 2 of the License.
|
|
|
|
# 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
|
|
|
|
|
|
|
|
import asyncio
|
2022-04-15 16:45:21 +01:00
|
|
|
import numpy as np
|
2022-04-15 14:35:30 +01:00
|
|
|
# pylint: disable=redefined-outer-name, unused-argument
|
|
|
|
from pathlib import Path
|
|
|
|
|
2022-04-15 16:45:21 +01:00
|
|
|
import simpleaudio as sa
|
|
|
|
|
2022-04-15 14:35:30 +01:00
|
|
|
from TTS.utils.manage import ModelManager
|
|
|
|
from TTS.utils.synthesizer import Synthesizer
|
|
|
|
|
|
|
|
|
2022-04-15 16:45:21 +01:00
|
|
|
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):
|
|
|
|
|
2022-04-15 16:47:43 +01:00
|
|
|
try:
|
2022-04-15 16:45:21 +01:00
|
|
|
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()
|
|
|
|
|
|
|
|
|
2022-04-15 14:35:30 +01:00
|
|
|
def synth(config):
|
|
|
|
path = Path(__file__).parent / ".models.json"
|
|
|
|
manager = ModelManager(path)
|
|
|
|
|
|
|
|
model_name = config.get("MODEL_NAME", "tts_models/en/ljspeech/tacotron2-DDC")
|
|
|
|
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(model_name)
|
|
|
|
vocoder_name = model_item["default_vocoder"]
|
|
|
|
vocoder_path, vocoder_config_path, _ = manager.download_model(vocoder_name)
|
|
|
|
speaker_idx = config.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):
|
2022-04-15 23:13:21 +01:00
|
|
|
while True:
|
|
|
|
# Read till EOF
|
|
|
|
data = await reader.read(4)
|
|
|
|
msg_len = int.from_bytes(data, 'big')
|
2022-04-15 16:45:21 +01:00
|
|
|
|
2022-04-15 23:13:21 +01:00
|
|
|
if msg_len != 0:
|
|
|
|
data = await reader.read(msg_len)
|
|
|
|
message = data.decode()
|
2022-04-15 16:45:21 +01:00
|
|
|
|
2022-04-15 23:13:21 +01:00
|
|
|
print(f"Received {message!r}")
|
2022-04-15 14:35:30 +01:00
|
|
|
|
2022-04-15 23:13:21 +01:00
|
|
|
if message == "//close":
|
|
|
|
break
|
|
|
|
|
|
|
|
wav = synthesizer.tts(message, speaker_idx, "None", None)
|
2022-09-05 16:09:06 +01:00
|
|
|
synthesizer.save_wav(wav, "/tmp/blah.wav")
|
2022-04-15 23:13:21 +01:00
|
|
|
play(wav)
|
|
|
|
|
|
|
|
writer.write(b"Ok")
|
|
|
|
await writer.drain()
|
2022-04-15 14:35:30 +01:00
|
|
|
writer.close()
|
|
|
|
|
|
|
|
return tcp_handler
|
|
|
|
|
|
|
|
|
|
|
|
async def start(config, fn):
|
|
|
|
host = config.get('host', '127.0.0.1')
|
|
|
|
port = config.get('port', 6666)
|
|
|
|
server = await asyncio.start_server(fn, host, port)
|
|
|
|
|
|
|
|
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
|
|
|
|
print(f'Serving on {addrs}')
|
|
|
|
|
|
|
|
async with server:
|
|
|
|
await server.serve_forever()
|