voice get

This commit is contained in:
Caikecheng
2025-10-13 20:53:16 +08:00
parent 92c18088f9
commit 5494b08a67
10 changed files with 3468 additions and 66 deletions

View File

@@ -0,0 +1,11 @@
class AudioProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
const input = inputs[0];
if (input.length > 0) {
this.port.postMessage(input[0]);
}
return true;
}
}
registerProcessor('audio-processor', AudioProcessor);

View File

@@ -4,7 +4,7 @@ let system_message_idx = 0
document.addEventListener('DOMContentLoaded', function () {
data = window.appData;
console.log(data);
socket = io('wss://hsamooc.com/agent', {
socket = io('wss://hsamooc.cn/agent', {
query: {
user_uuid: data.user_uuid,
folder: data.folder
@@ -18,6 +18,11 @@ document.addEventListener('DOMContentLoaded', function () {
OpenIframe();
});
// 监听来自服务器的消息
socket.on('voiceMessage', function (data) {
console.log(data)
const input = document.createElement('messageInput');
input.data = data['data'];
});
socket.on('message', function (data) {
// 显示服务器的回复消息
const serverMessage = document.createElement('div');
@@ -212,58 +217,76 @@ function handleMouseClick(event) {
stopRecording();
}
//发送语音
const startAudioRecording = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 16000,
channelCount: 1,
echoCancellation: true,
noiseSuppression: true
}
});
const mediaRecorder = new MediaRecorder(stream);
const audioContext = new AudioContext({
sampleRate: 16000
});
// Load the AudioWorklet
await audioContext.audioWorklet.addModule('/static/js/audio-processor.js');
const source = audioContext.createMediaStreamSource(stream);
const workletNode = new AudioWorkletNode(audioContext, 'audio-processor');
let audioChunks = [];
workletNode.port.onmessage = (e) => {
audioChunks.push(new Float32Array(e.data));
if (audioChunks.length >= 16) {
// 合并所有音频块
const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);
const mergedData = new Float32Array(totalLength);
let offset = 0;
for (const chunk of audioChunks) {
mergedData.set(chunk, offset);
offset += chunk.length;
}
const audioMessage = {
id: window.data.user_uuid,
type: 'audio',
data: mergedData,
sampleRate: 16000,
timestamp: Date.now()
};
socket.emit('voice', audioMessage);
console.log("voice sended")
audioChunks = [];
}
};
source.connect(workletNode);
workletNode.connect(audioContext.destination);
mediaRecorder.value = {
stop: () => {
source.disconnect();
workletNode.disconnect();
stream.getTracks().forEach(track => track.stop());
audioContext.close();
}
}
isRecording.value = true;
} catch (error) {
console.error('Error starting audio recording:', error);
}
};
function sendVoiceMessage() {
p = pyaudio.PyAudio()
startAudioRecording()
//变换录音中图标
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 毫秒检查一次录音状态
}
//停止录音