首页 > 其他分享 >如何用前端实现麦克风语音唤醒

如何用前端实现麦克风语音唤醒

时间:2024-06-06 10:35:09浏览次数:9  
标签:const log 麦克风 data 音频 eventName 语音 console 唤醒

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的简单网页</title>
    <!-- 这里可以添加CSS样式链接,例如:<link rel="stylesheet" href="styles.css"> -->
</head>

<body>
    <header>
        <h1>欢迎来到我的网页!</h1>
    </header>

    <main>
        <p>这是一个简单的语音唤醒页面示例。</p>
        <p>在页面授权后 可以在任意事件点对着麦克风说话 系统会自动收集你说话声音触发录音 将说话声音生成为audio出现在下方可以看到测试结果</p>
    </main>

    <footer>
        <p>版权所有 &copy; 2023 我的网站</p>
    </footer>

    <!-- 这里可以添加JavaScript脚本链接,例如:<script src="script.js"></script> -->
</body>

<script>


    class EventManager {
        constructor() {
            // 使用一个对象来存储事件和对应的回调函数  
            this.events = {};
        }

        // 添加事件监听器  
        on(eventName, callback) {
            if (!this.events[eventName]) {
                this.events[eventName] = [];
            }
            this.events[eventName].push(callback);
        }

        // 移除事件监听器  
        off(eventName, callback) {
            if (this.events[eventName]) {
                const index = this.events[eventName].indexOf(callback);
                if (index > -1) {
                    this.events[eventName].splice(index, 1);
                }
            }
        }

        // 触发事件  
        emit(eventName, ...args) {
            if (this.events[eventName]) {
                this.events[eventName].forEach(callback => {
                    callback(...args);
                });
            }
        }

        // 检查是否有某个事件  
        hasEvent(eventName) {
            return !!this.events[eventName];
        }

        // 移除所有事件监听器  
        removeAllListeners(eventName) {
            if (eventName) {
                delete this.events[eventName];
            } else {
                this.events = {};
            }
        }
    }


    class AudioTimer {

        // MediaRecorder实例
        mediaRecorder = null;

        // 关于音频处理器状态
        // 0 未启动 通常是因为没有获取到权限导致的
        // 1 麦克风启动中 
        // 2 在进行语音音频音量处理 对环境噪音进行过滤
        // 3 在请求服务器进行文字识别
        stateStyle = 0;

        /**
         * 创建一个用于存储录制数据的数组
         **/
        recordedChunks = [];

        audioContext = new (window.AudioContext || window.webkitAudioContext)();

        /**
         * 事件管理器
         * 1. onRms  // 声闻超出标准值 触发
         */
        $eventManager = new EventManager();

        /**
         * 零界点 进入录音保存模式
         */
        isRmsUp = false;

        /**
         * 关于RMS值的临界值
         */
        RMSVALUE = 0.05;

        /**
         * 构造函数
         */
        constructor() {
            // 获取权限
            this.getPermissions().then((stream) => {
                // 获取权限后才能正常运行

                // 创建一个MediaRecorder实例来录制音频
                this.createMediaRecorder(stream);

                // 开启麦克风定时启动监听音频流
                this.audioTimer();
            });

        }

        on(name, call) {
            // 绑定事件
            return this.$eventManager.on(name, call);
        }

        /**
         * 关于麦克风的循环监听流处理
         * 考虑到性能并发问题 采用流式队列处理器
         */
        audioTimer() {
            this.audioPeriodicity().then(() => {
                this.audioTimer();
            });
        }

        stop() {
            console.log("关闭音频监控")
            this.mediaRecorder.stop(2000);
        }

        start() {
            console.log("开启音频监控")
            this.mediaRecorder.start(2000);
        }

        /**
         * 启动一段音频处理周期
         */
        audioPeriodicity() {
            return new Promise((resovle, err) => {
                // 开启一个监听
                // 开始录制(可以指定选项,例如{ mimeType: 'audio/webm; codecs=opus' })  

                // 监听dataavailable事件以收集录制的数据  
                this.mediaRecorder.ondataavailable = (event) => {
                    if (event.data.size > 0 && event.data.size > 10000) {
                        // console.log("ondataavailableEvent", event);
                        console.log("ondataavailable", this.recordedChunks.length);

                        // 将阶段音频存入缓存中
                        this.addRecordedChunks(event.data);

                        // 处理一下缓存数据的RMS值
                        this.handleAudioRms(event.data).then((Rms) => {
                            console.log(Rms)
                            // 判断RMS值是否达到一定的零界点 如果达到了 就判断为是为近距离说话
                            if (Rms > this.RMSVALUE) {
                                // 如果是出发了零界点 则开启录制模式 
                                this.isRmsUp = true;
                            } else if (Rms < this.RMSVALUE) {
                                if (this.isRmsUp) {
                                    // 如果没有达到临界值 但是 又是录音状态则挂不必录音状态
                                    this.isRmsUp = false;

                                    // 然后将保存后的录音缓存开启并进行返回
                                    this.$eventManager.emit("onRms", this.recordedChunks.concat());

                                }
                                // 清除掉监听事件
                                this.mediaRecorder.ondataavailable = function () { };
                                // 然后清空缓存
                                this.clearRecordedChunks();
                                // 停止掉此次的监听
                                this.stop();
                                resovle();
                            }
                        }).catch(() => {
                            console.log("音频RMS计算出现了未知错误");
                            // 清除掉监听事件
                            this.mediaRecorder.ondataavailable = null;
                            // 停止掉此次的监听
                            this.stop();
                            // 然后清空缓存
                            this.clearRecordedChunks();

                            resovle();
                        })
                    } else {
                        console.log("因为音频size小于10000字节判断为关闭stop残留数据 执行清除");
                        // 清除掉监听事件
                        // this.mediaRecorder.ondataavailable = null;
                        // 停止掉此次的监听
                        // this.stop();
                        // 然后清空缓存
                        // this.clearRecordedChunks();

                        // resovle();
                    }
                };

                this.start(2000);
                // setTimeout(() => {
                //   // 停止录制(例如,在一段时间后或用户点击按钮时)  
                //   this.mediaRecorder.stop();

                //   // 处理一下数据的RMS值
                //   this.handleAudioRms().then((Rms) => {
                //     console.log(Rms)
                //     if (Rms > 0.01) {
                //       this.$eventManager.emit("onRms", Rms);
                //     }
                //   }).finally(() => {
                //     resovle();
                //   });
                // }, 3000); // 10秒后停止录制  


            })
        }

        /**
         * 对音频流进行RMS判断 
         * RMS是一种对一段音频进行峰值判断的计算方法
         * 当RMS达到一定值 我则认为是有效语音 不然则认为是环境音不做识别
         */
        async handleAudioRms(chunks) {
            console.log("处理音频RMS", this.recordedChunks.map((e) => e.size));
            // 创建一个Blob对象,它包含所有录制的数据块  this.recordedChunks
            const blob = new Blob(this.recordedChunks, { 'type': 'audio/ogg; codecs=opus' });

            // 解码音频 Blob  
            const audioBuffer = await this.audioContext.decodeAudioData(await blob.arrayBuffer());

            let rmsSum = 0;
            let numSamples = 0;

            // 遍历所有通道和样本  
            for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
                console.log("处理音频RMS最终数据Size", audioBuffer.numberOfChannels);
                let data = audioBuffer.getChannelData(audioBuffer.numberOfChannels - 1);
                data = data.slice(data.length - data.length / this.recordedChunks.length, data.length);
                for (let i = 0; i < data.length; i++) {
                    const sample = data[i];
                    rmsSum += sample * sample; // 计算平方和  
                    numSamples++;
                }
            }

            // 计算 RMS  
            const rms = Math.sqrt(rmsSum / numSamples);

            return rms;
        }

        /**
         * 添加音频buffer数据缓存
         * 缓存会保留10秒内的数据
         */
        addRecordedChunks(arrayBuffer) {
            const index = this.recordedChunks.push(arrayBuffer);
            // if (index > 1) {
            //   this.recordedChunks.splice(0, 1);
            // }
        }

        // 清除音频缓存
        clearRecordedChunks() {
            this.recordedChunks = [];
        }

        /**
         * 创建一个MediaRecorder实例来录制音频
         */
        createMediaRecorder(stream) {
            // 创建一个MediaRecorder实例来录制音频  
            const mediaRecorder = new MediaRecorder(stream);
            this.mediaRecorder = mediaRecorder;

            // 监听dataavailable事件以收集录制的数据  
            // mediaRecorder.ondataavailable = (event) => {
            //   if (event.data.size > 0) {
            //     console.log("ondataavailable", this.recordedChunks.length);

            //     this.addRecordedChunks(event.data);
            //   }
            // };

            // 监听stop事件以处理录制完成  
            // mediaRecorder.onstop = function () {
            //   // 创建一个Blob对象,它包含所有录制的数据块  
            //   const blob = new Blob(recordedChunks, { 'type': 'audio/ogg; codecs=opus' });

            //   // 创建一个指向Blob的URL,该URL可用于在浏览器中播放音频  
            //   // const audioURL = URL.createObjectURL(blob);  

            //   // 你可以在这里使用audioURL,例如将其设置为audio元素的src属性  
            //   // const audioElement = document.createElement('audio');  
            //   // audioElement.controls = true;  
            //   // audioElement.src = audioURL;  
            //   // document.body.appendChild(audioElement);  

            //   // 添加到音频处理器
            //   // window.testAudio(recordedChunks);

            //   // var reader = new FileReader()
            //   // reader.onload = function() {
            //   //     console.log(this.result)
            //   window.testAudio(blob);
            //   // }
            //   // reader.readAsArrayBuffer(blob)

            //   // 清理资源(可选)  
            //   // URL.revokeObjectURL(audioURL);  
            // };
        }

        /**
         * 初始化麦克风权限 判断是否存在权限
         */
        getPermissions() {
            // 请求访问麦克风  
            return navigator.mediaDevices.getUserMedia({ audio: true })
                .catch(function (err) {
                    console.log('无法访问麦克风: ' + err);
                });
        }
    }

    const audios = new AudioTimer();

    audios.on("onRms", (chunks) => {
        debugger;
        console.log("触发语音唤醒输入")

        // 创建一个Blob对象,它包含所有录制的数据块  
        const blob = new Blob(chunks, { 'type': 'audio/ogg; codecs=opus' });

        // 创建一个指向Blob的URL,该URL可用于在浏览器中播放音频  
        const audioURL = URL.createObjectURL(blob);

        const audio = document.getElementsByTagName("audio")[0];
        audio && audio.remove();

        // 你可以在这里使用audioURL,例如将其设置为audio元素的src属性  
        const audioElement = document.createElement('audio');
        audioElement.controls = true;
        audioElement.src = audioURL;
        document.body.appendChild(audioElement);
    })


