语音播报_前端技术总结

应用前端语音唤醒机制简单研究、测试:音量、VAD等

近期开发一些应用,需要用到语音唤醒,做了一些简单的测试,代码基于React。

1,简单的语音操作

代码如下:

	// 新增:音量获取函数
	const getVolume = (stream: MediaStream) => {
	  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
	  const analyser = audioContext.createAnalyser();
	  const source = audioContext.createMediaStreamSource(stream);
	  source.connect(analyser);
	  
	  analyser.fftSize = ;  
	  const bufferLength = analyser.frequencyBinCount;
	  const dataArray = new Uint8Array(bufferLength);

	  const updateVolume = () => {
		analyser.getByteFrequencyData(dataArray);
		let sum = 0;
		for (let i = 0; i < bufferLength; i++) {
		  sum += dataArray[i];
		}
		const average = sum / bufferLength;
		setVolume(average);  
		requestAnimationFrame(updateVolume);
		// 逻辑:如果音量大于,则进行处理;
		// 逻辑:如果一段时间(1秒)内音量小于,则结束处理;
		if (average > ) {
			//开始声音处理
		}
		if (average < ) {
		  setTimeout(() => {
			if (average < ) {
			  //结束声音处理
			}
		  }, );
		}
	  };
	  updateVolume();
	};
    useEffect(() => {
        navigator.mediaDevices.getUserMedia({ audio: true })
            .then((stream) => {
                getVolume(stream);  // 开始监听音量
            })
            .catch((err) => {
                console.error(&#;Error accessing microphone: &#;, err);
            });
    }, []);
    

2, 增加防抖及过零点检测等机制

直接用音量处理发现无法区分语音与噪音,于是测试了平滑、频域限制、过零点检测等方式,发现过零点检测结合防抖机制效果稍好点。代码(React)如下:

	let intLowround: int = 0; //无声时间轮数
	
	const minEnergyThreshold = ; // 能量阈值
	const minZCRThreshold = ; // 过零率阈值

	// 新增:音量获取函数
	const getVolume = debounce((stream: MediaStream) => {
	  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
	  const analyser = audioContext.createAnalyser();
	  const source = audioContext.createMediaStreamSource(stream);
	  source.connect(analyser);

	  analyser.fftSize = ;  
	  const bufferLength = analyser.frequencyBinCount;
	  const dataArray = new Uint8Array(bufferLength);

	  const updateVolume = () => {
		analyser.getByteFrequencyData(dataArray);

		// 计算音频的能量(简单求和)
		let energy = 0;
		for (let i = 0; i < bufferLength; i++) {
		  energy += dataArray[i] ** 2;
		}
		const normalizedEnergy = Math.sqrt(energy / bufferLength);

		// 计算过零率(Zero Crossing Rate)
		let zeroCrossings = 0;
		for (let i = 1; i < bufferLength; i++) {
		  if ((dataArray[i - 1] >  && dataArray[i] < ) || (dataArray[i - 1] <  && dataArray[i] > )) {
			zeroCrossings++;
		  }
		}
		const zcr = zeroCrossings / bufferLength;

		// 判断是否是语音
		const isVoice = normalizedEnergy > minEnergyThreshold && zcr > minZCRThreshold;

		if (isVoice) {
		  setVolume(normalizedEnergy);
		} else {
		  setVolume(0); // 如果没有语音,设置音量为0
		}

		requestAnimationFrame(updateVolume);
		
		// 新增逻辑:如果音量大于startVolume,则调用语音处理逻辑
		if (!isRecording && !isProcessing && isVoice && normalizedEnergy>) {
		  //语音处理代码
		  intLowround = 0;
		}
		
		// 新增逻辑:如果一段时间(秒)内音量小于,则结束语音处理
		if (isRecording && !isProcessing){
			if(!isVoice && normalizedEnergy<) {
				if (intLowround >= ) {
					//结束语音处理
				} else {
					intLowround = intLowround + 1;
				}
			} else {
				intLowround = 0
			}
		}
	  };
	  updateVolume();
	}, );

	useEffect(() => {
	  navigator.mediaDevices.getUserMedia({ audio: true })
		.then((stream) => {
		  getVolume(stream);  // 开始监听音量
		})
		.catch((err) => {
		  console.error(&#;Error accessing microphone: &#;, err);
		});
	}, []);

虽然过零点机制能一定程度上区分语音与噪音,不过效果一般。于是又查了点资料发现这个东西有专门的技术叫VAD(Voice Activity Detection)。

3, VAD效果测试

找了一个基于JavaScript的VAD开源项目:

https://github.com/ricky0123/vad

添加图片注释,不超过 字(可选)

项目说明如下:此软件包旨在提供一个准确、用户友好的声音活动检测器(VAD),可在浏览器中运行。通过使用此软件包,您可以提示用户授予麦克风权限,开始录制音频,将包含语音的音频片段发送到您的服务器进行处理,或者在用户说话时显示特定的动画或指示器。

调用说明:

Voice Activity Detection for Javascript

Voice Activity Detection for Javascript

运行它提供的Demo,发现效果不错,能很准确的区分噪音和语音。而且录制的语音很完整。运行Demo的步骤如下:

npm i@ricky0123/vad-react
git clone https://github.com/ricky0123/vad
cd ./vad/examples/react-bundler
npm run build && npm run start

添加图片注释,不超过 字(可选)

原文链接:,转发请注明来源!