const edgeTTS = require('edge-tts'); const path = require('path'); const fs = require('fs'); // 默认配置 const VOICE = "zh-CN-XiaoxiaoNeural"; const OUTPUT_DIR = "/root/.openclaw/media/tts"; async function textToSpeech(text, options = {}) { const voice = options.voice || VOICE; const rate = options.rate || "+0%"; const pitch = options.pitch || "+0Hz"; const outputFile = options.output || path.join(OUTPUT_DIR, `tts_${Date.now()}.mp3`); // 确保输出目录存在 fs.mkdirSync(path.dirname(outputFile), { recursive: true }); try { const communicate = edgeTTS.Communicate(text, voice, { rate: rate, pitch: pitch }); await communicate.save(outputFile); console.log(`✅ TTS 生成成功: ${outputFile}`); return outputFile; } catch (error) { console.error(`❌ TTS 生成失败: ${error.message}`); return null; } } async function listVoices() { try { const voices = await edgeTTS.getVoices(); console.log(`\n可用语音 (${voices.length} 个):\n`); // 中文语音 const zhVoices = voices.filter(v => v.Lang.startsWith('zh')); console.log("中文语音:"); zhVoices.forEach(v => console.log(` - ${v.Name} (${v.Lang})`)); // 英文语音 const enVoices = voices.filter(v => v.Lang.startsWith('en')); console.log("\n英文语音:"); enVoices.forEach(v => console.log(` - ${v.Name} (${v.Lang})`)); return voices; } catch (error) { console.error(`获取语音列表失败: ${error.message}`); return []; } } // 命令行参数处理 async function main() { const args = process.argv.slice(2); if (args.includes('--list-voices') || args.includes('-L')) { await listVoices(); return; } if (args.length === 0) { console.log("用法:"); console.log(" node tts-converter.js \"文本内容\""); console.log(" node tts-converter.js \"文本\" --voice zh-CN-XiaoxiaoNeural --rate +10%"); console.log(" node tts-converter.js --list-voices"); console.log("\n选项:"); console.log(" --voice, -v 语音名称"); console.log(" --rate, -r 语速 (+/-百分比)"); console.log(" --pitch, -p 音调 (+/-Hz)"); console.log(" --output, -o 输出文件路径"); return; } // 解析参数 let text = args[0]; let options = { voice: VOICE, rate: "+0%", pitch: "+0Hz", output: path.join(OUTPUT_DIR, `tts_${Date.now()}.mp3`) }; for (let i = 1; i < args.length; i += 2) { const key = args[i].replace(/^--/, ''); const value = args[i + 1]; if (key === 'voice' || key === 'v') options.voice = value; if (key === 'rate' || key === 'r') options.rate = value; if (key === 'pitch' || key === 'p') options.pitch = value; if (key === 'output' || key === 'o') options.output = value; } await textToSpeech(text, options); } main();