feat: 完善功能,支持频谱显示
This commit is contained in:
202
assets/main.js
202
assets/main.js
@@ -12,13 +12,22 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
let reader = null;
|
let reader = null;
|
||||||
let writer = null;
|
let writer = null;
|
||||||
let deviceState = 0; // 0: 未连接, 1: 已连接
|
let deviceState = 0; // 0: 未连接, 1: 已连接
|
||||||
let refreshInterval = 200; // 刷新间隔(毫秒)
|
let refreshInterval = 100; // 刷新间隔(毫秒)
|
||||||
let animationId = null;
|
let animationId = null;
|
||||||
let lastRefreshTime = 0;
|
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_WIDTH = 320; // Canvas 显示宽度(用于网页显示)
|
||||||
let SHOW_HEIGHT = 80; // Canvas 显示高度(用于网页显示)
|
let SHOW_HEIGHT = 240; // Canvas 显示高度(用于网页显示)
|
||||||
let LCD_X = 160; // 设备实际宽度
|
let LCD_X = 160; // 设备实际宽度
|
||||||
let LCD_Y = 80; // 设备实际高度
|
let LCD_Y = 80; // 设备实际高度
|
||||||
|
|
||||||
@@ -160,6 +169,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
ctx.font = this.font;
|
ctx.font = this.font;
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
|
||||||
layouts.forEach(layout => {
|
layouts.forEach(layout => {
|
||||||
if (this.monitorData[layout.key]) {
|
if (this.monitorData[layout.key]) {
|
||||||
@@ -176,30 +186,147 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
// 创建全局监控器实例
|
// 创建全局监控器实例
|
||||||
const monitor = new SystemMonitor();
|
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');
|
const resolutionSelect = document.getElementById('resolution');
|
||||||
|
|
||||||
resolutionSelect.addEventListener('change', (e) => {
|
resolutionSelect.addEventListener('change', (e) => {
|
||||||
|
|
||||||
const selectedResolution = e.target.value;
|
const selectedResolution = e.target.value;
|
||||||
const resolutionConfig = RESOLUTIONS[selectedResolution];
|
const resolutionConfig = RESOLUTIONS[selectedResolution];
|
||||||
|
|
||||||
// 更新显示参数
|
// 更新显示参数
|
||||||
SHOW_WIDTH = resolutionConfig.showWidth;
|
SHOW_WIDTH = resolutionConfig.showWidth;
|
||||||
SHOW_HEIGHT = resolutionConfig.showHeight;
|
SHOW_HEIGHT = resolutionConfig.showHeight;
|
||||||
LCD_X = resolutionConfig.lcdX;
|
LCD_X = resolutionConfig.lcdX;
|
||||||
LCD_Y = resolutionConfig.lcdY;
|
LCD_Y = resolutionConfig.lcdY;
|
||||||
|
|
||||||
// 更新Canvas尺寸
|
// 更新Canvas尺寸
|
||||||
canvas.width = SHOW_WIDTH;
|
canvas.width = SHOW_WIDTH;
|
||||||
canvas.height = SHOW_HEIGHT;
|
canvas.height = SHOW_HEIGHT;
|
||||||
|
|
||||||
// 重新创建显示图像
|
// 重新创建显示图像
|
||||||
monitor.createDisplayImage();
|
if (displayMode === 'data') {
|
||||||
|
monitor.createDisplayImage();
|
||||||
|
} else {
|
||||||
|
drawSpectrum();
|
||||||
|
}
|
||||||
|
|
||||||
// 显示信息
|
// 显示信息
|
||||||
console.log(`分辨率已切换至: ${selectedResolution}`);
|
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(() => {
|
setTimeout(() => {
|
||||||
if (!animationId) {
|
if (!animationId) {
|
||||||
console.log('自动开始监控...');
|
console.log('自动开始...');
|
||||||
animationId = requestAnimationFrame(animate);
|
animationId = requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 100);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('连接设备时出错:', error);
|
console.error('连接设备时出错:', error);
|
||||||
@@ -366,7 +493,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
while (i < 128) {
|
while (i < 128) {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
let a_data = photoData[dataIndex];
|
let a_data = photoData[dataIndex];
|
||||||
|
|
||||||
// 统计第一种颜色的连续出现次数(最多15次)
|
// 统计第一种颜色的连续出现次数(最多15次)
|
||||||
for (let s = 0; s < 15; s++) {
|
for (let s = 0; s < 15; s++) {
|
||||||
if (i < 128 && a_data === photoData[dataIndex]) {
|
if (i < 128 && a_data === photoData[dataIndex]) {
|
||||||
@@ -380,7 +507,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
let b = 0;
|
let b = 0;
|
||||||
let b_data = 0;
|
let b_data = 0;
|
||||||
|
|
||||||
// 统计第二种颜色的连续出现次数(最多15次)
|
// 统计第二种颜色的连续出现次数(最多15次)
|
||||||
if (i < 128) {
|
if (i < 128) {
|
||||||
b_data = photoData[dataIndex];
|
b_data = photoData[dataIndex];
|
||||||
@@ -409,7 +536,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
const remainingDataSize = totalDataSize % dataPerPage;
|
const remainingDataSize = totalDataSize % dataPerPage;
|
||||||
if (remainingDataSize !== 0) {
|
if (remainingDataSize !== 0) {
|
||||||
const remainingData = photoData.slice(totalDataSize - remainingDataSize);
|
const remainingData = photoData.slice(totalDataSize - remainingDataSize);
|
||||||
|
|
||||||
// 补全数据到128字节
|
// 补全数据到128字节
|
||||||
while (remainingData.length < dataPerPage) {
|
while (remainingData.length < dataPerPage) {
|
||||||
remainingData.push(0xFFFF);
|
remainingData.push(0xFFFF);
|
||||||
@@ -420,7 +547,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
while (i < 128) {
|
while (i < 128) {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
let a_data = remainingData[dataIndex];
|
let a_data = remainingData[dataIndex];
|
||||||
|
|
||||||
for (let s = 0; s < 15; s++) {
|
for (let s = 0; s < 15; s++) {
|
||||||
if (i < 128 && a_data === remainingData[dataIndex]) {
|
if (i < 128 && a_data === remainingData[dataIndex]) {
|
||||||
a++;
|
a++;
|
||||||
@@ -433,7 +560,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
let b = 0;
|
let b = 0;
|
||||||
let b_data = 0;
|
let b_data = 0;
|
||||||
|
|
||||||
if (i < 128) {
|
if (i < 128) {
|
||||||
b_data = remainingData[dataIndex];
|
b_data = remainingData[dataIndex];
|
||||||
for (let s = 0; s < 15; s++) {
|
for (let s = 0; s < 15; s++) {
|
||||||
@@ -536,6 +663,33 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
await sendData(hexUse);
|
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) {
|
async function animate(timestamp) {
|
||||||
if (!lastRefreshTime) {
|
if (!lastRefreshTime) {
|
||||||
@@ -552,10 +706,18 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
// 设置显示区域
|
// 设置显示区域
|
||||||
await LCD_ADD(0, 0, LCD_X, LCD_Y);
|
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) {
|
} catch (error) {
|
||||||
console.error('显示系统状态时出错:', error);
|
console.error('显示内容时出错:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
index.html
11
index.html
@@ -13,16 +13,23 @@
|
|||||||
<button id="start-button">开始发送</button>
|
<button id="start-button">开始发送</button>
|
||||||
<button id="stop-button">停止发送</button>
|
<button id="stop-button">停止发送</button>
|
||||||
<div class="resolution-selector">
|
<div class="resolution-selector">
|
||||||
<label for="resolution">分辨率:</label>
|
<label for="resolution">分辨率:</label>
|
||||||
<select id="resolution">
|
<select id="resolution">
|
||||||
<option value="160x80" selected>160x80</option>
|
<option value="160x80" selected>160x80</option>
|
||||||
<option value="350x172">350x172</option>
|
<option value="350x172">350x172</option>
|
||||||
<option value="320x240">320x240</option>
|
<option value="320x240">320x240</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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>
|
||||||
<div class="canvas-container">
|
<div class="canvas-container">
|
||||||
<canvas id="monitor-canvas" width="160" height="80"></canvas>
|
<canvas id="monitor-canvas" width="320" height="240"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/main.js"></script>
|
<script src="./assets/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user