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à!