Search code examples
node.jsangularweb-audio-apikoaarraybuffer

Can't decode audio file when returned by server


On the server (node + koa.js), this is how I fetch the file and set the body of the response:

fs.readFile(file.wav, (err, data) => {
    ctx.body = data; // data is a Buffer
});

When requesting the file, the server returns the following response headers:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/octet-stream
Content-Length: 49396932
Date: Sat, 09 Sep 2017 14:55:35 GMT
Connection: keep-alive

So far so good. Things get problematic on the front end.

this.filesService.readContent().subscribe(response => {
    let arrayBuffer = response.arrayBuffer();

    console.log(response._body) //"RIFF���WAVEfmt D��data0������[...]d3 *ID3 TXXXSoftwareFL Studio 11"
    console.log(response._body.length) //47616738
    console.log(arrayBuffer.byteLength) //95233476

    audioContext.decodeAudioData(arrayBuffer).catch(e => {
       console.log(e) //DOMException: Unable to decode audio data
    })
});

Why can't I decode this audio file?

Also, I'm wondering:

  1. Why is the body of the response so easy to read? For example, "ID3 TXXXSoftwareFL Studio 11" looks like it's normal text, rather than bytes. Is it an encoding problem?
  2. Why is the body length 47616738 instead of 49396932 (Content-Length header)
  3. Why does the array buffer have the double size?

I know that my file is a perfectly valid file and that it is decodable from the browser. If I manually take the same file that the server returns and upload it on my application with a input file, the browser has no problem decoding it.

The problem is either the way that the server returns the file or the way that angular 2 interprets the response.

Any help is appreciated :). Cheers!


Solution

  • When calling response.arrayBuffer(), angular converts the body into an ArrayBuffer with a Uint16Array view, which I think explains why response.arrayBuffer().byteLength had double size.

    However, if you tell angular beforehand that you are expecting a response content type that is an ArrayBuffer, the associated view will be a Uint8Array:

    readContent() {
        return this.http.get(url, { responseType: ResponseContentType.ArrayBuffer })
    }
    

    It seems like decodeAudioData only works with Uint8Array.