大数跨境

重叠滑动/口语韵律感知实时语音识别-[AI人工智能(八十六)]—东方仙盟

重叠滑动/口语韵律感知实时语音识别-[AI人工智能(八十六)]—东方仙盟 未来之窗软件服务中心
2026-04-11
5
A real-time speech recognition optimization scheme based on overlapped sliding window and audio chunking technology.Combined with speech activity detection (VAD) and linguistic prosody perception principles, it uses adaptive silence detection and segment control to simulate natural human speaking rhythm.By adopting overlap chunk merging and text deduplication algorithm, it ensures low-latency response and continuous recognition output, achieving a humanized and smooth real-time interaction experience.
  • Overlapped Sliding Window
     重叠滑动窗口
  • Audio Chunking
     音频分片
  • VAD (Voice Activity Detection)
     语音活动检测
  • Linguistic Prosody Perception
     语言韵律感知
  • Text Deduplication & Merging
     文本去重与合并
  • Silence Detection
     静音检测
  • Humanized Rhythm Control
     人性化节奏控制



    // ====================== 可配置变量(你要的全部在这里) ======================    const CHUNK_TIME        = 0.8;     // 每0.8秒上传一次分片    const FINAL_WINDOW      = 2.3;     // 2.5秒窗口合并最终结果    const SILENCE_TIMEOUT   = 1.5;     // 静音1.5秒自动收尾    const OVERLAP_SEC       = 0.3;     // 音频重叠    const SAMPLE_RATE       = 16000;   // 采样率 11776    const HTTP_UPLOAD_URL   = "http://127.0.0.1:20369/asr";

     const fais_asr_timemaxlen      = 2.3;     // 2.5秒窗口合并最终结果    // =========================================================================
    const CHUNK = Math.floor(SAMPLE_RATE * CHUNK_TIME);     // 0.8s = 12800    const FINAL = Math.floor(7000 * FINAL_WINDOW);   // 2.5s = 40000
    let isRecording = false;    let audioContext, mediaStream, workletNode;    let audioBuffer = [];    let textBuffer = [];    let finalText = "";    let silenceTimer = null;
    const startRecord = document.getElementById("startRecord");    const stopRecord = document.getElementById("stopRecord");    const streamStatus = document.getElementById("streamStatus");    const tempResult = document.getElementById("tempResult");    const finalResult = document.getElementById("finalResult");    const audioFile = document.getElementById("audioFile");    const uploadBtn = document.getElementById("uploadBtn");    const uploadStatus = document.getElementById("uploadStatus");    const fileResult = document.getElementById("fileResult");
    const createAudioWorkletModule = () => {        const code = `            class AudioCaptureProcessor extends AudioWorkletProcessor {                process(inputs) { const i=inputs[0];i.length&&this.port.postMessage(i[0]);return true; }            }            registerProcessor('audio-capture-processor', AudioCaptureProcessor);        `;        return URL.createObjectURL(new Blob([code], { type'application/javascript' }));    };
    const float32ToInt16 = (float32Array) => {        const int16Array = new Int16Array(float32Array.length);        for (let i=0; i<float32Array.length; i++) {            const v = Math.max(-1Math.min(1, float32Array[i]));            int16Array[i] = v < 0 ? v * 0x8000 : v * 0x7FFF;        }        return int16Array;    };
    // 去重重叠文字    function deduplicateOverlap(prev, curr) {        if (!prev || !curr) return curr;        const minLen = Math.min(prev.length, curr.length);        for (let i = minLen; i >= 1; i--) {            if (prev.slice(-i) === curr.slice(0, i)) {                return curr.slice(i);            }        }        return curr;    }
    async function sendChunk(floatData) {        const pcm = float32ToInt16(floatData);        const blob = new Blob([pcm.buffer], { type"audio/pcm" });        const fd = new FormData();        fd.append("audio", blob, "chunk.pcm");
        $cq.ajax({            urlHTTP_UPLOAD_URL,            type"POST",            dataType"JSON",            data: fd,            success(response) => {                if (!isRecording) return;
                let text = response.message || "";                const clean = deduplicateOverlap(textBuffer[textBuffer.length-1] || "", text);                textBuffer.push(clean);
                // 实时显示(保留)                tempResult.textContent = "【实时识别】\n" + textBuffer.join('');
                  console.log("rt textBuffer",textBuffer);
                    tempResult.textContent = "【实时识别】\n" + textBuffer.join('');
                     console.log("diff audioBuffer.length"+audioBuffer.length+","+SAMPLE_RATE * FINAL_WINDOW); console.log("diff floatData.length"+floatData.length+","+7000 * FINAL_WINDOW);

// 新的(正确:看发送的音频长度是否满 2.5s)const isFullWindow = (floatData.length >= FINAL);console.log("是否满2.5s窗口:", isFullWindow, floatData.length,"FINAL="+FINAL);
                // ====================== 【修复点1】判断当前发送音频是否满2.5秒 ======================             //   const currentAudioSec = floatData.length / SAMPLE_RATE;                  const currentAudioSec = floatData.length / 7000;                if (currentAudioSec >= FINAL_WINDOW) {                    finalText += " " + clean;                    finalResult.textContent = "【最终定稿】\n" + finalText;                    textBuffer = [];                }
                resetSilenceTimer();            },            error(e) => console.error("上传失败", e)        });    }
    function resetSilenceTimer() {        clearTimeout(silenceTimer);        silenceTimer = setTimeout(() => {            if (textBuffer.length > 0) {                finalText += " " + textBuffer.join('');                finalResult.textContent = "【最终定稿】\n" + finalText;                textBuffer = [];            }        }, SILENCE_TIMEOUT * 1000);    }
    // 开始录音    startRecord.onclick = async () => {        if (isRecording) return;        isRecording = true;        startRecord.disabled = true;        stopRecord.disabled = false;        streamStatus.textContent = "🎙️ 录音中(0.8秒分片)";        audioBuffer = [];        textBuffer = [];
        mediaStream = await navigator.mediaDevices.getUserMedia({ audiotrue });        audioContext = new AudioContext({ sampleRateSAMPLE_RATE });        await audioContext.audioWorklet.addModule(createAudioWorkletModule());        workletNode = new AudioWorkletNode(audioContext, "audio-capture-processor");        const source = audioContext.createMediaStreamSource(mediaStream);        source.connect(workletNode);
        const chunkSamples = Math.floor(SAMPLE_RATE * CHUNK_TIME);        const overlapSamples = Math.floor(SAMPLE_RATE * OVERLAP_SEC);        const finalSamples = Math.floor(SAMPLE_RATE * FINAL_WINDOW);
        // ====================== 【修复点2】核心滑动窗口:累积音频,不被切碎 ======================        /*        workletNode.port.onmessage = (e) => {            if (!isRecording) return;            audioBuffer.push(...e.data);            resetSilenceTimer();
            // 只保留最近 2.5 秒,防止内存爆炸            if (audioBuffer.length > finalSamples) {                audioBuffer = audioBuffer.slice(-finalSamples);            }
            // 每 0.8s 发送一次【全部累积音频】            while (audioBuffer.length >= chunkSamples) {                sendChunk(audioBuffer); // 发送全部累积 = 越长识别越准                audioBuffer = audioBuffer.slice(chunkSamples); // 滑动0.8s            }        };        */        /*        workletNode.port.onmessage = (e) => {            if (!isRecording) return;            // 1. 持续累积音频(永不主动清空,只限制最大长度)            audioBuffer.push(...e.data);            resetSilenceTimer();
            const chunkSamples    = Math.floor(SAMPLE_RATE * CHUNK_TIME);      // 滑步:0.8s            const finalSamples    = Math.floor(SAMPLE_RATE * FINAL_WINDOW);    // 窗口:2.5s
            // 2. 限制最大长度 = 2.5s,避免无限增长            if (audioBuffer.length > finalSamples) {                audioBuffer = audioBuffer.slice(-finalSamples);            }
            // 3. 每满 0.8s → 发送【完整2.5s窗口音频】→ 只滑0.8s            while (audioBuffer.length >= chunkSamples) {                // ✅ 核心:每次发送 完整2.5s 音频(纯音频滑动窗口)                const sendAudio = audioBuffer.slice(-finalSamples); // 取最后2.5s                sendChunk(sendAudio);
                // ✅ 只滑动 0.8s(切掉头部 oldest 0.8s)                audioBuffer = audioBuffer.slice(chunkSamples);            }     };
     */
     /*     workletNode.port.onmessage = (e) => {            if (!isRecording) return;
            // 1. 持续累积新音频            audioBuffer.push(...e.data);            resetSilenceTimer();
            const CHUNK内部 = Math.floor(SAMPLE_RATE * CHUNK_TIME);     // 0.8s = 12800            const FINAL内部 = Math.floor(7000 * FINAL_WINDOW);   // 2.5s = 40000
            // ==============================================            // ✅ 【真正的滑动窗口】            // ==============================================            while (audioBuffer.length >= CHUNK内部) {                // 每次发送:取【最近 2.5s】音频                let sendAudio = audioBuffer.slice(-FINAL内部);                 sendChunk(sendAudio);
                // 只滑动:移除【最早 0.8s】                audioBuffer = audioBuffer.slice(CHUNK内部);            }        };
        */
        workletNode.port.onmessage = (e) => {            if (!isRecording) return;
            // 1. 不断追加新音频            audioBuffer.push(...e.data);            resetSilenceTimer();
            // ======================            // 你自己的配置            // ======================            const chunkStep   = Math.floor(SAMPLE_RATE * CHUNK_TIME);    // 0.8s 触发一次            const maxKeepLen  = Math.floor(SAMPLE_RATE * fais_asr_timemaxlen);           // 最大保留 2.3s
            // 2. 超过2.3s,只保留【最新2.3s】,前面的丢掉            if (audioBuffer.length > maxKeepLen) {                audioBuffer = audioBuffer.slice(-maxKeepLen); // 只截掉老的,保留最新一段完整            }
            // 3. 每够0.8s,发送【当前完整缓冲区(≈2.3s)】            if (audioBuffer.length >= chunkStep) {                sendChunk(audioBuffer); // 发送的是一段完整长音频,不是只发0.8s            }        };
        resetSilenceTimer();    };
    // 停止录音    stopRecord.onclick = () => {        isRecording = false;        clearTimeout(silenceTimer);        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);
        $cq.ajax({            urlHTTP_UPLOAD_URL,            type"POST",            dataType"JSON",            data: fd,            success(response) => {                let text = response.message || response.text || "";                fileResult.textContent = "文件识别结果:\n" + text;                uploadStatus.textContent = "✅ 识别完成";            },            error() => {                uploadStatus.textContent = "❌ 上传失败";            }        });    };