</script>

</html>

 

1. 思路大概就是 监听麦克风  然后根据麦克风捕捉到的音频RMS值来判断是否是高音  如果是 则判断为近距离说话  从而进行捕捉截取 

后续 我是采用的第三方语音识别来转化为文字  在使用 pinyin库 转化为拼音来匹配识别的。。

标签:const,log,麦克风,data,音频,eventName,语音,console,唤醒
From: https://www.cnblogs.com/liao1992/p/18234626

相关文章

  • 【语音处理】声音信号频谱分析仪(时域分析 频域分析)【含GUI Matlab源码 4627期】
    ✅博主简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,Matlab项目合作可私信。......
  • 实验19-使用keras完成语音识别
      wavs_to_model.pyimportwaveimportnumpyasnpimportosimportkerasfromkeras.modelsimportSequentialfromkeras.layersimportDensenum_class=0#加载的语音文件有几种类别labsIndName=[]##训练集标签的名字["seven","stop"]#加载数......
  • 【手把手教学】最新ChatTTS语音合成项目使用指南AI变声器chatTTS教程来了!5S夺走你的卧
    像这种充满语气充满感情色彩的人声,再搭配一段自拍图,是由最近大火的AI项目chatTTS生成的,ChatTTS是专门为对话场景设计的文本转语音模型,例如LLM助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。在HuggingFace中开源的版本为4万小......
  • Navi日语社App一款支持日文OCR文字识别提取的应用,功能丰富,支持日语翻译、语音翻译、日
    如果你正在寻找一款简单好用、功能丰富的日文OCR识别软件,那么推荐你试试《Navi日语社》App,在安卓和苹果手机上,很多应用都支持免费的日语翻译功能,但是支持日文OCR文字识别的软件并不多,针对这一痛点,准橙翻译开发上线了《Navi日语社》App,一款支持日文OCR识别提取文字的移动软件,识......
  • 老挝语翻译通App中国人出门在外都在用的老挝语翻译工具,支持老挝文OCR识别、文字转语音
    老挝语翻译通App,一款更加符合中国人用语习惯的翻译工具,在国内外都能正常使用的翻译器。当大家选择去东南亚国家旅游、GAP的时候,老挝这个国家是值得一去的,可以让大家感受到另一番风情。但是,在去之前,需要做一些准备:衣食住行都要提前规划和准备好,而贯穿整个旅途的另一个容易被......
  • 最像真人的语音转文本ChatTTS本地部署+Colab部署+跳坑指南
    1.WindowsChatTTSUI:https://github.com/jianchang512/ChatTTS-ui/releases/tag/v0.85v0.85完整包下载(2.7G含模型)百度网盘下载: https://pan.baidu.com/s/1RntYLT6UNd8_ew2osy8d1A?pwd=by14123网盘下载:https://www.123pan.com/s/03Sxjv-oEyB3.htmlhuggingfacelink: h......
  • 杰理语音芯片AC1042A,变声喇叭玩具方案—云信通讯
    变声喇叭玩具内置多种声音效果,例如机器人声、怪兽声、动物声以及各种搞笑声,让孩子能够在玩耍过程中体验不同的声音变化。有一些变声喇叭还可以模拟名人声音,让孩子们仿佛变身成为自己心目中的英雄或者明星。无论是自由的想象力游戏还是模仿他人的声音,变声喇叭玩具都能让孩子乐在......
  • 一键云部署:资源编排 ROS 轻松部署文本转语音系统 ChatTTS
    ChatTTS是专门为对话场景设计的文本转语音模型,例如LLM助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。ChatTTSwebUI&API为ChatTTS提供了网页界面和API服务。资源编排服务(ResourceOrchestrationService,ROS)是阿里云提供基......
  • 一周万星的文本转语音开源项目「GitHub 热点速览」
    上周的热门开源项目让我想起了「图灵测试」,测试者在不知道对面是机器还是人类的前提下随意提问,最后根据对方回复的内容,判断与他们交谈的是人还是计算机。如果无法分辨出回答者是机器还是人类,则说明机器已通过测试,具有人类的智力水平。​虽然现在大模型的回答还充满AI“味”,可......
  • 实时的语音降噪神经网络算法
    概要现代基于深度学习的模型在语音增强任务方面取得了显著的性能改进。然而,最先进模型的参数数量往往太大,无法部署在现实世界应用的设备上。为此,我们提出了微小递归U-Net(TRU-Net),这是一种轻量级的在线推理模型,与当前最先进的模型的性能相匹配。TRU-Net的量化版本的大小为362千字节......