语音交互功能
This commit is contained in:
@@ -1,30 +1,52 @@
|
||||
|
||||
var socket;
|
||||
let system_message_idx=0
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let system_message_idx = 0
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
data = window.appData;
|
||||
console.log(data);
|
||||
socket = io('wss://hsamooc.com/agent',{
|
||||
query:{
|
||||
user_uuid:data.user_uuid,
|
||||
folder:data.folder
|
||||
socket = io('wss://hsamooc.com/agent', {
|
||||
query: {
|
||||
user_uuid: data.user_uuid,
|
||||
folder: data.folder
|
||||
}
|
||||
});
|
||||
socket.on('connect', function() {
|
||||
socket.on('connect', function () {
|
||||
console.log('Connected to server');
|
||||
socket.emit('login',JSON.stringify(data));
|
||||
socket.emit('login', JSON.stringify(data));
|
||||
OpenIframe();
|
||||
});
|
||||
// 监听来自服务器的消息
|
||||
socket.on('message', function(data) {
|
||||
socket.on('message', function (data) {
|
||||
// 显示服务器的回复消息
|
||||
const serverMessage = document.createElement('div');
|
||||
serverMessage.innerHTML = `<div><b>华实君</b></div><div class="chat-message server-message"> ${marked.parse(data)}</div>`;
|
||||
serverMessage.innerHTML = `<div><b>华实君:</b></div><div class="chat-message server-message"> ${marked.parse(data)}</div>`;
|
||||
document.getElementById('chatbox').appendChild(serverMessage);
|
||||
// 滚动到最新消息
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
});
|
||||
socket.on('request_function', function(data){
|
||||
|
||||
//监听来自服务器的消息
|
||||
/*socket.on('VoiceMessage', function(data) {
|
||||
console.log("Received voice answer message:", data);
|
||||
// 显示服务器的回复消息
|
||||
const VoiceMessage = document.createElement('div');
|
||||
VoiceMessage.className = 'chat-message server-message';
|
||||
VoiceMessage.innerHTML = `
|
||||
<div><b>华实君:</b></div>
|
||||
${marked.parse(data)}
|
||||
`;
|
||||
//插入与滚动
|
||||
document.getElementById('chatbox').appendChild(VoiceMessage);
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
});*/
|
||||
//监听来自服务器的语音识别消息
|
||||
socket.on('VoiceMessage', function (data) {
|
||||
console.log("Received voice message I sent from server:", data);
|
||||
//将语音转文字显示在输入框中
|
||||
document.getElementById('messageInput').value = data;
|
||||
});
|
||||
|
||||
socket.on('request_function', function (data) {
|
||||
try {
|
||||
console.log("request_function", data);
|
||||
if (typeof data === 'string') {
|
||||
@@ -43,18 +65,18 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
approveButton.innerHTML = '√'; // 使用勾选符号
|
||||
approveButton.data = element;
|
||||
approveButton.className = 'approve-button';
|
||||
approveButton.onclick = function(event) {
|
||||
console.log(element);
|
||||
socket.emit('message', JSON.stringify({ type: 'function', data: element }));
|
||||
event.currentTarget.disabled = true;
|
||||
event.currentTarget.classList.add('disabled');
|
||||
approveButton.onclick = function (event) {
|
||||
console.log(element);
|
||||
socket.emit('message', JSON.stringify({ type: 'function', data: element }));
|
||||
event.currentTarget.disabled = true;
|
||||
event.currentTarget.classList.add('disabled');
|
||||
};
|
||||
requestMessage.appendChild(description);
|
||||
requestMessage.appendChild(approveButton);
|
||||
|
||||
|
||||
document.getElementById('chatbox').appendChild(requestMessage);
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
}
|
||||
}
|
||||
// for (let i = 0; i < data.length; i++) {
|
||||
// const element = data[i]
|
||||
// const requestMessage = document.createElement('div');
|
||||
@@ -82,11 +104,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log(error)
|
||||
}
|
||||
});
|
||||
socket.on('next_chapter', function(data){
|
||||
socket.on('next_chapter', function (data) {
|
||||
next_chapter(data);
|
||||
});
|
||||
socket.on('chapter_score',function(data){
|
||||
if (typeof data === 'string'){
|
||||
socket.on('chapter_score', function (data) {
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
console.log(data)
|
||||
@@ -96,7 +118,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('chatbox').appendChild(scoreMessage);
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
})
|
||||
socket.on('system_message',function(data){
|
||||
socket.on('system_message', function (data) {
|
||||
console.log(data)
|
||||
let systemMessage = document.createElement('div');
|
||||
systemMessage.className = 'chat-message server-message';
|
||||
@@ -104,14 +126,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
closeButton.className = 'close-button'; // 可以根据需要添加样式类
|
||||
closeButton.innerHTML = '×'; // 关闭按钮的文本内容
|
||||
closeButton.style.marginRight = '10px'; // 可选:设置关闭按钮的右边距
|
||||
|
||||
|
||||
systemMessage.appendChild(closeButton);
|
||||
systemMessage.innerHTML += `<b>系统提示: </b> ${data}`;
|
||||
systemMessage.id = 'system_message_'+system_message_idx;
|
||||
system_message_idx+=1
|
||||
systemMessage.id = 'system_message_' + system_message_idx;
|
||||
system_message_idx += 1
|
||||
document.getElementById('chatbox').appendChild(systemMessage);
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
$('.close-button').on('click', function(event) {
|
||||
$('.close-button').on('click', function (event) {
|
||||
$(this).css({
|
||||
'background-color': 'gray',
|
||||
'color': 'white', // 可选:设置文字颜色为白色,以便更清晰地显示
|
||||
@@ -119,7 +141,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
$(this).parent().remove();
|
||||
});
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
$(systemMessage.id).remove();
|
||||
}, 5000); // 5000 毫秒 = 5 秒
|
||||
})
|
||||
@@ -130,7 +152,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const fontSizeSlider = document.getElementById('fontSizeSlider');
|
||||
const chatbox = document.getElementById('chatbox');
|
||||
|
||||
fontSizeSlider.addEventListener('input', function() {
|
||||
fontSizeSlider.addEventListener('input', function () {
|
||||
console.log(fontSizeSlider.value);
|
||||
chatbox.style.fontSize = fontSizeSlider.value + 'px';
|
||||
});
|
||||
@@ -144,27 +166,27 @@ const chineseLabel = document.getElementById('label_for_zh');
|
||||
let language = 'zh'; // 默认语言
|
||||
|
||||
// 切换语言事件
|
||||
englishRadio.addEventListener('change', function() {
|
||||
englishRadio.addEventListener('change', function () {
|
||||
if (englishRadio.checked) {
|
||||
language = 'en';
|
||||
englishLabel.classList.add('active');
|
||||
chineseLabel.classList.remove('active');
|
||||
}
|
||||
socket.emit('language',language);
|
||||
socket.emit('language', language);
|
||||
});
|
||||
|
||||
chineseRadio.addEventListener('change', function() {
|
||||
chineseRadio.addEventListener('change', function () {
|
||||
if (chineseRadio.checked) {
|
||||
language = 'zh';
|
||||
chineseLabel.classList.add('active');
|
||||
englishLabel.classList.remove('active');
|
||||
}
|
||||
socket.emit('language',language);
|
||||
socket.emit('language', language);
|
||||
});
|
||||
|
||||
|
||||
function sendInitiativeFunctionCall(function_name){
|
||||
socket.emit('initiative', {'name': function_name});
|
||||
function sendInitiativeFunctionCall(function_name) {
|
||||
socket.emit('initiative', { 'name': function_name });
|
||||
}
|
||||
// 发送消息
|
||||
function sendMessage() {
|
||||
@@ -180,7 +202,7 @@ function sendMessage() {
|
||||
document.getElementById('chatbox').appendChild(userMessage);
|
||||
|
||||
// 发送消息到服务器
|
||||
socket.emit('message', JSON.stringify({data: input, type:'text'}));
|
||||
socket.emit('message', JSON.stringify({ data: input, type: 'text' }));
|
||||
|
||||
// 清空输入框
|
||||
document.getElementById('messageInput').value = '';
|
||||
@@ -189,6 +211,107 @@ function sendMessage() {
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
}
|
||||
|
||||
let voice_stream = null;
|
||||
let audio_chunks = [];
|
||||
let isRecording = false;
|
||||
let recordingInterval = null;
|
||||
//鼠标点击终止事件
|
||||
function handleMouseClick(event) {
|
||||
event.stopPropagation();
|
||||
stopRecording();
|
||||
}
|
||||
//发送语音
|
||||
function sendVoiceMessage() {
|
||||
p = pyaudio.PyAudio()
|
||||
//变换录音中图标
|
||||
document.getElementById('recordIcon').className = 'bi bi-record-circle';
|
||||
//监听
|
||||
document.addEventListener('click', handleMouseClick);
|
||||
//打开麦克风流
|
||||
voice_stream = p.open(format = FORMAT,
|
||||
channels = CHANNELS,
|
||||
rate = SAMPLE_RATE,
|
||||
input = True,
|
||||
frames_per_buffer = CHUNK_SIZE)
|
||||
audio_chunks = []
|
||||
|
||||
recordingInterval = setInterval(() => {
|
||||
if (!isRecording) {
|
||||
clearInterval(recordingInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 从麦克风读取音频数据
|
||||
let audio_data = voice_stream.read(CHUNK_SIZE);
|
||||
|
||||
if (!audio_data || audio_data.length === 0) {
|
||||
stopRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
// 保留原始 PCM 二进制数据
|
||||
audio_chunks.push(audio_data);
|
||||
|
||||
// 当积累到一定数量的块时,发送
|
||||
if (audio_chunks.length >= 16) {
|
||||
const merged_data = new Uint8Array(
|
||||
audio_chunks.reduce((acc, chunk) => acc + chunk.length, 0)
|
||||
);
|
||||
let offset = 0;
|
||||
for (const chunk of audio_chunks) {
|
||||
merged_data.set(new Uint8Array(chunk), offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
// 发送到服务器
|
||||
socket.emit('VoiceMessage', merged_data, { binary: true });
|
||||
// 清空音频块,准备接收下一批数据
|
||||
audio_chunks = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading audio data:', error);
|
||||
stopRecording();
|
||||
}
|
||||
}, 10); // 每 10 毫秒检查一次录音状态
|
||||
}
|
||||
|
||||
//停止录音
|
||||
function stopRecording() {
|
||||
if (!isRecording) return;
|
||||
isRecording = false;
|
||||
//取消监听
|
||||
document.removeEventListener('click', handleMouseClick);
|
||||
//取消定时器
|
||||
if (recordingInterval) {
|
||||
clearInterval(recordingInterval);
|
||||
recordingInterval = null;
|
||||
}
|
||||
//变换图标
|
||||
document.getElementById('recordIcon').className = 'bi bi-mic';
|
||||
//发送剩下的数据
|
||||
if (audio_chunks.length > 0) {
|
||||
const merged_data = new Uint8Array(audio_chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
let offset = 0;
|
||||
for (const chunk of audio_chunks) {
|
||||
merged_data.set(new Uint8Array(chunk), offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
socket.emit('VoiceMessage', JSON.stringify({ data: merged_data, type: 'audio' }));
|
||||
audio_chunks = [];
|
||||
}
|
||||
overlay.style.display = 'none';
|
||||
//关闭音频流
|
||||
if (voice_stream) {
|
||||
voice_stream.stop_stream();
|
||||
voice_stream.close();
|
||||
voice_stream = null;
|
||||
}
|
||||
if (p) {
|
||||
p.terminate();
|
||||
p = null;
|
||||
}
|
||||
}
|
||||
|
||||
let currentChapterIndex = -1;
|
||||
function next_chapter(data) {
|
||||
// 获取所有的 h3 元素
|
||||
@@ -231,22 +354,22 @@ function next_chapter(data) {
|
||||
function generateProgressBar(N, idx, titles) {
|
||||
const container = document.getElementById('markdown-content-process');
|
||||
container.innerHTML = ''; // 清空内容
|
||||
|
||||
|
||||
// 创建进度条的外框
|
||||
const progressBox = document.createElement('div');
|
||||
progressBox.className = 'progress-box';
|
||||
|
||||
|
||||
// 获取详细信息容器
|
||||
const detailBox = document.getElementById('progress-detail');
|
||||
|
||||
|
||||
// 根据N值动态调整每个进度节点的宽度
|
||||
const nodeWidth = 100 / N; // 每个节点的宽度为 100% / N
|
||||
|
||||
|
||||
// 根据N值生成子框并设置颜色
|
||||
for (let i = 0; i < N; i++) {
|
||||
const titleBox = document.createElement('div');
|
||||
titleBox.className = 'progress-title';
|
||||
|
||||
|
||||
// 设置进度条的颜色
|
||||
if (i < idx) {
|
||||
titleBox.classList.add('green'); // 已完成部分
|
||||
@@ -261,7 +384,7 @@ function generateProgressBar(N, idx, titles) {
|
||||
titleBox.style.width = `${nodeWidth}%`;
|
||||
|
||||
// 监听鼠标移入事件来显示详情
|
||||
titleBox.addEventListener('mouseenter', function() {
|
||||
titleBox.addEventListener('mouseenter', function () {
|
||||
detailBox.innerHTML = `Step ${i + 1}: ${titles[i] || "No title"}`;// - Additional details here...`;
|
||||
detailBox.style.display = 'block';
|
||||
// 根据进度条位置显示详情
|
||||
@@ -271,7 +394,7 @@ function generateProgressBar(N, idx, titles) {
|
||||
});
|
||||
|
||||
// 监听鼠标移出事件来隐藏详情
|
||||
titleBox.addEventListener('mouseleave', function() {
|
||||
titleBox.addEventListener('mouseleave', function () {
|
||||
detailBox.style.display = 'none';
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user