Search code examples
javascriptfilereaderfileinputstream

FileReader.readAsArray() from source other than <input type="file">


I have the following HTML:

    <script type="text/javascript">
      window.onload = function() {

        var volumeBars = {
          mono: document.getElementById("monoFill")
        };

        document.getElementById("open-file").onchange = function(evt) {
          var file = evt.target.files[0];
          var reader = new FileReader();
          reader.onload = function(e) {
            playSound(e.target.result);
            console.log(e.target.result);
          }
          reader.readAsArrayBuffer(file);
        }

        var context = new AudioContext();

        function playSound(arraybuffer) {
          context.close();
          context = new AudioContext();

          var source = context.createBufferSource();
          context.decodeAudioData(arraybuffer, function(buffer) {
            source.buffer = buffer;
          });


          //=
          console.log(arraybuffer)
          var analyser = context.createAnalyser();
          analyser.smoothingTimeConstant = .9;
          analyser.fftSize = 1024;

          jsNode = context.createScriptProcessor(2048, 1, 1);
          jsNode.onaudioprocess = function() {
            var array = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(array);
            volumeBars.mono.style.height = Math.average(array) * 2 + "px";
            volumeBars.mono.innerHTML = Math.floor(Math.average(array));
            console.log(volumeBars.mono.innerHTML);

          }

          source.connect(analyser);
          source.connect(context.destination);
          jsNode.connect(context.destination);
          analyser.connect(jsNode);

          source.start();
        }

        Math.average = function(arguments) {
          var numbers;
          if (arguments[0] instanceof Array) {
            numbers = arguments[0];
          } else if (typeof arguments[0] == "number") {
            numbers = arguments;
          }
          var sum = 0;
          var average = 0;
          for (var i = 0; i < numbers.length; i++) {
            sum += numbers[i];
          }
          average = sum / numbers.length;
          return average;
        }

      }

    </script>


    <div id="container">
      <div class="bar" id="mono">
        <div class="fill" id="monoFill"></div>
      </div>
    </div>

    <input type="file" id="open-file" accept="audio/*">

The issue that I am facing, is I want to replace the block

  var file = evt.target.files[0];

with a local file that doesn't need to be loaded every time. I have been unable to find a method that encodes the file as an ArrayBuffer. I'm sure this is a very amateur question with a obvious answer, but any assistance is welcome.


Solution

  • Since what you want is to access a file on your server, you simply need to make an Ajax request, with the response set to an ArrayBuffer, no need for a FileReader here.

    Using the fetch API it would look like

    const a_ctx = new(window.AudioContext || window.webkitAudioContext)();
    fetch('https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3')
      .then(resp => resp.arrayBuffer()) // request as ArrayBuffer
      .then(buf => a_ctx.decodeAudioData(buf))
      .then(a_buf => {
        btn.onclick = e => {
          let node = a_ctx.createBufferSource();
          node.buffer = a_buf;
          node.connect(a_ctx.destination);
          node.start(0);
        };
        btn.disabled = false;
      });
    <button id="btn" disabled>play</button>
    
    
    <!-- Promising decodeAudioData for Safari https://github.com/mohayonao/promise-decode-audio-data/ [MIT] -->
    <script src="https://cdn.rawgit.com/mohayonao/promise-decode-audio-data/eb4b1322/build/promise-decode-audio-data.min.js"></script>

    And using XMLHttpRequest API:

    var a_ctx = new(window.AudioContext || window.webkitAudioContext)();
    var xhr = new XMLHttpRequest();
    xhr.open('get', 'https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3');
    xhr.responseType = 'arraybuffer'; // request as ArrayBuffer
    xhr.onload = function() {
      var buf = xhr.response;
      a_ctx.decodeAudioData(buf, function(a_buf) {
        btn.onclick = function(e) {
          let node = a_ctx.createBufferSource();
          node.buffer = a_buf;
          node.connect(a_ctx.destination);
          node.start(0);
        };
        btn.disabled = false;
      });
    };
    xhr.send();
    <button id="btn" disabled>play</button>