diff --git a/backend/app/routes/tts_synthesize.py b/backend/app/routes/tts_synthesize.py index ffe5da2..e4b2c1b 100644 --- a/backend/app/routes/tts_synthesize.py +++ b/backend/app/routes/tts_synthesize.py @@ -3,6 +3,7 @@ import base64 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.routes.auth import login_required from app.db import get_db @@ -37,6 +38,11 @@ def synthesize(): text=text, speed=speed, pitch=pitch, volume=volume, instruction=instruction ) + elif engine == 'doubao': + audio_data = synthesize_doubao( + 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/doubao_service.py b/backend/app/services/doubao_service.py new file mode 100644 index 0000000..7eece74 --- /dev/null +++ b/backend/app/services/doubao_service.py @@ -0,0 +1,52 @@ +import os +import uuid +import requests + +def synthesize_doubao(text, speed=1.0, pitch=0, volume=1.0, emotion='neutral'): + """调用火山引擎豆包 Seed-ICL 2.0 语音合成 API""" + api_key = os.getenv('DOUBAO_API_KEY', '') + if not api_key: + raise ValueError('DOUBAO_API_KEY未设置') + + url = 'https://openspeech.bytedance.com/api/v1/tts' + headers = { + 'x-api-key': api_key, + 'Content-Type': 'application/json' + } + + payload = { + 'app': { + 'cluster': 'volcano_icl' + }, + 'user': { + 'uid': 'military_tech_voice' + }, + 'audio': { + 'voice_type': 'S_MK0j18H72', + 'encoding': 'mp3', + 'speed_ratio': speed, + }, + 'request': { + 'reqid': str(uuid.uuid4()), + 'text': text, + 'operation': 'query' + } + } + + response = requests.post(url, json=payload, headers=headers, timeout=60) + + if response.status_code != 200: + raise Exception(f'豆包API错误: {response.status_code} - {response.text}') + + result = response.json() + + # 豆包返回格式: {"code": 3000, "data": "base64编码的音频"} + if result.get('code') == 3000 and result.get('data'): + import base64 + audio_bytes = base64.b64decode(result['data']) + return audio_bytes + + # 错误处理 + error_msg = result.get('message', '未知错误') + error_code = result.get('code', 'unknown') + raise Exception(f'豆包返回错误 [{error_code}]: {error_msg}') diff --git a/frontend/app.js b/frontend/app.js index 8476c9c..b9e9cca 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -17,6 +17,11 @@ const ENGINE_CONFIG = { name: 'CosyVoice', emotionMode: 'global', color: '#ff6b6b' + }, + doubao: { + name: '豆包', + emotionMode: 'global', + color: '#7c5cfc' } }; diff --git a/frontend/index.html b/frontend/index.html index 3373a3c..38ccadc 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -40,6 +40,7 @@ 情绪: