| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- "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;
|