2

I am using AudioContext's decodeAudioData method to play back audio in Chrome, Firefox, and Opera. All browsers successfully decode and play audio that was recorded using Firefox. But when the audio was recorded using Chrome or Opera, only Firefox successfully decodes and plays it back. I get the following error when decoding the audio in Chrome or Opera:

Uncaught (in promise) DOMException: Unable to decode audio data.    

I attempted to implement Ladislav Nevery's suggested fix mentioned here: decodeAudioData returning a null error

The implemented code executes his suggestion successfully (traversing through the buffer to find start points in the audio stream), but the decoding still fails in Chrome for the Firefox-recorded audio.

Any ideas why decoding is failing?

    function syncStream(node){ // should be done by api itself. and hopefully will.
      var buf8 = new Uint8Array(node.buf);
      buf8.indexOf = Array.prototype.indexOf;
      var i=node.sync, b=buf8;
      while(1) {
        node.retry++;
        i=b.indexOf(0xFF,i); if(i==-1 || (b[i+1] & 0xE0 == 0xE0 )) break;
        i++;
      }
      if(i!=-1) {
        var tmp=node.buf.slice(i); //carefull there it returns copy
        delete(node.buf); node.buf=null;
        node.buf=tmp;
        node.sync=i;
        return true;
      }
      return false;
    }

    export function loadAudio(deckId) {
      store.audioPlayerDomain.audioLoading = true;
      let activeRecord = getActiveRecordByDeckId(deckId);
      let path = `${deckId}/${activeRecord.id}`;
      let context = getAudioContext();
      let processFn = function(node){
        return context.decodeAudioData(node.buf, function (decoded) {
            return decoded;
          },
          function(){ // only on error attempt to sync on frame boundary
            if(syncStream(node)){
              return processFn(node);
            };
          });
      };
      return AudioPlayerWebAPI.default.getAudio(path, context)
        .then((buffer) => {
          let node = {};
          node.buf=buffer;
          node.sync=0;
          node.retry=0;
          return processFn(node);
        }).then(function(decodedData) {
          store.audioPlayerDomain.audio = decodedData;
          store.audioPlayerDomain.audioLoading = false;
          return true;
        });
    }

    ....

    getAudio(path, context) {
    return fetch(`/api/public/audio/${path}`)
      .then(processResponse)
      .then(function(response) {
        if(response.message && response.message === 'non-existent'){
          return null;
        }else{
          return response.arrayBuffer();
        }
      })
    },
Community
  • 1
  • 1
Jacob Myers
  • 33
  • 1
  • 6
  • Why do you not use `.decodeAudioData().then()` instead of trying to return a value from callback function version of `.decodeAudioData(callbackFunction)`? `callbackFunction` is asynchronous, yes? – guest271314 Mar 11 '17 at 18:49
  • Wouldn't it be worth tackling this from the opposite direction and figure out why Chrome records in a different format and how to change that? You might want to luse `ffprobe` to check your file format. You can also use `ffmpeg` or a cloud based solution (there are several) to trans-code your file to make sure that it is always playable. – Marc Rohloff Mar 11 '17 at 23:13

1 Answers1

3

Until very recently, decodeAudioData could not decode the result of Chrome's MediaRecorder. Try a Chrome canary or beta to see if this is fixed. If you still have a problem, provide a more complete (but simple) example of what's not working.

Raymond Toy
  • 5,490
  • 10
  • 13