/** * CosyVoice API 测试页面 JavaScript */ // 全局变量 let uploadedAudioPath = null; let loadingModal = null; // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { initializeComponents(); bindEvents(); loadAvailableVoices(); }); /** * 初始化组件 */ function initializeComponents() { loadingModal = new bootstrap.Modal(document.getElementById('loadingModal')); // 语速滑块显示 const speedSlider = document.getElementById('preset-speed'); const speedValue = document.getElementById('preset-speed-value'); speedSlider.addEventListener('input', function() { speedValue.textContent = this.value; }); } /** * 绑定事件 */ function bindEvents() { // 连接测试 document.getElementById('test-connection-btn').addEventListener('click', testConnection); // 预训练音色测试 document.getElementById('preset-voice-form').addEventListener('submit', generatePresetVoice); document.getElementById('preset-random-seed').addEventListener('click', () => getRandomSeed('preset-seed')); // 自然语言控制测试 document.getElementById('natural-control-form').addEventListener('submit', generateNaturalControl); document.getElementById('natural-random-seed').addEventListener('click', () => getRandomSeed('natural-seed')); // 语音克隆测试 document.getElementById('audio-upload-form').addEventListener('submit', uploadReferenceAudio); document.getElementById('voice-clone-form').addEventListener('submit', generateVoiceClone); document.getElementById('clone-random-seed').addEventListener('click', () => getRandomSeed('clone-seed')); // 清空日志 document.getElementById('clear-log').addEventListener('click', clearLog); } /** * 显示加载状态 */ function showLoading(message = '正在处理中...') { document.getElementById('loading-message').textContent = message; loadingModal.show(); } /** * 隐藏加载状态 */ function hideLoading() { loadingModal.hide(); } /** * 添加日志 */ function addLog(message, type = 'info') { const logContainer = document.getElementById('test-log'); const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); const colors = { 'info': 'text-primary', 'success': 'text-success', 'error': 'text-danger', 'warning': 'text-warning' }; logEntry.className = `mb-2 ${colors[type] || 'text-primary'}`; logEntry.innerHTML = `[${timestamp}] ${message}`; logContainer.appendChild(logEntry); logContainer.scrollTop = logContainer.scrollHeight; } /** * 清空日志 */ function clearLog() { const logContainer = document.getElementById('test-log'); logContainer.innerHTML = '

测试记录将显示在这里...

