document.addEventListener('DOMContentLoaded', function() {
// 获取所有Canvas元素
const signalCanvas = document.getElementById('signalCanvas');
const energyCanvas = document.getElementById('energyCanvas');
const gainCanvas = document.getElementById('gainCanvas');
const suppressedCanvas = document.getElementById('suppressedCanvas');
const maskCanvas = document.getElementById('maskCanvas');
const residualCanvas = document.getElementById('residualCanvas');
const enhancedCanvas = document.getElementById('enhancedCanvas');
const originalComparison = document.getElementById('originalComparison');
const enhancedComparison = document.getElementById('enhancedComparison');
// 获取控制元素
const startBtn = document.getElementById('startBtn');
const resetBtn = document.getElementById('resetBtn');
const statusIndicator = document.getElementById('statusIndicator');
const statusText = document.getElementById('statusText');
const progressFill = document.getElementById('progressFill');
const progressText = document.getElementById('progressText');
// 状态轮询变量
let statusInterval = null;
// 初始化所有Canvas
function initCanvases() {
const canvases = [
signalCanvas, energyCanvas, gainCanvas, suppressedCanvas,
maskCanvas, residualCanvas, enhancedCanvas,
originalComparison, enhancedComparison
];
canvases.forEach(canvas => {
const ctx = canvas.getContext('2d');
const width = canvas.clientWidth;
const height = canvas.clientHeight;
// 设置Canvas尺寸
canvas.width = width;
canvas.height = height;
// 清除Canvas
ctx.clearRect(0, 0, width, height);
// 绘制初始状态
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = 'rgba(100, 120, 255, 0.3)';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.fillText('点击"开始处理"启动仿真', width/2, height/2);
});
}
// 绘制信号波形
function drawSignal(ctx, width, height, signalData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制信号波形
ctx.beginPath();
ctx.strokeStyle = '#4facfe';
ctx.lineWidth = 2;
const centerY = height / 2;
const amplitude = height / 3;
const dataPoints = signalData.length;
for (let i = 0; i < dataPoints; i += Math.ceil(dataPoints / width)) {
const x = (i / dataPoints) * width;
const y = centerY + signalData[i] * amplitude;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 绘制混响区域标记
ctx.fillStyle = 'rgba(255, 100, 100, 0.3)';
ctx.fillRect(width * 0.6, 0, width * 0.4, height);
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '12px Arial';
ctx.textAlign = 'left';
ctx.fillText('混响区域', width * 0.65, 30);
}
// 绘制能量分布
function drawEnergyDistribution(ctx, width, height, energyData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制频谱图
const freqBands = energyData.length;
const bandHeight = height / freqBands;
for (let i = 0; i < freqBands; i++) {
const y = i * bandHeight;
const bandData = energyData[i];
const dataPoints = bandData.length;
// 绘制频率标签
ctx.fillStyle = 'rgba(200, 200, 255, 0.7)';
ctx.font = '10px Arial';
ctx.textAlign = 'left';
ctx.fillText(`${(i+1)*100} Hz`, 5, y + 12);
// 绘制能量分布
for (let j = 0; j < dataPoints; j += 1) {
const x = (j / dataPoints) * width;
const intensity = Math.max(0, Math.min(1, bandData[j]));
ctx.fillStyle = `rgba(255, {100 + intensity \* 155}, 100, {intensity * 0.7})`;
ctx.fillRect(x, y, 3, bandHeight - 2);
}
// 绘制衰减曲线
ctx.beginPath();
ctx.strokeStyle = '#00f2fe';
ctx.lineWidth = 1.5;
for (let j = 0; j < dataPoints; j++) {
const x = (j / dataPoints) * width;
const decay = bandData[j];
const yPos = y + bandHeight - decay * bandHeight * 0.8;
if (j === 0) {
ctx.moveTo(x, yPos);
} else {
ctx.lineTo(x, yPos);
}
}
ctx.stroke();
}
}
// 绘制抑制增益
function drawSuppressionGain(ctx, width, height, gainData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制增益矩阵
const rows = gainData.length;
const cols = gainData[0].length;
const cellWidth = width / cols;
const cellHeight = height / rows;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const gain = gainData[i][j];
const x = j * cellWidth;
const y = i * cellHeight;
ctx.fillStyle = `rgba(100, 200, 255, ${gain * 0.8})`;
ctx.fillRect(x, y, cellWidth - 1, cellHeight - 1);
}
}
// 绘制增益图例
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = '12px Arial';
ctx.textAlign = 'left';
ctx.fillText('增益值: 高', width - 100, 20);
ctx.fillText('增益值: 低', width - 100, height - 10);
}
// 绘制抑制后的信号
function drawSuppressedSignal(ctx, width, height, signalData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制信号波形
ctx.beginPath();
ctx.strokeStyle = '#4facfe';
ctx.lineWidth = 2;
const centerY = height / 2;
const amplitude = height / 3;
const dataPoints = signalData.length;
for (let i = 0; i < dataPoints; i += Math.ceil(dataPoints / width)) {
const x = (i / dataPoints) * width;
const y = centerY + signalData[i] * amplitude;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 标记混响抑制效果
ctx.fillStyle = 'rgba(100, 255, 100, 0.3)';
ctx.fillRect(width * 0.6, 0, width * 0.4, height);
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '12px Arial';
ctx.textAlign = 'left';
ctx.fillText('混响抑制区域', width * 0.65, 30);
}
// 绘制时频掩码
function drawTimeFrequencyMask(ctx, width, height, maskData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制时频掩码
const rows = maskData.length;
const cols = maskData[0].length;
const cellWidth = width / cols;
const cellHeight = height / rows;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const mask = maskData[i][j];
const x = j * cellWidth;
const y = i * cellHeight;
ctx.fillStyle = `rgba(255, 255, 100, ${mask * 0.8})`;
ctx.fillRect(x, y, cellWidth - 1, cellHeight - 1);
}
}
// 绘制图例
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = '12px Arial';
ctx.textAlign = 'left';
ctx.fillText('语音概率: 高', width - 100, 20);
ctx.fillText('语音概率: 低', width - 100, height - 10);
}
// 绘制残余能量
function drawResidualEnergy(ctx, width, height, residualData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制残余能量分布
const freqBands = residualData.length;
const bandHeight = height / freqBands;
for (let i = 0; i < freqBands; i++) {
const y = i * bandHeight;
const bandData = residualData[i];
const dataPoints = bandData.length;
// 绘制频率标签
ctx.fillStyle = 'rgba(200, 200, 255, 0.7)';
ctx.font = '10px Arial';
ctx.textAlign = 'left';
ctx.fillText(`${(i+1)*150} Hz`, 5, y + 12);
// 绘制残余能量
for (let j = 0; j < dataPoints; j += 1) {
const x = (j / dataPoints) * width;
const energy = bandData[j];
ctx.fillStyle = `rgba(255, 100, 200, ${energy * 0.7})`;
ctx.fillRect(x, y, 3, bandHeight - 2);
}
}
}
// 绘制增强后的信号
function drawEnhancedSignal(ctx, width, height, signalData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制信号波形
ctx.beginPath();
ctx.strokeStyle = '#4facfe';
ctx.lineWidth = 2;
const centerY = height / 2;
const amplitude = height / 3;
const dataPoints = signalData.length;
for (let i = 0; i < dataPoints; i += Math.ceil(dataPoints / width)) {
const x = (i / dataPoints) * width;
const y = centerY + signalData[i] * amplitude;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 标记谐波增强区域
ctx.fillStyle = 'rgba(100, 200, 255, 0.3)';
ctx.fillRect(width * 0.3, 0, width * 0.4, height);
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.fillText('谐波增强区域', width * 0.5, 30);
}
// 绘制对比信号 - 原始
function drawOriginalComparison(ctx, width, height, signalData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制信号波形
ctx.beginPath();
ctx.strokeStyle = '#ff6b6b';
ctx.lineWidth = 2;
const centerY = height / 2;
const amplitude = height / 3;
const dataPoints = signalData.length;
for (let i = 0; i < dataPoints; i += Math.ceil(dataPoints / width)) {
const x = (i / dataPoints) * width;
const y = centerY + signalData[i] * amplitude;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 添加标签
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.fillText('原始信号 (含混响)', width/2, 20);
}
// 绘制对比信号 - 增强后
function drawEnhancedComparison(ctx, width, height, signalData) {
ctx.clearRect(0, 0, width, height);
// 绘制背景
ctx.fillStyle = 'rgba(20, 30, 70, 0.8)';
ctx.fillRect(0, 0, width, height);
// 绘制信号波形
ctx.beginPath();
ctx.strokeStyle = '#51cf66';
ctx.lineWidth = 2;
const centerY = height / 2;
const amplitude = height / 3;
const dataPoints = signalData.length;
for (let i = 0; i < dataPoints; i += Math.ceil(dataPoints / width)) {
const x = (i / dataPoints) * width;
const y = centerY + signalData[i] * amplitude;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 添加标签
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.fillText('增强后信号', width/2, 20);
}
// 更新可视化
function updateVisualizations(results) {
const width = signalCanvas.width;
const height = signalCanvas.height;
if (results.original_signal) {
drawSignal(signalCanvas.getContext('2d'), width, height, results.original_signal);
}
if (results.energy_distribution) {
drawEnergyDistribution(energyCanvas.getContext('2d'), width, height, results.energy_distribution);
}
if (results.suppression_gain) {
drawSuppressionGain(gainCanvas.getContext('2d'), width, height, results.suppression_gain);
}
if (results.suppressed_signal) {
drawSuppressedSignal(suppressedCanvas.getContext('2d'), width, height, results.suppressed_signal);
}
if (results.time_frequency_mask) {
drawTimeFrequencyMask(maskCanvas.getContext('2d'), width, height, results.time_frequency_mask);
}
if (results.residual_energy) {
drawResidualEnergy(residualCanvas.getContext('2d'), width, height, results.residual_energy);
}
if (results.enhanced_signal) {
drawEnhancedSignal(enhancedCanvas.getContext('2d'), width, height, results.enhanced_signal);
}
// 更新对比图
if (results.original_signal) {
drawOriginalComparison(originalComparison.getContext('2d'), originalComparison.width, originalComparison.height, results.original_signal);
}
if (results.enhanced_signal) {
drawEnhancedComparison(enhancedComparison.getContext('2d'), enhancedComparison.width, enhancedComparison.height, results.enhanced_signal);
}
}
// 更新状态显示
function updateStatusDisplay(status, progress, message, currentStep, results) {
// 更新状态指示器
statusIndicator.className = 'status-indicator';
if (status === 'idle') {
statusIndicator.classList.add('status-idle');
} else if (status === 'processing') {
statusIndicator.classList.add('status-processing');
} else if (status === 'completed') {
statusIndicator.classList.add('status-completed');
} else if (status === 'error') {
statusIndicator.classList.add('status-error');
}
// 更新状态文本
statusText.textContent = message;
// 更新进度条
progressFill.style.width = `${progress}%`;
progressText.textContent = `${progress}%`;
// 更新按钮状态
startBtn.disabled = status === 'processing';
// 如果有结果数据,更新可视化
if (results && Object.keys(results).length > 0) {
updateVisualizations(results);
}
}
// 获取状态
async function fetchStatus() {
try {
const response = await fetch('/api/status');
const data = await response.json();
updateStatusDisplay(
data.status,
data.progress,
data.message,
data.current_step,
data.results
);
// 如果处理完成或出错,停止轮询
if (data.status === 'completed' || data.status === 'error') {
clearInterval(statusInterval);
statusInterval = null;
}
} catch (error) {
console.error('获取状态失败:', error);
updateStatusDisplay('error', 0, '无法连接到服务器', 0, {});
}
}
// 开始处理
async function startProcessing() {
try {
const response = await fetch('/api/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.status === 'success') {
// 开始轮询状态
if (statusInterval) {
clearInterval(statusInterval);
}
statusInterval = setInterval(fetchStatus, 1000);
fetchStatus(); // 立即获取一次状态
} else {
alert(data.message);
}
} catch (error) {
console.error('启动处理失败:', error);
alert('启动处理失败,请检查服务器连接');
}
}
// 重置处理
async function resetProcessing() {
try {
const response = await fetch('/api/reset', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.status === 'success') {
// 停止轮询
if (statusInterval) {
clearInterval(statusInterval);
statusInterval = null;
}
// 重置状态显示
updateStatusDisplay('idle', 0, '系统已重置', 0, {});
// 重置可视化
initCanvases();
} else {
alert(data.message);
}
} catch (error) {
console.error('重置失败:', error);
alert('重置失败,请检查服务器连接');
}
}
// 绑定按钮事件
startBtn.addEventListener('click', startProcessing);
resetBtn.addEventListener('click', resetProcessing);
// 初始化
initCanvases();
fetchStatus(); // 初始获取状态
});