基于Flask的智能语音增强系统模拟

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(); // 初始获取状态

});

相关推荐
青衫码上行1 小时前
【Java Web学习 | 第14篇】JavaScript(8) -正则表达式
java·前端·javascript·学习·正则表达式
草帽lufei1 小时前
解锁AI新维度:深入体验Google Antigravity的Gemini3模型
前端·ai编程·gemini
CoolerWu1 小时前
TRAE SOLO实战:一个所见即所得的笔记软体
前端·trae
没落英雄2 小时前
简单了解 shadowDom
前端·html
天才熊猫君2 小时前
vue3 基于 el-table 的无限滚动自定义指令实现
前端·javascript
陳陈陳2 小时前
AIGC 时代,用自然语言操作数据库:SQLite + LLM 的轻量级实践
前端·数据库·python
星迷朦龙2 小时前
使用剪贴版复制网页内容
前端
BBB努力学习程序设计2 小时前
Bootstrap图片:让图片展示更优雅、更专业
前端·html
玉宇夕落2 小时前
深入理解 async/await:从原理到实战,彻底掌握 JavaScript 异步编程
前端