如何使用Web Audio Api选择目标输出设备

18

我一直在使用Web Audio API,并创建了一个上下文,用数据填充了源缓冲区。它可以在默认输出设备上很好地播放,但我不知道如何选择目标设备。在旧的W3规范中,您可以将正确的deviceId传递给音频上下文构造函数,但是现在我无法在不使用媒体元素的情况下弄清楚如何实现它。有什么建议吗?

source = context.createBufferSource()
source.loop = true;
source.buffer = globalAudioBuffer;
source.connect(context.destination);
context.resume();
source.start(0);

现在是否有一种使用WebAudioAPI在最新的稳定版Chrome或最新的稳定版Firefox中选择HTML页面的音频输出设备的方法?也许可以使用Audio Output Devices API?一个简单可运行的示例对于许多目的来说都很有趣。 - Basj
假设我想让浏览器音频输出到ASIO4ALL(以获得非常低的延迟-用于我目前正在构建的基于浏览器的音乐采样器),如何在Windows上的Chrome或Firefox中实现这一点? - Basj
PS:对于Windows上的Chrome或Firefox的情况下 - Basj
2个回答

20

很遗憾,目前尚未实现设置webaudio图的目标音频设备,并且此API尚未最终确定。

目前您可以做的是将webaudio图连接到HTML元素,并设置元素的sinkid(目前仅适用于Chrome)

这里是一个简单的示例:

var ac = new AudioContext();
var audio = new Audio();
var o = ac.createOscillator();
o.start();
var dest = ac.createMediaStreamDestination();
o.connect(dest);
audio.src = URL.createObjectURL(dest.stream);
audio.play();

现在你的振荡器将通过音频元素播放,你可以使用已连接输出设备的deviceId调用audio.setSinkId()


1
祝你好运。如果你想知道这个功能的状态,可以关注开放问题 - Asher
2
奇怪的是,我们使用这种方法在主线程负载很重的情况下(例如,在短时间内启动了许多振荡器),会导致音频出现故障,并且在几秒钟后会有*失调的音频输出(!)*,从chrome 56一直持续到63版本(当前)。我的假设是主线程负载会干扰MediaStream对象或HTMLAudioElement。有相关经验吗? - John Weisz
1
@JohnWeisz,你是否遇到了以下问题?https://bugs.chromium.org/p/chromium/issues/detail?id=595635#c1 和 https://bugs.chromium.org/p/chromium/issues/detail?id=766198 - Asher
1
@JohnWeisz 这里有一个更更新的Chrome问题:https://bugs.chromium.org/p/chromium/issues/detail?id=766198 - Asher
1
@Asher 我认为你可以省略创建 ObjectUrl 的步骤。因为 audio.srcObject 接受一个流,你可以通过修改倒数第二行代码来实现:audio.srcObject = dest.stream; 其他的部分应该都没问题,但是如果你不撤销已创建的 ObjectURL,就不会有内存泄漏的问题。 - Pieter-Jan Van Robays
显示剩余5条评论

1
在Chrome 100中可以正常运行的完整示例,但在Firefox 98中无法正常运行。
我在这里发布这篇文章,因为https://dev59.com/7VgR5IYBdhLWcg3wBZS3#43069474上的另一个答案在这里不起作用(调用createObjectURL是无效的),而且因为其他答案没有显示setSinkId()的使用方法。
下面的代码片段将在系统上找到的第3个音频输出设备(audioDevices[2])上播放音频。
JSFiddle上尝试一下。
async function playAudio() {
  await navigator.mediaDevices.getUserMedia({
    audio: { deviceId: undefined },
    video: false
  });
  const devices = await navigator.mediaDevices.enumerateDevices();
  const audioDevices = devices.filter((device) => {
    return (
      device.kind === "audiooutput"
    );
  });
  const audioDevice = audioDevices[2];

  var audioContext = new AudioContext();
  var audioElement = new Audio();
  await audioElement.setSinkId(audioDevice.deviceId);
  var oscillator = audioContext.createOscillator();
  var mediaStreamDestination = audioContext.createMediaStreamDestination();
  oscillator.connect(mediaStreamDestination);
  audioElement.srcObject = mediaStreamDestination.stream;

  oscillator.start();
  audioElement.play();
  await new Promise((r) => setTimeout(r, 2000));
  oscillator.stop();
}

playAudio();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接