原创应该获得永久分成



原创创意共创、永久收益分成,是东方仙盟始终坚守的核心理念。我们坚信,每一份原创智慧都值得被尊重与回馈,以永久分成锚定共创初心,让创意者长期享有价值红利,携手万千伙伴向着科技星辰大海笃定前行,拥抱硅基 生命与数字智能交融的未来,共筑跨越时代的数字文明共同体。



  • 东方仙盟 

    东方仙盟:拥抱知识开源,共筑数字新生态

    在全球化与数字化浪潮中,东方仙盟始终秉持开放协作、知识共享的理念,积极拥抱开源技术与开放标准。我们相信,唯有打破技术壁垒、汇聚全球智慧,才能真正推动行业的可持续发展。

    开源赋能中小商户:通过将前端异常检测、跨系统数据互联等核心能力开源化,东方仙盟为全球中小商户提供了低成本、高可靠的技术解决方案,让更多商家能够平等享受数字转型的红利。
    共建行业标准:我们积极参与国际技术社区,与全球开发者、合作伙伴共同制定开放协议与技术规范,推动跨境零售、文旅、餐饮等多业态的系统互联互通,构建更加公平、高效的数字生态。
    知识普惠,共促发展:通过开源社区、技术文档与培训体系,东方仙盟致力于将前沿技术转化为可落地的行业实践,赋能全球合作伙伴,共同培育创新人才,推动数字经济 的普惠式增长

    阿雪技术观

    在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。


    Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're 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. We're gonna team up and explore the whole silicon - based life thing, and in the process, we'll be fueling the growth of technology.  

    开通方法

    图片


    关注我们

    营销资讯:抖音运营,微信公众号运营,小红书运营

    网站运营:安全、备案、申请网站,漏洞扫描,数字证书

    开发:开发技巧、热点技术,人工智能,数据分析,数据优化,docker

    数据服务:数据恢复,数据安全,数据融合,数据异地容灾,数据优化,数据自动备份、数据清洗

    人工智能:智能物联网,智慧大屏幕,OCR,智慧刷脸,语音交互,智能机器人,数字生命,数字人,大模型,本地化,边缘化智能(手机模型)

    支付:微信支付服务商,支付支付服务商,刷脸支付

    安全服务:WAF安全,网安扫描,漏洞扫描,安全补丁,防火墙定制

    智慧大屏:物资耗材大屏幕,单位用餐大数据,智慧场馆大屏,销售大屏幕,智慧社区大屏幕,大厅查询机,景区自助机

    智慧酒店:酒店系统、酒店押金、酒店房价牌、酒店门锁、布草系统

    行业软件:酒店、餐饮、便利店,美发、超市,批发,景区门票,道闸,堂食,配送系统,烘培系统,健身,美容系统,月子中心系统

    物联网:智能衣柜,足浴店衣柜,售货柜,酒店自助入住机

    国产化:uos系统答疑,国产软件开发,国产服务器配置,docker

    一体化:酒店一体化(闸机,酒店系统,餐饮系统,售票,布草,无人酒店,在线订房),景区一体化(门票、餐饮、住宿、押金、超市、药店、商铺租赁,通车系统,售票大厅,大屏幕,无人景区,景区自助机)


          

    图片

【声明】内容源于网络
0
0
未来之窗软件服务中心
在线工单、售后、配送查询、附近商家 业务范围:餐饮、酒店、KTV、洗浴、客房、美容美发、糕点、POS收银系统 ;商城、团购、分销、众筹、医疗、学校、美容 ;OA、CRM、HRM;智能WIFI、产品、商家推广
内容 233
粉丝 0
未来之窗软件服务中心 在线工单、售后、配送查询、附近商家 业务范围:餐饮、酒店、KTV、洗浴、客房、美容美发、糕点、POS收银系统 ;商城、团购、分销、众筹、医疗、学校、美容 ;OA、CRM、HRM;智能WIFI、产品、商家推广
总阅读766
粉丝0
内容233