javascriptmedia-source

getting the error: Overload resolution failed when working with mediaSource and fileReader


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>

Solution

  • 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();
    })
    
    1. AppendBuffer
    2. ArrayBuffer