'; } /** * 显示错误信息 */ function showError(message) { const toast = document.createElement('div'); toast.className = 'toast align-items-center text-white bg-danger border-0 position-fixed top-0 end-0 m-3'; toast.style.zIndex = '9999'; toast.innerHTML = `
${message}
`; document.body.appendChild(toast); const bsToast = new bootstrap.Toast(toast); bsToast.show(); // 自动移除 setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 5000); } /** * 显示成功信息 */ function showSuccess(message) { const toast = document.createElement('div'); toast.className = 'toast align-items-center text-white bg-success border-0 position-fixed top-0 end-0 m-3'; toast.style.zIndex = '9999'; toast.innerHTML = `
${message}
`; document.body.appendChild(toast); const bsToast = new bootstrap.Toast(toast); bsToast.show(); // 自动移除 setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 3000); } /** * 测试连接 */ async function testConnection() { const btn = document.getElementById('test-connection-btn'); const statusDiv = document.getElementById('connection-status'); btn.disabled = true; btn.innerHTML = '测试中...'; statusDiv.innerHTML = '正在测试连接...'; addLog('开始测试CosyVoice服务连接...'); try { const response = await fetch('/voice-test/api/voice-test/connection', { method: 'POST', headers: { 'Content-Type': 'application/json', } }); const result = await response.json(); if (result.success) { statusDiv.innerHTML = ` 连接成功 (${result.api_url}) `; addLog(`连接成功!可用音色数量: ${result.available_voices ? result.available_voices.length : 0}`, 'success'); // 更新音色列表 if (result.available_voices) { updateVoiceOptions(result.available_voices); } } else { statusDiv.innerHTML = ` 连接失败: ${result.message} `; addLog(`连接失败: ${result.message}`, 'error'); } } catch (error) { statusDiv.innerHTML = ` 请求失败: ${error.message} `; addLog(`请求失败: ${error.message}`, 'error'); } finally { btn.disabled = false; btn.innerHTML = '测试连接'; } } /** * 加载可用音色 */ async function loadAvailableVoices() { try { const response = await fetch('/voice-test/api/voice-test/voices'); const result = await response.json(); if (result.success) { updateVoiceOptions(result.voices); } } catch (error) { console.error('加载音色失败:', error); } } /** * 更新音色选项 */ function updateVoiceOptions(voices) { const voiceSelect = document.getElementById('preset-voice'); voiceSelect.innerHTML = ''; voices.forEach(voice => { const option = document.createElement('option'); option.value = voice; option.textContent = voice; voiceSelect.appendChild(option); }); } /** * 获取随机种子 */ async function getRandomSeed(inputId) { try { const response = await fetch('/voice-test/api/voice-test/random-seed'); const result = await response.json(); if (result.success) { document.getElementById(inputId).value = result.seed; addLog(`生成随机种子: ${result.seed}`); } } catch (error) { showError('获取随机种子失败'); } } /** * 预训练音色语音生成 */ async function generatePresetVoice(e) { e.preventDefault(); const text = document.getElementById('preset-text').value.trim(); const voice = document.getElementById('preset-voice').value; const seed = parseInt(document.getElementById('preset-seed').value); const speed = parseFloat(document.getElementById('preset-speed').value); if (!text) { showError('请输入要合成的文本'); return; } showLoading('正在生成语音...'); addLog(`开始预训练音色生成 - 音色: ${voice}, 种子: ${seed}, 语速: ${speed}x`); try { const response = await fetch('/voice-test/api/voice-test/generate/preset', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text, voice: voice, seed: seed, speed: speed }) }); const result = await response.json(); if (result.success) { // 显示音频播放器 const audioSource = document.getElementById('preset-audio-source'); const resultDiv = document.getElementById('preset-result'); audioSource.src = result.audio_url; audioSource.parentElement.load(); resultDiv.style.display = 'block'; addLog(`预训练音色生成成功!音频地址: ${result.audio_url}`, 'success'); showSuccess('语音生成成功!'); } else { addLog(`预训练音色生成失败: ${result.message}`, 'error'); showError(result.message); } } catch (error) { addLog(`预训练音色生成出错: ${error.message}`, 'error'); showError('生成失败,请检查网络连接'); } finally { hideLoading(); } } /** * 自然语言控制语音生成 */ async function generateNaturalControl(e) { e.preventDefault(); const text = document.getElementById('natural-text').value.trim(); const instruction = document.getElementById('natural-instruction').value.trim(); const seed = parseInt(document.getElementById('natural-seed').value); if (!text) { showError('请输入要合成的文本'); return; } if (!instruction) { showError('请输入语音指令'); return; } showLoading('正在生成语音...'); addLog(`开始自然语言控制生成 - 指令: ${instruction}, 种子: ${seed}`); try { const response = await fetch('/voice-test/api/voice-test/generate/natural', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text, instruction: instruction, seed: seed }) }); const result = await response.json(); if (result.success) { // 显示音频播放器 const audioSource = document.getElementById('natural-audio-source'); const resultDiv = document.getElementById('natural-result'); audioSource.src = result.audio_url; audioSource.parentElement.load(); resultDiv.style.display = 'block'; addLog(`自然语言控制生成成功!音频地址: ${result.audio_url}`, 'success'); showSuccess('语音生成成功!'); } else { addLog(`自然语言控制生成失败: ${result.message}`, 'error'); showError(result.message); } } catch (error) { addLog(`自然语言控制生成出错: ${error.message}`, 'error'); showError('生成失败,请检查网络连接'); } finally { hideLoading(); } } /** * 上传参考音频 */ async function uploadReferenceAudio(e) { e.preventDefault(); const fileInput = document.getElementById('reference-audio'); const file = fileInput.files[0]; if (!file) { showError('请选择音频文件'); return; } showLoading('正在上传并识别音频...'); addLog(`开始上传音频文件: ${file.name} (${(file.size/1024/1024).toFixed(2)} MB)`); const formData = new FormData(); formData.append('audio', file); try { const response = await fetch('/voice-test/api/voice-test/upload-audio', { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { // 保存音频路径 uploadedAudioPath = result.file_path; // 显示识别结果 const resultDiv = document.getElementById('upload-result'); const recognizedText = document.getElementById('recognized-text'); recognizedText.value = result.recognized_text || ''; resultDiv.style.display = 'block'; // 启用克隆按钮 const cloneBtn = document.querySelector('#voice-clone-form button[type="submit"]'); cloneBtn.disabled = false; addLog(`音频上传成功!识别文本: ${result.recognized_text || '(无内容)'}`, 'success'); showSuccess('音频上传成功!'); } else { addLog(`音频上传失败: ${result.message}`, 'error'); showError(result.message); } } catch (error) { addLog(`音频上传出错: ${error.message}`, 'error'); showError('上传失败,请检查网络连接'); } finally { hideLoading(); } } /** * 语音克隆生成 */ async function generateVoiceClone(e) { e.preventDefault(); if (!uploadedAudioPath) { showError('请先上传参考音频'); return; } const text = document.getElementById('clone-text').value.trim(); const referenceText = document.getElementById('recognized-text').value.trim(); const seed = parseInt(document.getElementById('clone-seed').value); if (!text) { showError('请输入要合成的文本'); return; } showLoading('正在进行语音克隆...'); addLog(`开始语音克隆 - 种子: ${seed}`); try { const response = await fetch('/voice-test/api/voice-test/generate/clone', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text, reference_audio_path: uploadedAudioPath, reference_text: referenceText, seed: seed }) }); const result = await response.json(); if (result.success) { // 显示音频播放器 const audioSource = document.getElementById('clone-audio-source'); const resultDiv = document.getElementById('clone-result'); audioSource.src = result.audio_url; audioSource.parentElement.load(); resultDiv.style.display = 'block'; addLog(`语音克隆成功!音频地址: ${result.audio_url}`, 'success'); showSuccess('语音克隆成功!'); } else { addLog(`语音克隆失败: ${result.message}`, 'error'); showError(result.message); } } catch (error) { addLog(`语音克隆出错: ${error.message}`, 'error'); showError('克隆失败,请检查网络连接'); } finally { hideLoading(); } }