diff --git a/main/xiaozhi-server/test/js/document.js b/main/xiaozhi-server/test/js/document.js index eb23247e..6fb5b222 100644 --- a/main/xiaozhi-server/test/js/document.js +++ b/main/xiaozhi-server/test/js/document.js @@ -6,6 +6,7 @@ const messageInput = document.getElementById('messageInput'); const sendTextButton = document.getElementById('sendTextButton'); const recordButton = document.getElementById('recordButton'); const stopButton = document.getElementById('stopButton'); +// 会话记录 const conversationDiv = document.getElementById('conversation'); const logContainer = document.getElementById('logContainer'); let visualizerCanvas = document.getElementById('audioVisualizer'); @@ -37,3 +38,12 @@ export function updateScriptStatus(message, type) { } } +// 添加消息到会话记录 +export function addMessage(text, isUser = false) { + const messageDiv = document.createElement('div'); + messageDiv.className = `message ${isUser ? 'user' : 'server'}`; + messageDiv.textContent = text; + conversationDiv.appendChild(messageDiv); + conversationDiv.scrollTop = conversationDiv.scrollHeight; +} + diff --git a/main/xiaozhi-server/test/js/opus.js b/main/xiaozhi-server/test/js/opus.js index 9259b9de..67e7ec30 100644 --- a/main/xiaozhi-server/test/js/opus.js +++ b/main/xiaozhi-server/test/js/opus.js @@ -40,4 +40,147 @@ export function checkOpusLoaded() { log(`Opus库加载失败,请检查libopus.js文件是否存在且正确: ${err.message}`, 'error'); updateScriptStatus('Opus库加载失败,请检查libopus.js文件是否存在且正确', 'error'); } +} + + +// 创建一个Opus编码器 +let opusEncoder = null; +export function initOpusEncoder() { + try { + if (opusEncoder) { + return opusEncoder; // 已经初始化过 + } + + if (!window.ModuleInstance) { + log('无法创建Opus编码器:ModuleInstance不可用', 'error'); + return; + } + + // 初始化一个Opus编码器 + const mod = window.ModuleInstance; + const sampleRate = 16000; // 16kHz采样率 + const channels = 1; // 单声道 + const application = 2048; // OPUS_APPLICATION_VOIP = 2048 + + // 创建编码器 + opusEncoder = { + channels: channels, + sampleRate: sampleRate, + frameSize: 960, // 60ms @ 16kHz = 60 * 16 = 960 samples + maxPacketSize: 4000, // 最大包大小 + module: mod, + + // 初始化编码器 + init: function () { + try { + // 获取编码器大小 + const encoderSize = mod._opus_encoder_get_size(this.channels); + log(`Opus编码器大小: ${encoderSize}字节`, 'info'); + + // 分配内存 + this.encoderPtr = mod._malloc(encoderSize); + if (!this.encoderPtr) { + throw new Error("无法分配编码器内存"); + } + + // 初始化编码器 + const err = mod._opus_encoder_init( + this.encoderPtr, + this.sampleRate, + this.channels, + application + ); + + if (err < 0) { + throw new Error(`Opus编码器初始化失败: ${err}`); + } + + // 设置位率 (16kbps) + mod._opus_encoder_ctl(this.encoderPtr, 4002, 16000); // OPUS_SET_BITRATE + + // 设置复杂度 (0-10, 越高质量越好但CPU使用越多) + mod._opus_encoder_ctl(this.encoderPtr, 4010, 5); // OPUS_SET_COMPLEXITY + + // 设置使用DTX (不传输静音帧) + mod._opus_encoder_ctl(this.encoderPtr, 4016, 1); // OPUS_SET_DTX + + log("Opus编码器初始化成功", 'success'); + return true; + } catch (error) { + if (this.encoderPtr) { + mod._free(this.encoderPtr); + this.encoderPtr = null; + } + log(`Opus编码器初始化失败: ${error.message}`, 'error'); + return false; + } + }, + + // 编码PCM数据为Opus + encode: function (pcmData) { + if (!this.encoderPtr) { + if (!this.init()) { + return null; + } + } + + try { + const mod = this.module; + + // 为PCM数据分配内存 + const pcmPtr = mod._malloc(pcmData.length * 2); // 2字节/int16 + + // 将PCM数据复制到HEAP + for (let i = 0; i < pcmData.length; i++) { + mod.HEAP16[(pcmPtr >> 1) + i] = pcmData[i]; + } + + // 为输出分配内存 + const outPtr = mod._malloc(this.maxPacketSize); + + // 进行编码 + const encodedLen = mod._opus_encode( + this.encoderPtr, + pcmPtr, + this.frameSize, + outPtr, + this.maxPacketSize + ); + + if (encodedLen < 0) { + throw new Error(`Opus编码失败: ${encodedLen}`); + } + + // 复制编码后的数据 + const opusData = new Uint8Array(encodedLen); + for (let i = 0; i < encodedLen; i++) { + opusData[i] = mod.HEAPU8[outPtr + i]; + } + + // 释放内存 + mod._free(pcmPtr); + mod._free(outPtr); + + return opusData; + } catch (error) { + log(`Opus编码出错: ${error.message}`, 'error'); + return null; + } + }, + + // 销毁编码器 + destroy: function () { + if (this.encoderPtr) { + this.module._free(this.encoderPtr); + this.encoderPtr = null; + } + } + }; + + opusEncoder.init(); + return opusEncoder; + } catch (error) { + log(`创建Opus编码器失败: ${error.message}`, 'error'); + return false; + } } \ No newline at end of file diff --git a/main/xiaozhi-server/test/test_page.html b/main/xiaozhi-server/test/test_page.html index 03898608..c95ce98d 100644 --- a/main/xiaozhi-server/test/test_page.html +++ b/main/xiaozhi-server/test/test_page.html @@ -102,7 +102,8 @@