![SenseVoicecpp http 分片服务[AI人工智能(七十五)]—东方仙盟](http://pic.xiahunao.cn/yaotu/SenseVoicecpp http 分片服务[AI人工智能(七十五)]—东方仙盟)
核心代码完整代码!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title未来之窗-SenseVoice-CPP 8 语音client testa/title style * { box-sizing: border-box; margin: 0; padding: 0; font-family: Microsoft YaHei, sans-serif; } body { max-width: 800px; margin: 30px auto; padding: 0 20px; background: #f5f7fa; } .container { background: white; padding: 25px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } h2 { text-align: center; color: #333; margin-bottom: 25px; } .panel { margin: 20px 0; padding: 15px; border: 1px solid #eee; border-radius: 8px; } .panel h3 { color: #409eff; margin-bottom: 12px; font-size: 16px; } .btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; margin-right: 10px; background: #409eff; color: white; } .btn:disabled { background: #ccc; cursor: not-allowed; } .red { background: #f56c6c; } .green { background: #67c23a; } .status { margin: 10px 0; padding: 8px 12px; border-radius: 6px; font-size: 13px; background: #f0f2f5; } .result-box { margin-top: 15px; padding: 15px; min-height: 80px; border: 1px dashed #999; border-radius: 6px; white-space: pre-wrap; color: #333; } input[typefile] { margin: 10px 0; } /style /head body div classcontainer h2未来之窗-SenseVoice-CPP 8 分片技术 语音client /h2 div classpanel h3 实时麦克风录音HTTP短连接 0.7s/次/h3 button classbtn green idstartRecord开始录音/button button classbtn red idstopRecord disabled停止录音/button div classstatus idstreamStatus等待录音/div div classresult-box idrealResult实时识别结果/div /div div classpanel h3音频文件上传HTTP POST/h3 input typefile idaudioFile acceptaudio/* button classbtn iduploadBtn上传并识别/button div classstatus iduploadStatus等待选择文件/div div classresult-box idfileResult文件识别结果/div /div /div script // 可配置变量你要的时长都在这里 const UPLOAD_INTERVAL_SEC 0.7; // 每0.7秒发送一次 const OVERLAP_SEC 0.3; // 保留上一段0.3秒 const TOTAL_CHUNK_SEC 1.0; // 每次发送总时长1秒 const SAMPLE_RATE 16000; // 采样率 const CHANNEL_COUNT 1; // 单声道 const SHORT_URL http://127.0.0.1:20369/asr; // 短连接接口 const HTTP_UPLOAD_URL http://127.0.0.1:20369/asr; // let isRecording false; let audioContext null; let mediaStream null; let workletNode null; let sendTimer null; // 音频缓存 let audioBuffer []; const overlapSampleCount Math.floor(SAMPLE_RATE * OVERLAP_SEC); const chunkSampleCount Math.floor(SAMPLE_RATE * TOTAL_CHUNK_SEC); // DOM const startRecord document.getElementById(startRecord); const stopRecord document.getElementById(stopRecord); const streamStatus document.getElementById(streamStatus); const realResult document.getElementById(realResult); const audioFile document.getElementById(audioFile); const uploadBtn document.getElementById(uploadBtn); const uploadStatus document.getElementById(uploadStatus); const fileResult document.getElementById(fileResult); // 动态创建AudioWorklet const createAudioWorkletModule () { const processorCode class AudioCaptureProcessor extends AudioWorkletProcessor { process(inputs) { const input inputs[0]; if (input.length 0) this.port.postMessage(input[0]); return true; } } registerProcessor(audio-capture-processor, AudioCaptureProcessor); ; return URL.createObjectURL(new Blob([processorCode], { type: application/javascript })); }; // 浮点数转16位PCM const float32ToInt16 (float32Array) { const int16 new Int16Array(float32Array.length); for (let i 0; i float32Array.length; i) { const v Math.max(-1, Math.min(1, float32Array[i])); int16[i] v 0 ? v * 0x8000 : v * 0x7FFF; } return int16; }; // 发送音频片段短连接POST const sendAudioChunk async (pcmData) { try { const res await fetch(SHORT_URL, { method: POST, headers: { Content-Type: application/octet-stream }, body: pcmData.buffer }); const text await res.text(); realResult.textContent 实时识别结果\n text; } catch (e) { console.error(发送失败, e); } }; // 开始录音 startRecord.onclick async () { if (isRecording) return; isRecording true; startRecord.disabled true; stopRecord.disabled false; streamStatus.textContent 录音中0.7s发送一次; audioBuffer []; try { mediaStream await navigator.mediaDevices.getUserMedia({ audio: { sampleRate: SAMPLE_RATE, channelCount: CHANNEL_COUNT } }); audioContext new AudioContext({ sampleRate: SAMPLE_RATE }); await audioContext.audioWorklet.addModule(createAudioWorkletModule()); workletNode new AudioWorkletNode(audioContext, audio-capture-processor); const source audioContext.createMediaStreamSource(mediaStream); source.connect(workletNode); // 收集音频 workletNode.port.onmessage (e) { if (!isRecording) return; audioBuffer.push(...e.data); }; // 定时器每0.7秒发一次 sendTimer setInterval(() { if (audioBuffer.length chunkSampleCount) { // 取最后1秒数据 const chunk audioBuffer.slice(-chunkSampleCount); const pcm float32ToInt16(new Float32Array(chunk)); sendAudioChunk(pcm); // 保留最后0.3秒用于下一次重叠 audioBuffer audioBuffer.slice(-overlapSampleCount); } }, UPLOAD_INTERVAL_SEC * 1000); } catch (err) { console.error(录音失败, err); streamStatus.textContent ❌ 启动失败 err.message; isRecording false; startRecord.disabled false; stopRecord.disabled true; } }; // 停止录音 stopRecord.onclick () { if (!isRecording) return; isRecording false; clearInterval(sendTimer); if (mediaStream) mediaStream.getTracks().forEach(t t.stop()); if (audioContext) audioContext.close(); if (workletNode) workletNode.port.close(); audioBuffer []; startRecord.disabled false; stopRecord.disabled true; streamStatus.textContent ✅ 已停止; }; // 文件上传 audioFile.onchange () { if (audioFile.files.length) { uploadStatus.textContent 已选择 audioFile.files[0].name; } }; uploadBtn.onclick async () { const file audioFile.files[0]; if (!file) { uploadStatus.textContent ❌ 请选择文件; return; } uploadStatus.textContent ⏳ 上传中...; const fd new FormData(); fd.append(audio, file); try { const res await fetch(HTTP_UPLOAD_URL, { method: POST, body: fd }); const text await res.text(); fileResult.textContent 文件识别结果\n text; uploadStatus.textContent ✅ 识别完成; } catch (e) { uploadStatus.textContent ❌ 上传失败; } }; /script /body /html人人皆为创造者共创方能共成长每个人都是使用者也是创造者是数字世界的消费者更是价值的生产者与分享者。在智能时代的浪潮里单打独斗的发展模式早已落幕唯有开放连接、创意共创、利益共享才能让个体价值汇聚成生态合力让技术与创意双向奔赴实现平台与伙伴的快速成长、共赢致远。原创永久分成共赴星辰大海原创创意共创、永久收益分成是东方仙盟始终坚守的核心理念。我们坚信每一份原创智慧都值得被尊重与回馈以永久分成锚定共创初心让创意者长期享有价值红利携手万千伙伴向着科技星辰大海笃定前行拥抱硅基 生命与数字智能交融的未来共筑跨越时代的数字文明共同体。东方仙盟拥抱知识开源共筑数字新生态在全球化与数字化浪潮中东方仙盟始终秉持开放协作、知识共享的理念积极拥抱开源技术与开放标准。我们相信唯有打破技术壁垒、汇聚全球智慧才能真正推动行业的可持续发展。开源赋能中小商户通过将前端异常检测、跨系统数据互联等核心能力开源化东方仙盟为全球中小商户提供了低成本、高可靠的技术解决方案让更多商家能够平等享受数字转型的红利。共建行业标准我们积极参与国际技术社区与全球开发者、合作伙伴共同制定开放协议 与技术规范推动跨境零售、文旅、餐饮等多业态的系统互联互通构建更加公平、高效的数字生态。知识普惠共促发展通过开源社区 、技术文档与培训体系东方仙盟致力于将前沿技术转化为可落地的行业实践赋能全球合作伙伴共同培育创新人才推动数字经济 的普惠式增长阿雪技术观在科技发展浪潮中我们不妨积极投身技术共享。不满足于做受益者更要主动担当贡献者。无论是分享代码、撰写技术博客还是参与开源项目 维护改进每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地我们携手在此探索硅基 生命为科技进步添砖加瓦。Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Dont just be the one reaping all the benefits; step up and be a contributor too. Whether youre tossing out your code snippets , hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. Were gonna team up and explore the whole silicon - based life thing, and in the process, well be fueling the growth of technology