Files
api/api_chat/before/sovits_api.py
T
2025-01-12 06:15:15 +00:00

180 lines
5.7 KiB
Python

import os
import soundfile as sf
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from pydantic import BaseModel, Field
import uvicorn
import redis
import hashlib
import json
from kafka import KafkaProducer, KafkaConsumer
import threading
import time
from tools.i18n.i18n import I18nAuto
from GPT_SoVITS.inference_webui import change_gpt_weights, change_sovits_weights, get_tts_wav
from dotenv import load_dotenv
import os
import torch
# 加载 .env 文件
load_dotenv()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
# FastAPI configuration
app = FastAPI()
i18n = I18nAuto()
# CORS configuration
ALLOWED_ORIGINS = os.getenv('ALLOWED_ORIGINS').split(',')
# Redis configuration
REDIS_HOST = os.getenv('REDIS_HOST')
REDIS_PORT = int(os.getenv('REDIS_PORT'))
REDIS_DB = int(os.getenv('REDIS_TTS_DB'))
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD')
# Kafka configuration
KAFKA_BROKER = os.getenv('KAFKA_BROKER')
KAFKA_TOPIC = os.getenv('KAFKA_TTS_TOPIC')
# KAFKA_GROUP_ID = 'tts_group'
KAFKA_CONSUMER_THREADS = 1
# TTS configuration
GPT_MODEL_PATH = os.getenv('GPT_MODEL_PATH')
SOVITS_MODEL_PATH = os.getenv('SOVITS_MODEL_PATH')
REF_AUDIO_PATH = os.getenv('REF_AUDIO_PATH')
REF_TEXT_PATH = os.getenv('REF_TEXT_PATH')
REF_LANGUAGE = os.getenv('REF_LANGUAGE')
TARGET_LANGUAGE = os.getenv('TARGET_LANGUAGE')
OUTPUT_PATH = os.getenv('OUTPUT_PATH')
# Initialize FastAPI CORS
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize Redis client
redis_client = redis.Redis(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
password=REDIS_PASSWORD
)
# Initialize Kafka producer
kafka_producer = KafkaProducer(bootstrap_servers=KAFKA_BROKER)
class TTSRequest(BaseModel):
text: str = Field(..., alias="text")
def get_audio_hash(text):
return hashlib.md5(text.encode()).hexdigest()
# Initialize models at startup
print("Initializing models...")
change_gpt_weights(gpt_path=GPT_MODEL_PATH)
change_sovits_weights(sovits_path=SOVITS_MODEL_PATH)
# Read reference text
with open(REF_TEXT_PATH, 'r', encoding='utf-8') as file:
ref_text = file.read()
print("Models initialized successfully.")
def synthesize(target_text, output_path):
# Synthesize audio
with torch.cuda.device(device):
synthesis_result = get_tts_wav(ref_wav_path=REF_AUDIO_PATH,
prompt_text=ref_text,
prompt_language=i18n(REF_LANGUAGE),
text=target_text,
text_language=i18n(TARGET_LANGUAGE), top_p=1, temperature=1)
result_list = list(synthesis_result)
if result_list:
last_sampling_rate, last_audio_data = result_list[-1]
audio_hash = get_audio_hash(target_text)
output_wav_path = os.path.join(output_path, f"{audio_hash}.wav")
sf.write(output_wav_path, last_audio_data, last_sampling_rate)
return output_wav_path
else:
return None
@app.post("/tts")
async def synthesize_audio(request: TTSRequest):
try:
print(f"Received TTS request: {request.dict()}")
target_text = request.text
audio_hash = get_audio_hash(target_text)
# Check Redis cache
cached_audio = redis_client.get(audio_hash)
if cached_audio:
audio_info = json.loads(cached_audio)
return FileResponse(audio_info['path'], media_type="audio/wav")
# Check file system
file_path = os.path.join(OUTPUT_PATH, f"{audio_hash}.wav")
if os.path.exists(file_path):
# Cache the file path in Redis
redis_client.set(audio_hash, json.dumps({"path": file_path}))
return FileResponse(file_path, media_type="audio/wav")
# Send message to Kafka
kafka_producer.send(KAFKA_TOPIC, json.dumps({
'text': target_text,
'audio_hash': audio_hash
}).encode('utf-8'))
# Wait for the audio to be generated (you might want to implement a more sophisticated waiting mechanism)
for _ in range(60): # Wait for up to 30 seconds
if os.path.exists(file_path):
return FileResponse(file_path, media_type="audio/wav")
time.sleep(1)
# If audio is not generated within the timeout
raise HTTPException(status_code=504, detail="Audio generation timed out")
except Exception as e:
print(f"Error processing TTS request: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/")
async def root():
return {"message": "TTS API is running"}
def kafka_consumer_thread():
consumer = KafkaConsumer(
KAFKA_TOPIC,
bootstrap_servers=KAFKA_BROKER,
# group_id=KAFKA_GROUP_ID,
auto_offset_reset='latest',
value_deserializer=lambda m: json.loads(m.decode('utf-8'))
)
for message in consumer:
target_text = message.value['text']
audio_hash = message.value['audio_hash']
output_path = synthesize(target_text, OUTPUT_PATH)
if output_path:
redis_client.set(audio_hash, json.dumps({"path": output_path}))
print(f"Audio synthesized successfully: {output_path}")
else:
print("Failed to synthesize audio")
if __name__ == "__main__":
# Start Kafka consumer threads
torch.cuda.set_device(device)
for _ in range(KAFKA_CONSUMER_THREADS):
consumer_thread = threading.Thread(target=kafka_consumer_thread)
consumer_thread.start()
uvicorn.run(app, host="0.0.0.0", port=6002)