in the code below i'm trying to create waveform image from audio file my function receives as an input, but when I upload audio files im getting the following error: Failed to execute 'appendBuffer' on 'SourceBuffer': Overload resolution failed. line 85 is the on that triggers the error but what I can't understand is why the fileReader.onload function is never being executed. any idea what's the cause?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Circular Waveform Example</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<input type="file" id="audio-file" accept="audio/*" />
<br /><br />
<canvas id="waveform"></canvas>
<script>
// function to generate circular waveform image
function generateCircularWaveform(audioFile, canvasId) {
const canvas = document.getElementById(canvasId);
const canvasContext = canvas.getContext("2d");
const audioContext = new AudioContext();
const mediaSource = new MediaSource();
const audioElement = document.createElement("audio");
audioElement.controls = false;
audioElement.style.display = "none";
document.body.appendChild(audioElement);
let audioData = [];
let chunkSize = 1024 * 1024; // 1MB
mediaSource.addEventListener("sourceopen", function(e) {
console.log("Media sourceopen starting");
let sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg");
let fileReader = new FileReader();
let offset = 0;
fileReader.onload = function() {
audioContext.decodeAudioData(this.result, function(buffer) {
let channelData = buffer.getChannelData(0);
for (let i = 0; i < channelData.length; i += 100) {
let magnitude = Math.abs(channelData[i]) * canvas.height;
let angle = (i / channelData.length) * 2 * Math.PI;
let x = magnitude * Math.cos(angle);
let y = magnitude * Math.sin(angle);
audioData.push({ x: x, y: y });
}
if (offset < audioFile.size) {
offset += chunkSize;
let chunk = audioFile.slice(offset, offset + chunkSize);
fileReader.readAsArrayBuffer(chunk);
} else {
let waveformPath = new Path2D();
waveformPath.moveTo(audioData[0].x, audioData[0].y);
for (let i = 1; i < audioData.length; i++) {
waveformPath.lineTo(audioData[i].x, audioData[i].y);
}
canvasContext.lineWidth = 1;
canvasContext.strokeStyle = "black";
canvasContext.stroke(waveformPath);
document.body.removeChild(audioElement);
}
});
};
mediaSource.addEventListener("sourceended", function() {
console.log("Media source ended");
});
mediaSource.addEventListener("sourceclose", function() {
console.log("Media source closed");
});
mediaSource.addEventListener("error", function(e) {
console.error("Media source error:", e);
});
sourceBuffer.mode = "sequence";
sourceBuffer.timestampOffset = 0;
sourceBuffer.appendBuffer(audioFile.slice(0, chunkSize));
audioElement.play();
});
canvasContext.fillStyle = "yellow";
canvasContext.fillRect(0, 0, canvas.width, canvas.height);
}
// handle file
// input change event listener
const inputElement = document.getElementById("audio-file");
inputElement.addEventListener("change", function(event) {
const file = event.target.files[0];
generateCircularWaveform(file, "waveform");
});
</script>
</body>
</html>
You need to convert the Blob to a data type that's supported by the appendBuffer() method first, before passing it in. So instead of doing this
sourceBuffer.appendBuffer(audioFile.slice(0, chunkSize));
audioElement.play();
You need to do this
audioFile.slice(0, chunkSize).arrayBuffer().then((data) => {
sourceBuffer.appendBuffer(data);
audioElement.play();
})