1
0

feat: 完善功能,支持频谱显示

This commit is contained in:
2026-02-09 19:40:50 +08:00
parent 184c42002b
commit 687064041c
2 changed files with 191 additions and 22 deletions

View File

@@ -12,13 +12,22 @@ window.addEventListener('DOMContentLoaded', () => {
let reader = null;
let writer = null;
let deviceState = 0; // 0: 未连接, 1: 已连接
let refreshInterval = 200; // 刷新间隔(毫秒)
let refreshInterval = 100; // 刷新间隔(毫秒)
let animationId = null;
let lastRefreshTime = 0;
let displayMode = 'data'; // 显示模式: 'data' 或 'spectrum'
// 音频相关变量
let audioContext = null;
let analyser = null;
let microphone = null;
let dataArray = null;
let bufferLength = null;
let isMicrophoneActive = false;
// 显示参数
let SHOW_WIDTH = 160; // Canvas 显示宽度(用于网页显示)
let SHOW_HEIGHT = 80; // Canvas 显示高度(用于网页显示)
let SHOW_WIDTH = 320; // Canvas 显示宽度(用于网页显示)
let SHOW_HEIGHT = 240; // Canvas 显示高度(用于网页显示)
let LCD_X = 160; // 设备实际宽度
let LCD_Y = 80; // 设备实际高度
@@ -160,6 +169,7 @@ window.addEventListener('DOMContentLoaded', () => {
ctx.font = this.font;
ctx.textBaseline = 'top';
ctx.textAlign = 'left';
layouts.forEach(layout => {
if (this.monitorData[layout.key]) {
@@ -176,30 +186,147 @@ window.addEventListener('DOMContentLoaded', () => {
// 创建全局监控器实例
const monitor = new SystemMonitor();
// 初始化音频上下文和麦克风
async function initAudio() {
try {
// 创建音频上下文
audioContext = new (window.AudioContext || window.webkitAudioContext)();
// 请求麦克风访问
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// 创建麦克风源
microphone = audioContext.createMediaStreamSource(stream);
// 创建分析器
analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
// 连接麦克风到分析器
microphone.connect(analyser);
// 获取分析器数据
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
isMicrophoneActive = true;
console.log('麦克风初始化成功');
} catch (error) {
console.error('初始化音频时出错:', error);
isMicrophoneActive = false;
}
}
// 停止音频分析
function stopAudio() {
if (audioContext) {
audioContext.close();
audioContext = null;
}
if (microphone) {
microphone.disconnect();
microphone = null;
}
if (analyser) {
analyser.disconnect();
analyser = null;
}
isMicrophoneActive = false;
console.log('音频分析已停止');
}
// 绘制频谱
function drawSpectrum() {
// 清空画布
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, SHOW_WIDTH, SHOW_HEIGHT);
if (!isMicrophoneActive || !analyser) {
ctx.fillStyle = 'white';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.fillText('麦克风未初始化', SHOW_WIDTH / 2, SHOW_HEIGHT / 2);
return;
}
// 获取频谱数据
analyser.getByteFrequencyData(dataArray);
// 计算柱状图宽度和间距
const barWidth = (SHOW_WIDTH / bufferLength) * 2.5;
let x = 0;
// 绘制频谱柱状图
for (let i = 0; i < bufferLength; i++) {
// 计算柱状图高度
const barHeight = (dataArray[i] / 255) * SHOW_HEIGHT;
// 生成渐变颜色
const r = Math.floor((i / bufferLength) * 255);
const g = Math.floor((dataArray[i] / 255) * 255);
const b = 150;
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
// 绘制柱状图
ctx.fillRect(x, SHOW_HEIGHT - barHeight, barWidth, barHeight);
// 移动到下一个柱状图位置
x += barWidth + 1;
}
}
// 分辨率选择事件监听器
const resolutionSelect = document.getElementById('resolution');
resolutionSelect.addEventListener('change', (e) => {
const selectedResolution = e.target.value;
const resolutionConfig = RESOLUTIONS[selectedResolution];
// 更新显示参数
SHOW_WIDTH = resolutionConfig.showWidth;
SHOW_HEIGHT = resolutionConfig.showHeight;
LCD_X = resolutionConfig.lcdX;
LCD_Y = resolutionConfig.lcdY;
// 更新Canvas尺寸
canvas.width = SHOW_WIDTH;
canvas.height = SHOW_HEIGHT;
// 重新创建显示图像
monitor.createDisplayImage();
if (displayMode === 'data') {
monitor.createDisplayImage();
} else {
drawSpectrum();
}
// 显示信息
console.log(`分辨率已切换至: ${selectedResolution}`);
});
// 显示模式选择事件监听器
const displayModeSelect = document.getElementById('display-mode');
displayModeSelect.addEventListener('change', (e) => {
displayMode = e.target.value;
// 重新创建显示图像
if (displayMode === 'data') {
monitor.createDisplayImage();
} else {
// 如果切换到频谱模式,初始化麦克风
if (!isMicrophoneActive) {
initAudio();
}
drawSpectrum();
}
// 显示信息
console.log(`显示模式已切换至: ${displayMode === 'data' ? '文本数据' : '频谱分析'}`);
});
// 连接按钮点击事件
@@ -234,10 +361,10 @@ window.addEventListener('DOMContentLoaded', () => {
// 连接成功后自动开始监控
setTimeout(() => {
if (!animationId) {
console.log('自动开始监控...');
console.log('自动开始...');
animationId = requestAnimationFrame(animate);
}
}, 200);
}, 100);
} catch (error) {
console.error('连接设备时出错:', error);
@@ -366,7 +493,7 @@ window.addEventListener('DOMContentLoaded', () => {
while (i < 128) {
let a = 0;
let a_data = photoData[dataIndex];
// 统计第一种颜色的连续出现次数最多15次
for (let s = 0; s < 15; s++) {
if (i < 128 && a_data === photoData[dataIndex]) {
@@ -380,7 +507,7 @@ window.addEventListener('DOMContentLoaded', () => {
let b = 0;
let b_data = 0;
// 统计第二种颜色的连续出现次数最多15次
if (i < 128) {
b_data = photoData[dataIndex];
@@ -409,7 +536,7 @@ window.addEventListener('DOMContentLoaded', () => {
const remainingDataSize = totalDataSize % dataPerPage;
if (remainingDataSize !== 0) {
const remainingData = photoData.slice(totalDataSize - remainingDataSize);
// 补全数据到128字节
while (remainingData.length < dataPerPage) {
remainingData.push(0xFFFF);
@@ -420,7 +547,7 @@ window.addEventListener('DOMContentLoaded', () => {
while (i < 128) {
let a = 0;
let a_data = remainingData[dataIndex];
for (let s = 0; s < 15; s++) {
if (i < 128 && a_data === remainingData[dataIndex]) {
a++;
@@ -433,7 +560,7 @@ window.addEventListener('DOMContentLoaded', () => {
let b = 0;
let b_data = 0;
if (i < 128) {
b_data = remainingData[dataIndex];
for (let s = 0; s < 15; s++) {
@@ -536,6 +663,33 @@ window.addEventListener('DOMContentLoaded', () => {
await sendData(hexUse);
}
// 显示频谱到LCD屏幕
async function showFrequencySpectrum() {
// 绘制频谱
drawSpectrum();
// 创建一个临时Canvas用于图像缩放
const tempCanvas = document.createElement('canvas');
tempCanvas.width = LCD_X;
tempCanvas.height = LCD_Y;
const tempCtx = tempCanvas.getContext('2d');
// 将原始Canvas内容缩放到设备实际分辨率
tempCtx.drawImage(canvas, 0, 0, LCD_X, LCD_Y);
// 获取缩放后的图像数据
const imageData = tempCtx.getImageData(0, 0, LCD_X, LCD_Y);
// 转换为RGB565格式
const rgb565 = rgb888ToRgb565(imageData);
// 压缩数据
const hexUse = ScreenDateProcess(rgb565);
// 发送数据到设备
await sendData(hexUse);
}
// 动画循环
async function animate(timestamp) {
if (!lastRefreshTime) {
@@ -552,10 +706,18 @@ window.addEventListener('DOMContentLoaded', () => {
// 设置显示区域
await LCD_ADD(0, 0, LCD_X, LCD_Y);
// 显示系统状态
await showPCState();
// 根据显示模式选择要显示的内容
if (displayMode === 'spectrum') {
// 如果是频谱模式但麦克风未初始化,则初始化麦克风
if (!isMicrophoneActive) {
await initAudio();
}
await showFrequencySpectrum();
} else {
await showPCState();
}
} catch (error) {
console.error('显示系统状态时出错:', error);
console.error('显示内容时出错:', error);
}
}
}

View File

@@ -13,16 +13,23 @@
<button id="start-button">开始发送</button>
<button id="stop-button">停止发送</button>
<div class="resolution-selector">
<label for="resolution">分辨率:</label>
<label for="resolution">分辨率</label>
<select id="resolution">
<option value="160x80" selected>160x80</option>
<option value="350x172">350x172</option>
<option value="320x240">320x240</option>
</select>
</div>
<div class="display-mode-selector">
<label for="display-mode">显示模式:</label>
<select id="display-mode">
<option value="data" selected>文本数据</option>
<option value="spectrum">频谱分析</option>
</select>
</div>
</div>
<div class="canvas-container">
<canvas id="monitor-canvas" width="160" height="80"></canvas>
<canvas id="monitor-canvas" width="320" height="240"></canvas>
</div>
<script src="./assets/main.js"></script>
</body>