commit
b7126f091c
@ -0,0 +1,38 @@
|
||||
# This is the parameter configuration file for TTS server.
|
||||
|
||||
##################################################################
|
||||
# TTS SERVER SETTING #
|
||||
##################################################################
|
||||
host: '0.0.0.0'
|
||||
port: 8692
|
||||
|
||||
##################################################################
|
||||
# ACOUSTIC MODEL SETTING #
|
||||
# am choices=['speedyspeech_csmsc', 'fastspeech2_csmsc',
|
||||
# 'fastspeech2_ljspeech', 'fastspeech2_aishell3',
|
||||
# 'fastspeech2_vctk']
|
||||
##################################################################
|
||||
am: 'fastspeech2_csmsc'
|
||||
am_config:
|
||||
am_ckpt:
|
||||
am_stat:
|
||||
phones_dict:
|
||||
tones_dict:
|
||||
speaker_dict:
|
||||
spk_id: 0
|
||||
|
||||
##################################################################
|
||||
# VOCODER SETTING #
|
||||
# voc choices=['pwgan_csmsc', 'pwgan_ljspeech', 'pwgan_aishell3',
|
||||
# 'pwgan_vctk', 'mb_melgan_csmsc']
|
||||
##################################################################
|
||||
voc: 'pwgan_csmsc'
|
||||
voc_config:
|
||||
voc_ckpt:
|
||||
voc_stat:
|
||||
|
||||
##################################################################
|
||||
# OTHERS #
|
||||
##################################################################
|
||||
lang: 'zh'
|
||||
device: paddle.get_device()
|
@ -0,0 +1,156 @@
|
||||
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import argparse
|
||||
import base64
|
||||
|
||||
import librosa
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
import yaml
|
||||
from engine.base_engine import BaseEngine
|
||||
|
||||
from paddlespeech.cli.log import logger
|
||||
from paddlespeech.cli.tts.infer import TTSExecutor
|
||||
from utils.errors import ErrorCode
|
||||
from utils.exception import ServerBaseException
|
||||
|
||||
__all__ = ['TTSEngine']
|
||||
|
||||
|
||||
class TTSServerExecutor(TTSExecutor):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog='paddlespeech.tts', add_help=True)
|
||||
self.parser.add_argument(
|
||||
'--conf',
|
||||
type=str,
|
||||
default='./conf/tts/tts.yaml',
|
||||
help='Configuration parameters.')
|
||||
|
||||
|
||||
class TTSEngine(BaseEngine):
|
||||
"""TTS server engine
|
||||
|
||||
Args:
|
||||
metaclass: Defaults to Singleton.
|
||||
"""
|
||||
|
||||
def __init__(self, name=None):
|
||||
"""Initialize TTS server engine
|
||||
"""
|
||||
super(TTSEngine, self).__init__()
|
||||
self.executor = TTSServerExecutor()
|
||||
|
||||
config_path = self.executor.parser.parse_args().conf
|
||||
with open(config_path, 'rt') as f:
|
||||
self.conf_dict = yaml.safe_load(f)
|
||||
|
||||
self.executor._init_from_path(
|
||||
am=self.conf_dict["am"],
|
||||
am_config=self.conf_dict["am_config"],
|
||||
am_ckpt=self.conf_dict["am_ckpt"],
|
||||
am_stat=self.conf_dict["am_stat"],
|
||||
phones_dict=self.conf_dict["phones_dict"],
|
||||
tones_dict=self.conf_dict["tones_dict"],
|
||||
speaker_dict=self.conf_dict["speaker_dict"],
|
||||
voc=self.conf_dict["voc"],
|
||||
voc_config=self.conf_dict["voc_config"],
|
||||
voc_ckpt=self.conf_dict["voc_ckpt"],
|
||||
voc_stat=self.conf_dict["voc_stat"],
|
||||
lang=self.conf_dict["lang"])
|
||||
|
||||
logger.info("Initialize TTS server engine successfully.")
|
||||
|
||||
def postprocess(self,
|
||||
wav,
|
||||
original_fs: int,
|
||||
target_fs: int=16000,
|
||||
volume: float=1.0,
|
||||
speed: float=1.0,
|
||||
audio_path: str=None,
|
||||
audio_format: str="wav"):
|
||||
"""Post-processing operations, including speech, volume, sample rate, save audio file
|
||||
|
||||
Args:
|
||||
wav (numpy(float)): Synthesized audio sample points
|
||||
original_fs (int): original audio sample rate
|
||||
target_fs (int): target audio sample rate
|
||||
volume (float): target volume
|
||||
speed (float): target speed
|
||||
"""
|
||||
|
||||
# transform sample_rate
|
||||
if target_fs == 0 or target_fs > original_fs:
|
||||
target_fs = original_fs
|
||||
wav_tar_fs = wav
|
||||
else:
|
||||
wav_tar_fs = librosa.resample(
|
||||
np.squeeze(wav), original_fs, target_fs)
|
||||
|
||||
# transform volume
|
||||
wav_vol = wav_tar_fs * volume
|
||||
|
||||
# transform speed
|
||||
# TODO
|
||||
target_wav = wav_vol.reshape(-1, 1)
|
||||
|
||||
# save audio
|
||||
if audio_path is not None:
|
||||
sf.write(audio_path, target_wav, target_fs)
|
||||
logger.info('Wave file has been generated: {}'.format(audio_path))
|
||||
|
||||
# wav to base64
|
||||
base64_bytes = base64.b64encode(target_wav)
|
||||
base64_string = base64_bytes.decode('utf-8')
|
||||
wav_base64 = base64_string
|
||||
|
||||
return target_fs, wav_base64
|
||||
|
||||
def run(self,
|
||||
sentence: str,
|
||||
spk_id: int=0,
|
||||
speed: float=1.0,
|
||||
volume: float=1.0,
|
||||
sample_rate: int=0,
|
||||
save_path: str=None,
|
||||
audio_format: str="wav"):
|
||||
|
||||
lang = self.conf_dict["lang"]
|
||||
|
||||
try:
|
||||
self.executor.infer(
|
||||
text=sentence,
|
||||
lang=lang,
|
||||
am=self.conf_dict["am"],
|
||||
spk_id=spk_id)
|
||||
except:
|
||||
raise ServerBaseException(ErrorCode.SERVER_INTERNAL_ERR,
|
||||
"tts infer failed.")
|
||||
|
||||
try:
|
||||
target_sample_rate, wav_base64 = self.postprocess(
|
||||
wav=self.executor._outputs['wav'].numpy(),
|
||||
original_fs=self.executor.am_config.fs,
|
||||
target_fs=sample_rate,
|
||||
volume=volume,
|
||||
speed=speed,
|
||||
audio_path=save_path,
|
||||
audio_format=audio_format)
|
||||
except:
|
||||
raise ServerBaseException(ErrorCode.SERVER_INTERNAL_ERR,
|
||||
"tts postprocess failed.")
|
||||
|
||||
return lang, target_sample_rate, wav_base64
|
@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import json
|
||||
from enum import IntEnum
|
||||
|
||||
from fastapi import Response
|
||||
|
||||
|
||||
class ErrorCode(IntEnum):
|
||||
SERVER_OK = 200 # success.
|
||||
|
||||
SERVER_PARAM_ERR = 400 # Input parameters are not valid.
|
||||
SERVER_TASK_NOT_EXIST = 404 # Task is not exist.
|
||||
|
||||
SERVER_INTERNAL_ERR = 500 # Internal error.
|
||||
SERVER_NETWORK_ERR = 502 # Network exception.
|
||||
SERVER_UNKOWN_ERR = 509 # Unknown error occurred.
|
||||
|
||||
|
||||
ErrorMsg = {
|
||||
ErrorCode.SERVER_OK: "success.",
|
||||
ErrorCode.SERVER_PARAM_ERR: "Input parameters are not valid.",
|
||||
ErrorCode.SERVER_TASK_NOT_EXIST: "Task is not exist.",
|
||||
ErrorCode.SERVER_INTERNAL_ERR: "Internal error.",
|
||||
ErrorCode.SERVER_NETWORK_ERR: "Network exception.",
|
||||
ErrorCode.SERVER_UNKOWN_ERR: "Unknown error occurred."
|
||||
}
|
||||
|
||||
|
||||
def failed_response(code, msg=""):
|
||||
"""Interface call failure response
|
||||
|
||||
Args:
|
||||
code (int): error code number
|
||||
msg (str, optional): Interface call failure information. Defaults to "".
|
||||
|
||||
Returns:
|
||||
Response (json): failure json information.
|
||||
"""
|
||||
|
||||
if not msg:
|
||||
msg = ErrorMsg.get(code, "Unknown error occurred.")
|
||||
|
||||
res = {"success": False, "code": int(code), "message": {"global": msg}}
|
||||
|
||||
return Response(content=json.dumps(res), media_type="application/json")
|
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import traceback
|
||||
|
||||
from utils.errors import ErrorMsg
|
||||
|
||||
|
||||
class ServerBaseException(Exception):
|
||||
""" Server Base exception
|
||||
"""
|
||||
|
||||
def __init__(self, error_code, msg=None):
|
||||
#if msg:
|
||||
#log.error(msg)
|
||||
msg = msg if msg else ErrorMsg.get(error_code, "")
|
||||
super(ServerBaseException, self).__init__(error_code, msg)
|
||||
self.error_code = error_code
|
||||
self.msg = msg
|
||||
traceback.print_exc()
|
Loading…
Reference in new issue