/**
* 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 = `
`;
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 = `
`;
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();
}
}