Make running the server easier

pull/10/head
Reuben Morais 2019-11-27 16:40:32 +01:00
parent 649312fc78
commit d62234e06e
6 changed files with 130 additions and 44 deletions

View File

@ -1,9 +1,34 @@
## TTS example web-server
Steps to run:
1. Download one of the models given on the main page. Click [here](https://drive.google.com/drive/folders/1Q6BKeEkZyxSGsocK2p_mqgzLwlNvbHFJ?usp=sharing) for the lastest model.
2. Checkout the corresponding commit history or use ```server``` branch if you like to use the latest model.
3. Set the paths and the other options in the file ```server/conf.json```.
4. Run the server ```python server/server.py -c server/conf.json```. (Requires Flask)
5. Go to ```localhost:[given_port]``` and enjoy.
For high quality results, please use the library versions shown in the ```requirements.txt``` file.
You'll need a model package (Zip file, includes TTS Python wheel, model files, server configuration, and optional nginx/uwsgi configs). Publicly available models are listed [here](https://github.com/mozilla/TTS/wiki/Released-Models).
Instructions below are based on a Ubuntu 18.04 machine, but it should be simple to adapt the package names to other distros if needed. Python 3.6 is recommended, as some of the dependencies' versions predate Python 3.7 and will force building from source, which requires extra dependencies and is not guaranteed to work.
Development server:
1. apt-get install -y espeak libsndfile1 python3-venv
2. python3 -m venv /tmp/venv
3. source /tmp/venv/bin/activate
4. pip install -U pip setuptools wheel
5. # Download model package
6. unzip model.zip
7. pip install -U ./TTS*.whl
8. python -m TTS.server.server
You can now browse to http://localhost:5002
Running with nginx/uwsgi:
1. apt-get install -y uwsgi uwsgi-plugin-python3 nginx espeak libsndfile1 python3-venv
2. python3 -m venv /tmp/venv
3. source /tmp/venv/bin/activate
4. pip install -U pip setuptools wheel
5. # Download model package
6. unzip model.zip
7. pip install -U ./TTS*.whl
8. cp tts_site_nginx /etc/nginx/sites-enabled/default
9. service nginx restart
10. uwsgi --ini uwsgi.ini
You can now browse to http://localhost:80 (edit the port in /etc/nginx/sites-enabled/tts_site_nginx).
Configure number of workers (number of requests that will be processed in parallel) in uwsgi.ini, `processes` setting.

View File

@ -1,17 +1,45 @@
#!flask/bin/python
import argparse
from synthesizer import Synthesizer
from TTS.utils.generic_utils import load_config
import os
from flask import Flask, request, render_template, send_file
from TTS.server.synthesizer import Synthesizer
def create_argparser():
def convert_boolean(x):
return x.lower() in ['true', '1', 'yes']
parser = argparse.ArgumentParser()
parser.add_argument('--tts_checkpoint', type=str, help='path to TTS checkpoint file')
parser.add_argument('--tts_config', type=str, help='path to TTS config.json file')
parser.add_argument('--tts_speakers', type=str, help='path to JSON file containing speaker ids, if speaker ids are used in the model')
parser.add_argument('--wavernn_lib_path', type=str, help='path to WaveRNN project folder to be imported. If this is not passed, model uses Griffin-Lim for synthesis.')
parser.add_argument('--wavernn_file', type=str, help='path to WaveRNN checkpoint file.')
parser.add_argument('--wavernn_config', type=str, help='path to WaveRNN config file.')
parser.add_argument('--is_wavernn_batched', type=convert_boolean, default=False, help='true to use batched WaveRNN.')
parser.add_argument('--port', type=int, default=5002, help='port to listen on.')
parser.add_argument('--use_cuda', type=convert_boolean, default=False, help='true to use CUDA.')
parser.add_argument('--debug', type=convert_boolean, default=False, help='true to enable Flask debug mode.')
return parser
config = None
synthesizer = None
embedded_model_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'model')
checkpoint_file = os.path.join(embedded_model_folder, 'checkpoint.pth.tar')
config_file = os.path.join(embedded_model_folder, 'config.json')
if os.path.isfile(checkpoint_file) and os.path.isfile(config_file):
# Use default config with embedded model files
config = create_argparser().parse_args([])
config.tts_checkpoint = checkpoint_file
config.tts_config = config_file
synthesizer = Synthesizer(config)
parser = argparse.ArgumentParser()
parser.add_argument(
'-c', '--config_path', type=str, help='path to config file for training')
args = parser.parse_args()
config = load_config(args.config_path)
app = Flask(__name__)
synthesizer = Synthesizer(config)
@app.route('/')
def index():
@ -27,4 +55,8 @@ def tts():
if __name__ == '__main__':
if not config or not synthesizer:
args = create_argparser().parse_args()
synthesizer = Synthesizer(args)
app.run(debug=config.debug, host='0.0.0.0', port=config.port)

View File

@ -24,19 +24,20 @@ class Synthesizer(object):
def __init__(self, config):
self.wavernn = None
self.config = config
self.use_cuda = config.use_cuda
self.use_cuda = self.config.use_cuda
if self.use_cuda:
assert torch.cuda.is_available(), "CUDA is not availabe on this machine."
self.load_tts(self.config.tts_path, self.config.tts_file, self.config.tts_config, config.use_cuda)
self.load_tts(self.config.tts_checkpoint, self.config.tts_config,
self.config.use_cuda)
if self.config.wavernn_lib_path:
self.load_wavernn(config.wavernn_lib_path, config.wavernn_path, config.wavernn_file, config.wavernn_config, config.use_cuda)
self.load_wavernn(self.config.wavernn_lib_path, self.config.wavernn_path,
self.config.wavernn_file, self.config.wavernn_config,
self.config.use_cuda)
def load_tts(self, model_path, model_file, model_config, use_cuda):
tts_config = os.path.join(model_path, model_config)
self.model_file = os.path.join(model_path, model_file)
def load_tts(self, tts_checkpoint, tts_config, use_cuda):
print(" > Loading TTS model ...")
print(" | > model config: ", tts_config)
print(" | > model file: ", model_file)
print(" | > checkpoint file: ", tts_checkpoint)
self.tts_config = load_config(tts_config)
self.use_phonemes = self.tts_config.use_phonemes
self.ap = AudioProcessor(**self.tts_config.audio)
@ -52,7 +53,8 @@ class Synthesizer(object):
num_speakers = 0
self.tts_model = setup_model(self.input_size, num_speakers=num_speakers, c=self.tts_config)
# load model state
cp = torch.load(self.model_file)
map_location = None if use_cuda else torch.device('cpu')
cp = torch.load(tts_checkpoint, map_location=map_location)
# load the model
self.tts_model.load_state_dict(cp['model'])
if use_cuda:

View File

@ -57,6 +57,7 @@
<div class="row">
<div class="col-lg-12 text-center">
<img class="mt-5" src="https://user-images.githubusercontent.com/1402048/52643646-c2102980-2edd-11e9-8c37-b72f3c89a640.png" alt=></img>
<h1 class="mt-5">Mozilla TTS</h1>
<ul class="list-unstyled">
</ul>
<input id="text" placeholder="Type here..." size=45 type="text" name="text">
@ -68,12 +69,10 @@
</div>
<!-- Bootstrap core JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"></script>
<script>
function q(selector) {return document.querySelector(selector)}
q('#text').focus()
q('#speak-button').addEventListener('click', function(e) {
function do_tts(e) {
text = q('#text').value
if (text) {
q('#message').textContent = 'Synthesizing...'
@ -83,6 +82,12 @@
}
e.preventDefault()
return false
}
q('#speak-button').addEventListener('click', do_tts)
q('#text').addEventListener('keyup', function(e) {
if (e.keyCode == 13) { // enter
do_tts(e)
}
})
function synthesize(text) {
fetch('/api/tts?text=' + encodeURIComponent(text), {cache: 'no-cache'})

8
setup.cfg Normal file
View File

@ -0,0 +1,8 @@
[build_py]
build-lib=temp_build
[bdist_wheel]
bdist-dir=temp_build
[install_lib]
build-dir=temp_build

View File

@ -1,10 +1,23 @@
#!/usr/bin/env python
import argparse
import os
import shutil
import subprocess
import sys
from setuptools import setup, find_packages
import setuptools.command.develop
import setuptools.command.build_py
import os
import subprocess
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--checkpoint', type=str, help='Path to checkpoint file to embed in wheel.')
parser.add_argument('--model_config', type=str, help='Path to model configuration file to embed in wheel.')
args, unknown_args = parser.parse_known_args()
# Remove our arguments from argv so that setuptools doesn't see them
sys.argv = [sys.argv[0]] + unknown_args
version = '0.0.1'
@ -42,20 +55,17 @@ class develop(setuptools.command.develop.develop):
setuptools.command.develop.develop.run(self)
def create_readme_rst():
try:
subprocess.check_call(
[
"pandoc", "--from=markdown", "--to=rst", "--output=README.rst",
"README.md"
],
cwd=cwd)
print("Generated README.rst from README.md using pandoc.")
except subprocess.CalledProcessError:
pass
except OSError:
pass
package_data = ['server/templates/*']
if 'bdist_wheel' in unknown_args and args.checkpoint and args.model_config:
print('Embedding model in wheel file...')
model_dir = os.path.join('server', 'model')
os.makedirs(model_dir, exist_ok=True)
embedded_checkpoint_path = os.path.join(model_dir, 'checkpoint.pth.tar')
shutil.copy(args.checkpoint, embedded_checkpoint_path)
embedded_config_path = os.path.join(model_dir, 'config.json')
shutil.copy(args.model_config, embedded_config_path)
package_data.extend([embedded_checkpoint_path, embedded_config_path])
setup(
name='TTS',
@ -65,6 +75,9 @@ setup(
license='MPL-2.0',
package_dir={'': 'tts_namespace'},
packages=find_packages('tts_namespace'),
package_data={
'TTS': package_data,
},
project_urls={
'Documentation': 'https://github.com/mozilla/TTS/wiki',
'Tracker': 'https://github.com/mozilla/TTS/issues',
@ -75,12 +88,13 @@ setup(
'build_py': build_py,
'develop': develop,
},
setup_requires=["numpy==1.15.4"],
install_requires=[
"scipy >=0.19.0",
"torch >= 0.4.1",
"scipy>=0.19.0",
"torch>=0.4.1",
"numpy==1.15.4",
"librosa==0.6.2",
"unidecode==0.4.20",
"attrdict",
"tensorboardX",
"matplotlib",
"Pillow",