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 @@
+
情绪: