Browser Audio JavaScript#
Hooking up two-way audio to a web-browser is fairly straightforward. This page provides startAudio and stopAudio
functions that open a 2-way websocket connection to a server and stream audio in both directions. All of the browser
based examples make use of these functions.
Importing#
You can directly include and use these functions from within your JavaScript using:
<script
src="https://cdn.jsdelivr.net/gh/DaveDeCaprio/voice-stream@main/examples/static/audio_ws.js">
</script>
Usage#
To play audio, these functions make use of the HTML audio element. You should have an element on your page, and pass the
id of that element to the startAudio function. startAudio also takes the WS endpoint to connect to.
Outgoing audio is handled through a MediaRecorder, which doesn’t require any additional elements on your HTML page.
Here is a simple example from the quickstart demonstrating how these functions can be used in an HTML page.
<button onclick="startAudio('audio-player', '/ws/audio')">Start Voice Chat</button>
<button onclick="stopAudio()">Stop Voice Chat</button>
<audio id="audio-player"></audio>
Source#
/** This file contains startAudio and stopAudio functions that feed two-way audio over a WebSocket connection to a browser.
* It is used by several examples. */
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host; // Includes hostname and port if present
let audioWebsocket;
let mediaRecorder;
async function startAudio(audioPlayerId, path) {
console.log("Starting Bidirectional Audio")
// Initialize MediaRecorder to record the audio stream
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.onerror = (error) => console.error('MediaRecorder error:', error);
mediaRecorder.ondataavailable = function(event) {
if (event.data.size > 0 && audioWebsocket && audioWebsocket.readyState === WebSocket.OPEN) {
audioWebsocket.send(event.data);
}
}
// Initialize MediaSource to play back received audio
const audioPlayer = document.getElementById(audioPlayerId);
const queue = [];
let sourceBuffer;
const mediaSource = new MediaSource();
mediaSource.onsourceopen = () => {
sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
sourceBuffer.addEventListener('updateend', () => {
if (queue.length > 0 && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(queue.shift());
}
});
audioPlayer.play().catch(function(error) {
if (error.name == "NotSupportedError") {
console.log("Stopping playback. No audio was received")
}
});
};
audioPlayer.src = URL.createObjectURL(mediaSource);
const wsUrl = `${protocol}//${host}${path}`;
audioWebsocket = new WebSocket(wsUrl);
audioWebsocket.onerror = (event) => {
console.error('Audio WebSocket Error:', event);
};
audioWebsocket.onopen = function(event) {
console.log("Websocket open, starting audio recording")
mediaRecorder.start(20); // Send data every 20ms. This is what Twilio uses.
};
audioWebsocket.onmessage = async (event) => {
const audioData = await event.data.arrayBuffer();
queue.push(audioData);
if (sourceBuffer && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(queue.shift());
}
};
audioWebsocket.onclose = () => {
if (mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
};
}
function stopAudio() {
console.log("Stopping Bidirectional Audio")
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
if (audioWebsocket) {
audioWebsocket.close();
}
}