lenML/ChatTTS-Forge
301
1from typing import Union2 3from fastapi import HTTPException4from pydantic import BaseModel5 6from modules.api import utils as api_utils7from modules.api.Api import APIManager8from modules.api.impl.handler.SSMLHandler import SSMLHandler9from modules.api.impl.handler.TTSHandler import TTSHandler10from modules.api.impl.model.audio_model import AdjustConfig, AudioFormat11from modules.api.impl.model.chattts_model import ChatTTSConfig, InferConfig12from modules.api.impl.model.enhancer_model import EnhancerConfig13from modules.speaker import Speaker, speaker_mgr14 15 16class SynthesisInput(BaseModel):17 text: Union[str, None] = None18 ssml: Union[str, None] = None19 20 21class VoiceSelectionParams(BaseModel):22 languageCode: str = "ZH-CN"23 24 name: str = "female2"25 style: str = ""26 temperature: float = 0.327 topP: float = 0.728 topK: int = 2029 seed: int = 4230 31 # end_of_sentence32 eos: str = "[uv_break]"33 34 35class AudioConfig(BaseModel):36 audioEncoding: AudioFormat = AudioFormat.mp337 speakingRate: float = 138 pitch: float = 039 volumeGainDb: float = 040 sampleRateHertz: int = 2400041 batchSize: int = 442 spliterThreshold: int = 10043 44 45class GoogleTextSynthesizeRequest(BaseModel):46 input: SynthesisInput47 voice: VoiceSelectionParams48 audioConfig: AudioConfig49 enhancerConfig: EnhancerConfig = None50 51 52class GoogleTextSynthesizeResponse(BaseModel):53 audioContent: str54 55 56async def google_text_synthesize(request: GoogleTextSynthesizeRequest):57 input = request.input58 voice = request.voice59 audioConfig = request.audioConfig60 enhancerConfig = request.enhancerConfig61 62 # 提取参数63 64 # TODO 这个也许应该传给 normalizer65 language_code = voice.languageCode66 voice_name = voice.name67 infer_seed = voice.seed or 4268 eos = voice.eos or "[uv_break]"69 audio_format = audioConfig.audioEncoding70 71 if not isinstance(audio_format, AudioFormat) and isinstance(audio_format, str):72 audio_format = AudioFormat(audio_format)73 74 speaking_rate = audioConfig.speakingRate or 175 pitch = audioConfig.pitch or 076 volume_gain_db = audioConfig.volumeGainDb or 077 78 batch_size = audioConfig.batchSize or 179 80 spliter_threshold = audioConfig.spliterThreshold or 10081 82 # TODO83 sample_rate = audioConfig.sampleRateHertz or 2400084 85 params = api_utils.calc_spk_style(spk=voice.name, style=voice.style)86 87 # 虽然 calc_spk_style 可以解析 seed 形式,但是这个接口只准备支持 speakers list 中存在的 speaker88 if speaker_mgr.get_speaker(voice_name) is None:89 raise HTTPException(90 status_code=422, detail="The specified voice name is not supported."91 )92 93 if not isinstance(params.get("spk"), Speaker):94 raise HTTPException(95 status_code=422, detail="The specified voice name is not supported."96 )97 98 speaker = params.get("spk")99 tts_config = ChatTTSConfig(100 style=params.get("style", ""),101 temperature=voice.temperature,102 top_k=voice.topK,103 top_p=voice.topP,104 )105 infer_config = InferConfig(106 batch_size=batch_size,107 spliter_threshold=spliter_threshold,108 eos=eos,109 seed=infer_seed,110 )111 adjust_config = AdjustConfig(112 speaking_rate=speaking_rate,113 pitch=pitch,114 volume_gain_db=volume_gain_db,115 )116 enhancer_config = enhancerConfig117 118 mime_type = f"audio/{audio_format.value}"119 if audio_format == AudioFormat.mp3:120 mime_type = "audio/mpeg"121 try:122 if input.text:123 text_content = input.text124 125 handler = TTSHandler(126 text_content=text_content,127 spk=speaker,128 tts_config=tts_config,129 infer_config=infer_config,130 adjust_config=adjust_config,131 enhancer_config=enhancer_config,132 )133 134 base64_string = handler.enqueue_to_base64(format=audio_format)135 return {"audioContent": f"data:{mime_type};base64,{base64_string}"}136 137 elif input.ssml:138 ssml_content = input.ssml139 140 handler = SSMLHandler(141 ssml_content=ssml_content,142 infer_config=infer_config,143 adjust_config=adjust_config,144 enhancer_config=enhancer_config,145 )146 147 base64_string = handler.enqueue_to_base64(format=audio_format)148 149 return {"audioContent": f"data:{mime_type};base64,{base64_string}"}150 151 else:152 raise HTTPException(153 status_code=422, detail="Invalid input text or ssml specified."154 )155 156 except Exception as e:157 import logging158 159 logging.exception(e)160 161 if isinstance(e, HTTPException):162 raise e163 else:164 raise HTTPException(status_code=500, detail=str(e))165 166 167def setup(app: APIManager):168 app.post(169 "/v1/text:synthesize",170 response_model=GoogleTextSynthesizeResponse,171 description="""172google api document: <br/>173[https://cloud.google.com/text-to-speech/docs/reference/rest/v1/text/synthesize](https://cloud.google.com/text-to-speech/docs/reference/rest/v1/text/synthesize)174 175- 多个属性在本系统中无用仅仅是为了兼容google api176- voice 中的 topP, topK, temperature 为本系统中的参数177- voice.name 即 speaker name (或者speaker seed)178- voice.seed 为 infer seed (可在webui中测试具体作用)179 180- 编码格式影响的是 audioContent 的二进制格式,所以所有format都是返回带有base64数据的json181 """,182 )(google_text_synthesize)183 