Audio waveform with Node.JS

When playing an audio file, it’s a lot more visually appealing user experience if the user can see a waveform of the audio. Surprisingly enough, generating waveforms is still a reasonably hard problem and there are very few resources out there that try to simplify the generation for the layman. Fortunately, generating images isn’t as hard as it would appear to be, though I agree there is a dearth of information & documentation in this space.

For doing this in Node.JS, my choice was using the waveform generator – https://github.com/rzurad/waveform and using node’s asynchronous process apis to spawn a child process and generate the image. A simple way would be to place all media in a single place / folder on the server, where we could also add files by uploading from the site, and generate all images for new / changed media. Here is sample code that can be used to generate waveforms for all media in mediaPath and place all output images in imagePath using the waveform generator :

var _ = require('lodash'),
    fs = require('fs'),
    path = require('path'),
    exec = require('child_process').exec;

function waveformCLI(mediaFile, imageFile) {
  return 'waveform -i ' + mediaFile + ' ' +
         '-h 100 -t 25 -w 300 -m -o ' + imageFile;
}

function generateWaveform(mediaFile, imagePath, callback) {
  var imageFile = path.join(imagePath, path.basename(mediaFile, '.mp3')) + '.png';
  exec(waveformCLI(mediaFile, imageFile), function (error, stdout, stderr) {
    if (error) {
      console.log('dispensing error message:');
      console.log(error, stdout, stderr);
      console.log('error message dispensed');
    }
    callback(imageFile);
  });
}

function generateWaveforms(mediaPath, imagePath, callback) {
  fs.readdir(mediaPath, function (err, files) {
    var results = [];
    var mp3files = _.filter(files, function (name) {
      return /.*\.mp3$/.test(name);
    });
    _.each(mp3files, function (mediaFile) {
      var name = path.basename(mediaFile, '.mp3').replace(/_/g, ' ');
      var mediaFilePath = path.join(mediaPath, mediaFile);
      generateWaveform(mediaFilePath, imagePath, function (imageFile) {
        results.push({
          name: name,
          mediaFile: mediaFilePath,
          imageFile: imageFile
        });
        if (results.length === mp3files.length) {
          callback(results);
        }
      });
    });
  });
}

Here is what the waveform looks like with default settings, we can style it better by changing the colors using a few more cli options for the waveform binary.

Tamara_Laurel_-_SweetYou can easily hook this api into a route to generate the images, and additionally add checks to ensure the images are generated only for new / changed media files. Here’s a link for the demo – http://104.131.111.91:3000/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s