Search code examples
javascriptfirefoxaudiofirefox-addonfirefox-addon-sdk

Loading an audio file with the Add-On SDK and playing it with Aurora.js


I'm currently trying to write a music player add-on for Mozilla Firefox using the Add-On SDK. I'm using Aurora.js (Wiki) to handle loading/demuxing/decoding the audio files

I've currently got the add-on working so you can select an audio file with an HTML file input, which is pretty straight forward.

    <input type="file" id="file-selector" />
    <script>
        var fileSelector = document.getElementById("file-selector");

        fileSelector.onchange = function() {
            var audioFile = fileSelector.files[0];

            var audioPlayer = AV.Player.fromFile(audioFile);

            audioPlayer.play();
        };
    </script>

What I'm currently trying to do though, is read a file returned from the add-on SDK rather than the file from the input on the HTML page, and create an AV.Player. Reading a file is simple enough using the sdk/io/file and sdk/io/byte-streams modules.

The problem is I can't figure out how to properly construct a AV.Player from this data. From what it looks like:

  1. AV.Player.fromFile(x) works by constructing a AV.Asset from AV.Asset.fromFile(x)
  2. AV.Asset.fromFile(x) constructs an AV.Asset with AV.FileSource(x)
  3. AV.FileSource is constructed by creating a FileReader and reading the data from the file as a Blob

I've been trying to figure out how to create a Blob with the byte array returned by io/file.read(filePath, "br") with no luck. I tried creating an object that resembles a Blob with size, type, and a slice method, but that also didn't seem to work.


Solution

  • Since you will be (or at least should be) using Aurora in some content script (widget, panel, ...), things will be a little more complex.

    1. Read data in your main.js, e.g. via the file module you already mentioned.
    2. Pass that data over to the content script via messaging.
    3. In the content script, receive the message, wrap the bytes in a Uint8Array and do your Aurora thing:

      addon.port.on("bytes-loaded", function(stream) {
        var data = new Uint8Array(stream.length);
        for (var i = 0; i < stream.length; ++i) {
            data[i] = String.charCodeAt(stream[i]) & 0xff;
        }
        avPlayer = AV.Player.fromBuffer(data);
      });
      

    The file module isn't really good, as it is a synchronous API that will block while reading the file, but there is at least one alternative.

    A complete example with NetUtil async file reading, and without the need to convert the string to an array in Javascript land can be found in this builder, but make sure to replace the hardcoded path. ;)