如何使用JavaScript无需Speech Recognition API检测用户说话起止时间?
基于Web Audio API的语音起止时间检测实现
本方案不使用兼容性较差的Speech Recognition API,仅依赖跨浏览器支持度更高的Web Audio API实现说话起始、结束时间检测,无需调用云端接口,所有计算均在本地完成。
核心实现逻辑
- 调用
getUserMedia申请用户麦克风权限,获取音频输入流 - 通过
AnalyserNode实时获取音频时域数据,计算当前音量值 - 设定音量阈值和静默判定时长:音量超过阈值时标记为说话开始,音量持续低于阈值达到预设时长时标记为说话结束
完整代码实现
class SpeechTimeDetector { constructor(options = {}) { // 可调参数 this.volumeThreshold = options.volumeThreshold || 0.15; // 音量阈值 this.silenceDuration = options.silenceDuration || 500; // 静默判定时长(ms) this.pollingInterval = options.pollingInterval || 20; // 检测轮询间隔(ms) // 状态变量 this.isSpeaking = false; this.speechStartTime = null; this.speechEndTime = null; this.silenceTimer = null; // 音频相关实例 this.audioContext = null; this.analyser = null; this.mediaStreamSource = null; this.dataArray = null; } // 初始化,需要在用户交互事件(如click)中调用,避免浏览器限制AudioContext自动启动 async init() { try { // 请求麦克风权限 const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); // 初始化音频上下文 this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); this.analyser = this.audioContext.createAnalyser(); this.analyser.fftSize = 2048; // 连接音频流 this.mediaStreamSource = this.audioContext.createMediaStreamSource(stream); this.mediaStreamSource.connect(this.analyser); this.dataArray = new Uint8Array(this.analyser.fftSize); this._startDetect(); return true; } catch (err) { console.error('初始化失败:', err); throw err; } } // 计算当前音量 _getCurrentVolume() { this.analyser.getByteTimeDomainData(this.dataArray); let sum = 0; for (let i = 0; i < this.dataArray.length; i++) { const normalized = (this.dataArray[i] - 128) / 128; // 归一化到[-1,1] sum += normalized * normalized; } return Math.sqrt(sum / this.dataArray.length); // 返回均方根值作为音量 } // 开始检测 _startDetect() { setInterval(() => { const currentVolume = this._getCurrentVolume(); if (currentVolume > this.volumeThreshold) { // 音量超过阈值 if (this.silenceTimer) { clearTimeout(this.silenceTimer); this.silenceTimer = null; } if (!this.isSpeaking) { // 说话开始 this.isSpeaking = true; this.speechStartTime = Date.now(); console.log('说话开始:', new Date(this.speechStartTime)); // 这里可以自定义说话开始的回调逻辑 } } else { // 音量低于阈值 if (this.isSpeaking && !this.silenceTimer) { this.silenceTimer = setTimeout(() => { // 静默达到设定时长,说话结束 this.isSpeaking = false; this.speechEndTime = Date.now(); console.log('说话结束:', new Date(this.speechEndTime)); console.log('说话时长:', this.speechEndTime - this.speechStartTime, 'ms'); // 这里可以自定义说话结束的回调逻辑 }, this.silenceDuration); } } }, this.pollingInterval); } // 销毁实例,释放资源 destroy() { if (this.mediaStreamSource) this.mediaStreamSource.disconnect(); if (this.audioContext) this.audioContext.close(); this.isSpeaking = false; } } // 使用示例,需要绑定到用户交互事件,比如按钮点击 // document.querySelector('#startBtn').addEventListener('click', async () => { // const detector = new SpeechTimeDetector({ // volumeThreshold: 0.18, // silenceDuration: 600 // }); // await detector.init(); // })
参数调整说明
volumeThreshold:音量判定阈值,范围0-1,安静环境建议设置为0.1-0.2,嘈杂环境可适当提高silenceDuration:静默判定时长,单位ms,对话场景建议设置为300-800ms,避免说话停顿被误判为结束pollingInterval:检测轮询间隔,单位ms,建议设置为10-50ms,数值越小检测灵敏度越高,性能消耗也越大
兼容性说明
本方案依赖的Web Audio API支持Chrome 35+、Firefox 43+、Safari 14.1+、Edge 79+,全球用户覆盖率超过95%,远高于Speech Recognition API的兼容性。
内容的提问来源于stack exchange,提问作者Витязи Team
相关产品推荐
相关产品推荐

