"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AliyunRealtimeTtsProvider = void 0; const ws_1 = __importDefault(require("ws")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const config_1 = require("../../config"); // 阿里云百炼 Qwen-TTS WebSocket 实时语音合成 // 文档: https://help.aliyun.com/zh/model-studio/qwen-tts-realtime // 支持流式输入,适合长文本 class AliyunRealtimeTtsProvider { apiKey; model; voice; baseUrl = 'wss://dashscope.aliyuncs.com/api-ws/v1/realtime'; constructor() { this.apiKey = config_1.config.dashscope.apiKey; this.model = config_1.config.dashscope.realtimeModel; this.voice = config_1.config.dashscope.voice; } // 语音合成(WebSocket 流式) async synthesize(text, voiceId, params, outputPath) { return new Promise((resolve, reject) => { const voice = voiceId || this.voice; const audioChunks = []; let sessionId; console.log('🔊 [Qwen Realtime TTS] 建立 WebSocket 连接...'); const ws = new ws_1.default(this.baseUrl, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'X-DashScope-App': 'audio-tts', }, }); ws.on('open', () => { console.log('✅ [Qwen Realtime TTS] 连接已建立'); }); ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); switch (message.event) { case 'session.configed': // 配置成功,发送文本 sessionId = message.session_id; console.log('📝 [Qwen Realtime TTS] Session ID:', sessionId); // 构建指令 const instructions = []; if (params.speed !== 1) { const speedDesc = params.speed > 1 ? '语速较快' : '语速较慢'; instructions.push(speedDesc); } if (params.pitch !== 0) { const pitchDesc = params.pitch > 0 ? '音调较高' : '音调较低'; instructions.push(pitchDesc); } // 使用 server_commit 模式:直接发送完整文本,服务端自动处理分段 const textMessage = { event: 'input_text_buffer.append', context: { text: text, }, }; ws.send(JSON.stringify(textMessage)); console.log('📤 [Qwen Realtime TTS] 文本已发送,长度:', text.length); // 发送 commit 触发合成 const commitMessage = { event: 'input_text_buffer.commit', }; ws.send(JSON.stringify(commitMessage)); console.log('📤 [Qwen Realtime TTS] 触发语音合成...'); break; case 'audio.stream': // 接收音频数据 if (message.data) { const audioBuffer = Buffer.from(message.data, 'base64'); audioChunks.push(audioBuffer); } break; case 'audio.finish': // 合成完成 console.log('✅ [Qwen Realtime TTS] 合成完成'); // 保存文件 if (audioChunks.length > 0) { const dir = path_1.default.dirname(outputPath); if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } const finalPath = outputPath.endsWith('.mp3') ? outputPath : outputPath.replace(/\.[^.]+$/, '.mp3'); const finalBuffer = Buffer.concat(audioChunks); fs_1.default.writeFileSync(finalPath, finalBuffer); console.log(`✅ [Qwen Realtime TTS] 文件已保存: ${finalPath}, 大小: ${finalBuffer.length} bytes`); ws.close(); resolve(finalPath); } else { ws.close(); reject(new Error('Qwen Realtime TTS 未返回音频数据')); } break; case 'task.finished': // 任务完成 ws.close(); resolve(outputPath); break; case 'error': console.error('❌ [Qwen Realtime TTS] 错误:', message.message); ws.close(); reject(new Error(`Qwen Realtime TTS 错误: ${message.message}`)); break; default: // 忽略其他消息 break; } } catch (err) { console.error('❌ [Qwen Realtime TTS] 解析消息失败:', err); } }); ws.on('error', (err) => { console.error('❌ [Qwen Realtime TTS] WebSocket 错误:', err.message); reject(err); }); ws.on('close', () => { console.log('🔌 [Qwen Realtime TTS] 连接已关闭'); }); // 超时处理 setTimeout(() => { if (ws.readyState === ws_1.default.OPEN) { ws.close(); reject(new Error('Qwen Realtime TTS 请求超时')); } }, 120000); // 2分钟超时 }); } } exports.AliyunRealtimeTtsProvider = AliyunRealtimeTtsProvider;