CoolFace
Apppublic

lenML/ChatTTS-Forge

sourceHugging Faceagpl-3.0updated 2y agoView on Hugging Face
301likes
tts_api.py170 linesDownload Raw Back to impl
1import logging2 3from fastapi import Depends, HTTPException, Query4from fastapi.responses import FileResponse, StreamingResponse5from pydantic import BaseModel6 7from modules.api import utils as api_utils8from modules.api.Api import APIManager9from 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 Speaker14 15logger = logging.getLogger(__name__)16 17 18class TTSParams(BaseModel):19    text: str = Query(..., description="Text to synthesize")20    spk: str = Query(21        "female2", description="Specific speaker by speaker name or speaker seed"22    )23    style: str = Query("chat", description="Specific style by style name")24    temperature: float = Query(25        0.3, description="Temperature for sampling (may be overridden by style or spk)"26    )27    top_p: float = Query(28        0.5, description="Top P for sampling (may be overridden by style or spk)"29    )30    top_k: int = Query(31        20, description="Top K for sampling (may be overridden by style or spk)"32    )33    seed: int = Query(34        42, description="Seed for generate (may be overridden by style or spk)"35    )36    format: str = Query("mp3", description="Response audio format: [mp3,wav]")37    prompt1: str = Query("", description="Text prompt for inference")38    prompt2: str = Query("", description="Text prompt for inference")39    prefix: str = Query("", description="Text prefix for inference")40    bs: str = Query("8", description="Batch size for inference")41    thr: str = Query("100", description="Threshold for sentence spliter")42    eos: str = Query("[uv_break]", description="End of sentence str")43 44    enhance: bool = Query(False, description="Enable enhancer")45    denoise: bool = Query(False, description="Enable denoiser")46 47    speed: float = Query(1.0, description="Speed of the audio")48    pitch: float = Query(0, description="Pitch of the audio")49    volume_gain: float = Query(0, description="Volume gain of the audio")50 51    stream: bool = Query(False, description="Stream the audio")52 53 54async def synthesize_tts(params: TTSParams = Depends()):55    try:56        # Validate text57        if not params.text.strip():58            raise HTTPException(59                status_code=422, detail="Text parameter cannot be empty"60            )61 62        # Validate temperature63        if not (0 <= params.temperature <= 1):64            raise HTTPException(65                status_code=422, detail="Temperature must be between 0 and 1"66            )67 68        # Validate top_p69        if not (0 <= params.top_p <= 1):70            raise HTTPException(status_code=422, detail="top_p must be between 0 and 1")71 72        # Validate top_k73        if params.top_k <= 0:74            raise HTTPException(75                status_code=422, detail="top_k must be a positive integer"76            )77        if params.top_k > 100:78            raise HTTPException(79                status_code=422, detail="top_k must be less than or equal to 100"80            )81 82        # Validate format83        if params.format not in ["mp3", "wav"]:84            raise HTTPException(85                status_code=422,86                detail="Invalid format. Supported formats are mp3 and wav",87            )88 89        calc_params = api_utils.calc_spk_style(spk=params.spk, style=params.style)90 91        spk = calc_params.get("spk", params.spk)92        if not isinstance(spk, Speaker):93            raise HTTPException(status_code=422, detail="Invalid speaker")94 95        style = calc_params.get("style", params.style)96        seed = params.seed or calc_params.get("seed", params.seed)97        temperature = params.temperature or calc_params.get(98            "temperature", params.temperature99        )100        prefix = params.prefix or calc_params.get("prefix", params.prefix)101        prompt1 = params.prompt1 or calc_params.get("prompt1", params.prompt1)102        prompt2 = params.prompt2 or calc_params.get("prompt2", params.prompt2)103        eos = params.eos or ""104 105        batch_size = int(params.bs)106        threshold = int(params.thr)107 108        tts_config = ChatTTSConfig(109            style=style,110            temperature=temperature,111            top_k=params.top_k,112            top_p=params.top_p,113            prefix=prefix,114            prompt1=prompt1,115            prompt2=prompt2,116        )117        infer_config = InferConfig(118            batch_size=batch_size,119            spliter_threshold=threshold,120            eos=eos,121            seed=seed,122        )123        adjust_config = AdjustConfig(124            pitch=params.pitch,125            speed_rate=params.speed,126            volume_gain_db=params.volume_gain,127        )128        enhancer_config = EnhancerConfig(129            enabled=params.enhance or params.denoise or False,130            lambd=0.9 if params.denoise else 0.1,131        )132 133        handler = TTSHandler(134            text_content=params.text,135            spk=spk,136            tts_config=tts_config,137            infer_config=infer_config,138            adjust_config=adjust_config,139            enhancer_config=enhancer_config,140        )141        media_type = f"audio/{params.format}"142        if params.format == "mp3":143            media_type = "audio/mpeg"144 145        if params.stream:146            if infer_config.batch_size != 1:147                # 流式生成下仅支持 batch size 为 1,当前请求参数将被忽略148                logger.warning(149                    f"Batch size {infer_config.batch_size} is not supported in streaming mode, will set to 1"150                )151 152            buffer_gen = handler.enqueue_to_stream(format=AudioFormat(params.format))153            return StreamingResponse(buffer_gen, media_type=media_type)154        else:155            buffer = handler.enqueue_to_buffer(format=AudioFormat(params.format))156            return StreamingResponse(buffer, media_type=media_type)157    except Exception as e:158        import logging159 160        logging.exception(e)161 162        if isinstance(e, HTTPException):163            raise e164        else:165            raise HTTPException(status_code=500, detail=str(e))166 167 168def setup(api_manager: APIManager):169    api_manager.get("/v1/tts", response_class=FileResponse)(synthesize_tts)170