diff --git a/backend/app/routes/tts_synthesize.py b/backend/app/routes/tts_synthesize.py index e4b2c1b..348af9f 100644 --- a/backend/app/routes/tts_synthesize.py +++ b/backend/app/routes/tts_synthesize.py @@ -4,6 +4,7 @@ import os from app.services.minimax_service import synthesize_minimax from app.services.cosyvoice_service import synthesize_cosyvoice from app.services.doubao_service import synthesize_doubao +from app.services.fish_audio_service import synthesize_fish_audio from app.routes.auth import login_required from app.db import get_db @@ -43,6 +44,11 @@ def synthesize(): text=text, speed=speed, pitch=pitch, volume=volume, emotion=emotion ) + elif engine == 'fish_audio': + audio_data = synthesize_fish_audio( + text=text, speed=speed, pitch=pitch, + volume=volume, emotion=emotion + ) else: audio_data = synthesize_minimax( text=text, speed=speed, pitch=pitch, diff --git a/backend/app/services/fish_audio_service.py b/backend/app/services/fish_audio_service.py new file mode 100644 index 0000000..5bde26d --- /dev/null +++ b/backend/app/services/fish_audio_service.py @@ -0,0 +1,46 @@ +import os +import requests + + +def synthesize_fish_audio(text, speed=1.0, pitch=0, volume=1.0, emotion='neutral'): + api_key = os.getenv('FISH_AUDIO_API_KEY', '') + if not api_key: + raise ValueError('FISH_AUDIO_API_KEY未设置') + + voice_id = os.getenv('FISH_AUDIO_VOICE_ID', '34e841f0f4ff44aca6e396debf2776d4') + + emotion_tags = { + 'happy': '[happy]', + 'sad': '[sad]', + 'angry': '[angry]', + 'surprised': '[excited]', + 'fearful': '[nervous]', + } + tag = emotion_tags.get(emotion, '') + if tag: + text = f"{tag}{text}" + + url = 'https://api.fish.audio/v1/tts' + headers = { + 'Authorization': f'Bearer {api_key}', + 'Content-Type': 'application/json' + } + + payload = { + 'text': text, + 'reference_id': voice_id, + 'format': 'mp3', + 'speed': max(0.5, min(2.0, speed)), + 'volume': max(-20, min(20, (volume - 1.0) * 20)), + } + + response = requests.post(url, json=payload, headers=headers, timeout=60, stream=True) + + if response.status_code != 200: + raise Exception(f'Fish Audio API错误: {response.status_code} - {response.text[:200]}') + + audio_bytes = response.content + if not audio_bytes: + raise Exception('Fish Audio返回空音频') + + return audio_bytes diff --git a/frontend/app.js b/frontend/app.js index b9e9cca..4e898bd 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -22,6 +22,11 @@ const ENGINE_CONFIG = { name: '豆包', emotionMode: 'global', color: '#7c5cfc' + }, + fish_audio: { + name: 'Fish Audio', + emotionMode: 'global', + color: '#f59e0b' } }; diff --git a/frontend/index.html b/frontend/index.html index 38ccadc..0479fff 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -41,6 +41,7 @@ + 情绪: