AudioDeviceRepresents an audio output device wrapper that owns an AudioContext and helps create and manage Channel, Master and InputChannel routing objects. Usually constructed when calling resolveDefaultAudioOutputDevice().
The output device can be switched at runtime with setOutputDevice(), without recreating the AudioContext. Master channels, channels, effects and audio clips keep working, so there is no need to reload the page.
import { listAudioOutputDevices } from "@fluex/fluexgl-dsp";
const device = await pipeline.resolveDefaultAudioOutputDevice();
const channel = device.createChannel();
channel.send(device.getMasterChannel());
// Switch to another output device while audio keeps playing.
const outputs = await listAudioOutputDevices();
await device.setOutputDevice(outputs[1]);
// Open the default microphone on its own channel.
const microphone = await device.createInputChannel(null, "Microphone");
Constructs a new AudioDevice and creates an internal AudioContext along with its default master channel. When deviceInfo is an output device other than the system's default, the AudioContext is created on that device (sinkId). If that fails, or the browser does not support it, the default output device is used and a warning is logged.
The constructor also starts listening for devicechange events of navigator.mediaDevices, which powers the devices-changed and output-device-lost events. Call close() to stop listening.
new AudioDevice(deviceInfo?: MediaDeviceInfo | null): AudioDevice;
deviceInfo?: MediaDeviceInfo | null - The output device to play on (from listAudioOutputDevices() or enumerateDevices()). Defaults to null, which uses the system's default output device.deviceInfo: MediaDeviceInfo | nullThe output device this instance is currently playing on. Set via the constructor and updated by setOutputDevice(). null when unknown.
id: stringA unique id for this AudioDevice instance. Automatically generated when constructing the device. Should NOT be changed.
timestamp: numberCreation timestamp (Date.now()) for this AudioDevice instance.
context: AudioContextThe AudioContext owned by this device. Used for creating channels and master busses. Stays the same when the output device is switched.
masterChannel: MasterThe currently selected active Master channel for this device.
masterChannels: Master[]All created Master channels owned by this device via createMasterChannel(). Note that the default masterChannel created in the constructor is not automatically pushed into this array.
inputChannels: InputChannel[]All InputChannel instances created via createInputChannel(). Closed automatically by close().
sampleRate: number (readonly)The sample rate of context.
maximumFrequency: number (readonly)Half of context.sampleRate (the Nyquist frequency).
setOutputDevice(device?: MediaDeviceInfo | string | null): Promise<boolean>Switches the output device this audio device plays on, using AudioContext.setSinkId(). The AudioContext and everything built on it stay intact. Passing nothing, null or "default" switches to the system's default output device. Fires output-device-changed on success.
Requires a browser that supports AudioContext.setSinkId() (Chromium based browsers). Check AudioDevice.supportsOutputDeviceSelection first.
device?: MediaDeviceInfo | string | null - The output device, or its deviceId. Must be an "audiooutput" device.boolean - true when the output device has been switched, otherwise false.WARNING:FLUEXGL-DSP@0002 (WarningCodes.OUTPUT_DEVICE_SWITCH_UNSUPPORTED) - The browser does not support AudioContext.setSinkId(). Returns false.ERROR:FLUEXGL-DSP@0024 (ErrorCodes.OUTPUT_DEVICE_UNAVAILABLE) - The given device is not an output device, or the browser refused to switch to it (for example because it was unplugged).createInputChannel(device?: MediaDeviceInfo | string | null, label?: string, options?: Partial<InputChannelOptions>): Promise<InputChannel>Creates a new InputChannel on this device's AudioContext, opens the given input device on it, and stores it in inputChannels. Passing nothing, null or "default" opens the system's default input device.
The input channel is not connected to anything by default. Use send() to route it to a channel or master channel.
device?: MediaDeviceInfo | string | null - The input device, or its deviceId.label?: string - Optional label. Defaults to "Input".options?: Partial<InputChannelOptions> - Capture options, such as echo cancellation.InputChannel - Also returned when the device could not be opened; check inputChannel.isOpen.removeInputChannel(inputChannel: InputChannel): voidCloses the input channel, removes all its sends (to channels and master channels) and removes it from inputChannels.
inputChannel: InputChannelvoidERROR:FLUEXGL-DSP@0012 (ErrorCodes.CHANNEL_NOT_FOUND) - The input channel is not part of this audio device.close(): Promise<void>Closes all input channels, stops listening for device changes and closes the AudioContext. The audio device cannot be used anymore afterwards.
No arguments
voidaddEventListener(event, callback): () => voidRegisters a callback for one of the events below.
event: keyof AudioDeviceEventMap - "output-device-changed", "output-device-lost" or "devices-changed".callback: AudioDeviceEventMap[event] - See AudioDeviceEventMap.() => void - A function that removes the listener again.removeEventListener(event, callback): AudioDeviceRemoves a previously registered callback.
AudioDevice - The same device.getMasterChannel(): MasterReturns the currently active Master channel.
No arguments
setMasterChannel(channel: Master): voidSets the active Master channel. This is typically one of the entries in masterChannels. Logs an error and does nothing if the given channel is already the active one.
channel: Master - The master channel to set as active.voidcreateMasterChannel(): MasterCreates a new Master channel using this device’s AudioContext, stores it in masterChannels, and returns it.
No arguments
getContext(): AudioContextReturns this device’s AudioContext.
No arguments
AudioContextcreateChannel(label?: string): ChannelCreates and returns a new Channel using this device’s AudioContext.
label?: string - Optional label for the new channel.Registered with addEventListener(). See AudioDeviceEventMap.
| Event | Payload | Fired when |
|---|---|---|
| "output-device-changed" | AudioDeviceChangedEvent | setOutputDevice() switched the output device. |
| "output-device-lost" | AudioDeviceLostEvent | The selected output device was disconnected. The device then falls back to the default output device (which fires output-device-changed as well) and logs WARNING:FLUEXGL-DSP@0003 (WarningCodes.OUTPUT_DEVICE_LOST). |
| "devices-changed" | AudioDeviceListChangedEvent | An audio input or output device was connected or disconnected. |
When the system's default output device is used, the browser follows changes of the default device by itself, so output-device-lost is only fired for a specifically selected device.
get outputDeviceId(): stringThe deviceId of the output device this audio device is currently playing on. An empty string means the system's default output device (also in browsers without setSinkId() support).
get baseLatency(): numberThe live baseLatency of context.
get outputLatency(): numberThe live outputLatency of context. Can change when the output device is switched.
get state(): AudioContextStateThe live state of context (e.g. "running", "suspended", "closed").
get currentTime(): numberThe live currentTime of context.
static get supportsOutputDeviceSelection(): booleanWhether the browser supports switching the output device of an AudioContext (AudioContext.prototype.setSinkId).
const device = new AudioDevice(deviceInfo);
const a = device.createChannel();
const b = device.createChannel();
// Route a into b, and b into the active master
a.send(b);
b.send(device.getMasterChannel());
const device = new AudioDevice(deviceInfo);
const masterA = device.getMasterChannel();
const masterB = device.createMasterChannel();
// Switch active master
device.setMasterChannel(masterB);
// You can still keep references to older master channels
console.log(masterA.id, masterB.id);
import { AudioDevice, listAudioOutputDevices } from "@fluex/fluexgl-dsp";
const select = document.querySelector("select")!;
async function render() {
const outputs = await listAudioOutputDevices();
select.replaceChildren(...outputs.map(output => new Option(output.label, output.deviceId)));
select.value = device.outputDeviceId || "default";
}
select.disabled = !AudioDevice.supportsOutputDeviceSelection;
select.addEventListener("change", () => device.setOutputDevice(select.value));
device.addEventListener("devices-changed", render);
device.addEventListener("output-device-changed", render);
await render();
device.addEventListener("output-device-lost", ({ lost }) => {
showToast(`${lost?.label ?? "Output device"} was disconnected, switched to the default device.`);
});