UNPKG

sox

Version:

node.js interface to the sox audio utility

179 lines (160 loc) 4.98 kB
var childProcess = require('child_process') , EventEmitter = require('events').EventEmitter , Batch = require('batch') , util = require('util') exports.identify = identify; exports.transcode = transcode; var SENTINEL = /[\n\r]/ // to edit this see https://gist.github.com/4142076 var PROGRESS_TIME_REGEX = /^In:([\d.]+)%\s+(\d\d):(\d\d):([\d.]+)\s+\[(\d\d):(\d\d):([\d.]+)\]\s+Out:([\d.\w]+)\s+\[[\s|\-!=]+\]\s+(?:Hd:([\d.]+))?\s+Clip:(\d+)\s*$/; var conversions = { sampleRate: int, sampleCount: int, channelCount: int, duration: float, bitRate: parseBitRate, } var suffixMultiplier = { 'k': 1024, 'm': 1024 * 1024, 'g': 1024 * 1024 * 1024, }; function parseBitRate(str) { var mult = suffixMultiplier[str[str.length - 1]]; var n = parseInt(str, 10); return mult ? mult * n : n; } function capture(exe, args, callback){ childProcess.execFile(exe, args, function(err, stdout, stderr){ if (err) { err.stdout = stdout; err.stderr = stderr; err.args = args; callback(err); } else { callback(null, stdout.trim()); } }); } function int(it){ return parseInt(it, 10); } function float(it){ return parseFloat(it, 10); } function identify(inputFile, callback){ var results = {} , batch = new Batch() soxInfo('-t', function(value) { results.format = value; }); soxInfo('-r', function(value) { results.sampleRate = value; }); soxInfo('-c', function(value) { results.channelCount = value; }); soxInfo('-s', function(value) { results.sampleCount = value; }); soxInfo('-D', function(value) { results.duration = value; }); soxInfo('-B', function(value) { results.bitRate = value; }); batch.end(function(err) { if (err) return callback(err); for (var k in conversions) { results[k] = conversions[k](results[k]) } callback(null, results); }); function soxInfo(arg, assign) { batch.push(function(cb) { capture('sox', ['--info', arg, inputFile], function(err, value) { if (err) return cb(err); assign(value); cb(); }); }); } } function transcode(inputFile, outputFile, options) { return new Transcode(inputFile, outputFile, options); } function Transcode(inputFile, outputFile, options) { EventEmitter.call(this); this.inputFile = inputFile; this.outputFile = outputFile; this.options = options; // defaults this.options = this.options || {}; this.options.sampleRate = this.options.sampleRate || 44100; this.options.format = this.options.format || 'mp3'; this.options.channelCount = this.options.channelCount || 2; this.options.bitRate = this.options.bitRate ? parseInt(this.options.bitRate, 10) : 192 * 1024; if (this.options.format === 'mp3') { this.options.compressionQuality = this.options.compressionQuality || 5; } } util.inherits(Transcode, EventEmitter); Transcode.prototype.start = function() { var self = this; identify(this.inputFile, function(err, src) { if (err) { self.emit('error', err); return } self.emit('src', src); var args = [ '--guard', '--magic', '--show-progress', self.inputFile, '-r', self.options.sampleRate, '-t', self.options.format, '-C', Math.round(self.options.bitRate / 1024) + self.options.compressionQuality, '-c', self.options.channelCount, self.outputFile ]; var bin = childProcess.spawn('sox', args); var stdout = ""; bin.stdout.setEncoding('utf8'); bin.stdout.on('data', function(data) { stdout += data; }); var stderr = ""; var buffer = ""; bin.stderr.setEncoding('utf8'); bin.stderr.on('data', function(data) { stderr += data; buffer += data; var lines = buffer.split(SENTINEL); buffer = lines.pop(); lines.forEach(function(line) { var m = line.match(PROGRESS_TIME_REGEX); if (!m) return; var hour = parseInt(m[2], 10) var min = parseInt(m[3], 10) var sec = parseInt(m[4], 10) var encodedTime = sec + min * 60 + hour * 60 * 60; // might have to correct duration now that we've scanned the file if (encodedTime > src.duration) { src.duration = encodedTime; self.emit('src', src); } self.emit('progress', encodedTime, src.duration); }); }); bin.on('close', function(code) { if (code) { var err = new Error("sox returned nonzero exit code: " + code); err.code = code; err.stdout = stdout; err.stderr = stderr; err.args = args; self.emit('error', err); } else { identify(self.outputFile, function(err, dest) { if (err) { self.emit('error', err); } else { self.emit('dest', dest); self.emit('progress', src.duration, src.duration); self.emit('end'); } }); } }); }); };