From microphone to .WAV to server

I blogged recently about capturing the audio from the Microphone with Web Audio and saving it as a .wav file. Some people asked me about saving the file on the server, I have to admit I did not have the time to look into this at that time, but it turns out, I had to write a little app this morning that does just that.

So I reused the code from the Web Audio article and just added the 2 additional pieces I needed.

  • The XHR code for the upload
  • 3 lines of PHP needed on the server.

So here we go. In the encoder, I had the following code to package our WAV file:

// we flat the left and right channels down
var leftBuffer = mergeBuffers ( leftchannel, recordingLength );
var rightBuffer = mergeBuffers ( rightchannel, recordingLength );
// we interleave both channels together
var interleaved = interleave ( leftBuffer, rightBuffer );

// we create our wav file
var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);

// RIFF chunk descriptor
writeUTFBytes(view, 0, 'RIFF');
view.setUint32(4, 44 + interleaved.length * 2, true);
writeUTFBytes(view, 8, 'WAVE');
// FMT sub-chunk
writeUTFBytes(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true);
// stereo (2 channels)
view.setUint16(22, 2, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * 4, true);
view.setUint16(32, 4, true);
view.setUint16(34, 16, true);
// data sub-chunk
writeUTFBytes(view, 36, 'data');
view.setUint32(40, interleaved.length * 2, true);

// write the PCM samples
var lng = interleaved.length;
var index = 44;
var volume = 1;
for (var i = 0; i < lng; i++){
    view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);
    index += 2;
}

// our final binary blob
var blob = new Blob ( [ view ], { type : 'audio/wav' } );

The key piece is the last line. This is our blob that we will send to the server. To do this, we use our good friend XHR:

function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', './upload.php', true);
  xhr.onload = function(e) {};
  // Listen to the upload progress.
  var progressBar = document.querySelector('progress');
  xhr.upload.onprogress = function(e) {
    if (e.lengthComputable) {
      progressBar.value = (e.loaded / e.total) * 100;
      progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
    }
  };

  xhr.send(blobOrFile);
}

It takes our blob as a parameter and sends it to our php file. Thank you Eric Bidelman for the great article about tricks with XHR on HTML5Rocks.com, the function is literally a copy/paste from there.

And then all you need are these 3 lines of PHP code. Simple, easy.

$fp = fopen( 'savedfile.wav', 'wb' );
fwrite( $fp, $GLOBALS[ 'HTTP_RAW_POST_DATA' ] );
fclose( $fp );

And that's it. VoilĂ !

Comments (2)

  1. Jon B wrote:

    What I looked at before was using a couple of libraries – one of which was an implementation of the Speex codec I believe. Compressing audio to send to the server would be awesome, there are powerful audio processing tools in JS, but no idea on how ‘fast’ they are.

    Thursday, January 22, 2015 at 1:39 am #
  2. Thibault Imbert wrote:

    Hi Jon,

    Indeed. I am actually looking at this right now. Speed is key here, and I also expect them to be pretty slow. I will keep exploring things and probably post about it soon.

    Thibault

    Tuesday, January 27, 2015 at 7:58 pm #