我已经建立了一个语音助理的演示,该演示会获取麦克风数据,将其传递到分析器,然后使用.getByteFrequencyData()
显示视觉效果。它的工作方式如下:
此处为实时版本:https://dyadstudios.com/playground/daysi/
我实现这一目标的方法如下:
var audioContext = (window.AudioContext) ? new AudioContext() : new window["webkitAudioContext"]();
var analyser = audioContext.createAnalyser();
analyser.fftSize = Math.pow(2, 9); // 512
var sourceMic = undefined; // Microphone stream source
var sourceMp3 = undefined; // MP3 buffer source
// Browser requests mic access
window.navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
sourceMic = audioContext.createMediaStreamSource(stream)
})
// 1. Mic button pressed, start listening
listen() {
audioContext.resume();
// Connect mic to analyser
if (sourceMic) {
sourceMic.connect(analyser);
}
}
// 2. Disconnect mic, play mp3
answer(mp3AudioBuffer) {
if (sourceMic) {
// Disconnect mic to prevent audio feedback
sourceMic.disconnect();
}
// Play mp3
sourceMp3 = audioContext.createBufferSource();
sourceMp3.onended = mp3StreamEnded;
sourceMp3.buffer = mp3AudioBuffer;
sourceMp3.connect(analyser);
sourceMp3.start(0);
// Connect to speakers to hear MP3
analyser.connect(audioContext.destination);
}
// 3. MP3 has ended
mp3StreamEnded() {
sourceMp3.disconnect();
// Disconnect speakers (prevents mic feedback)
analyser.disconnect();
}
它在Firefox和Chrome上运行良好,但是OSX Safari 12.1仅在我第一次按下按钮时获得麦克风数据。每当我第二次按下麦克风按钮时,分析仪就不再获取麦克风数据,但MP3数据仍然有效。似乎将麦克风的AudioNode连接,断开连接并将其重新连接到分析仪似乎会以某种方式破坏它。我检查了一下,Safari浏览器同时支持AudioNode.connect()
和AudioNode.connect()
。我知道Safari的WebAudio实现有些过时,是否有解决此问题的解决方法?
Safari中确实存在一个错误,如果MediaStreamAudioSourceNode断开连接一段时间,它会导致信号丢失。您可以通过不断开连接来避免这种情况,只要您可能再次需要它即可。您可以改用GainNode来使信号静音。
您可以通过引入新的变量来控制音量来做到这一点。
AudioNode.disconnect()
然后,您需要在实例化AudioNode.disconnect()
时立即连接所有内容。
const sourceMicVolume = audioContext.createGain();
sourceMicVolume.gain.value = 0;
然后在事件处理程序内部,您将只设置增益的大小,而不是(断开)连接节点。在sourceMic
函数内部,如下所示:
sourceMic = audioContext.createMediaStreamSource(stream);
sourceMic.connect(sourceMicVolume);
sourceMicVolume.connect(analyser);
在listen()
函数内部,看起来像这样:
if (sourceMic) {
sourceMicVolume.gain.value = 1;
}