AudioNode.disconnect() 后跟 .connect() 在 Safari 中不起作用

AudioNode.disconnect() followed by .connect() not working in Safari

我构建了一个语音助手演示,它获取麦克风数据,将其传递给分析器,然后使用 .getByteFrequencyData() 显示视觉效果。它的工作原理如下:

  1. 按麦克风按钮连接到麦克风输入
  2. 释放麦克风按钮断开麦克风流,并播放响应的 MP3。
  3. 当MP3结束时:return进入待机状态,等待按下新的按钮再次开始步骤1。

在线版本: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() as well as AudioNode.disconnect()。我知道 Safari 的 WebAudio 实现有点过时,是否有解决此问题的解决方法?

Safari 中确实存在一个错误,如果 MediaStreamAudioSourceNode 断开连接一段时间,它会导致信号丢失。只要您可能再次需要它,就可以通过不断开连接来避免这种情况。您可以改用 GainNode 来静音信号。

您可以通过引入一个新变量来控制音量来做到这一点。

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;
}

answer() 函数中它看起来像这样:

if (sourceMic) {
    sourceMicVolume.gain.value = 0;
}