Rogue45
Rogue45

Reputation: 381

How to select destination output device using Web Audio API

I have been using web audio API and have created a context, and populated a source buffer with data. It plays fine over the default output device, but I don't understand how to choose the destination. In an old w3 spec you were able to pass in the correct deviceId to the audio context constructor, but I can't figure out how to do it now without using a media element. Any suggestions?

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

Upvotes: 19

Views: 12290

Answers (3)

Jerboas86
Jerboas86

Reputation: 654

Chrome provides an experimental API since v 110.0 to select output device. AudioContext.setSinkId(id: string) (MDN docs)

To obtain the deviceId, you can enumerate devices with navigator.mediaDevices.enumerateDevices().

// build your graph
const context = new AudioContext()
source = context.createBufferSource()
source.loop = true;
source.buffer = globalAudioBuffer;
source.connect(context.destination);
context.resume();
source.start(0);

const devices = await navigator.mediaDevices.enumerateDevices();
const outputDevices = devices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default');

// Select a new device
const selectedDevice = outputDevices[1]

await context.setSinkId(selectedDevice.deviceId)

Alternativaley, you can provide your deviceId as an option when you instantiate your audioContext (MDN docs).

// build your graph
const context = new AudioContext({sinkId: "bb04fea9a8318c96de0bd..."})
[...]

Limitations:

  • Currently implemented only on Chrome >v110, but Firefox (position) and Safari (position) are working on it (or seems to be ok to work on it)
  • Chrome Web Audio API implementation returns incorrect maxChannelCount property. This means that if you want to change your output device for a multichannel output device, there is a good chance that maxChannelCount be still 2.

Upvotes: 0

Thomas McGuire
Thomas McGuire

Reputation: 5466

Complete example that works in Chrome 100, but not in Firefox 98.

Posting this here since the other answer at https://stackoverflow.com/a/43069474/1005419 doesn't work here (the call to createObjectURL is invalid) and because the other answer doesn't show the setSinkId() usage.

The snippet below plays the audio on the 3rd audio output device found on the system (audioDevices[2]).

Try it out at 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();

Upvotes: 1

Asher
Asher

Reputation: 1267

Unfortunately, setting the destination audio device of a webaudio graph isn't yet implemented, and the api for this is not yet finalized.

What you can do for now, is connect the webaudio graph to an HTML element, and set the sinkid of the element (currently works on Chrome only)

Here is a simple example:

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();

Now your oscillator will play through the audio element and you can now call audio.setSinkId() with the deviceId of a connected output device.

Upvotes: 20

Related Questions