Created
May 29, 2014 19:08
-
-
Save Twipped/4b667562b5d1437da629 to your computer and use it in GitHub Desktop.
Code to generate SVG waveforms from mp3 files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var lame = require('lame'); | |
var through = require('through2'); | |
var fs = require('fs'); | |
var d3 = require('d3'); | |
var file = fs.createReadStream('source.mp3'); | |
var frames = [], frameBuffer = []; | |
var format; | |
var decoder = new lame.Decoder(); | |
decoder.on('format', function (form) { | |
format = form; | |
switch (format.bitDepth) { | |
case 8: | |
format.readName = 'readInt8'; | |
format.increment = 1; | |
break; | |
case 16: | |
format.readName = 'readInt16LE'; | |
format.increment = 2; | |
break; | |
case 32: | |
format.readName = 'readInt32LE'; | |
format.increment = 4; | |
break; | |
default: | |
console.error('Unsupported bitdepth: ', format.bitDepth); | |
process.exit(); | |
} | |
decoder.pipe(through(frameReader, process)); | |
}); | |
var max = 0; | |
function frameReader (chunk, enc, next) { | |
var i = 0, c = chunk.length; | |
var channel; | |
var frame; | |
while (i < c) { | |
frame = []; | |
for (channel=0;channel < format.channels;channel++) { | |
frame[channel] = chunk[format.readName](i); | |
i += format.increment; | |
max = Math.max(max, Math.abs(frame[channel])); | |
} | |
frames.push(frame); | |
} | |
next(); | |
} | |
file.pipe(decoder); | |
function reduce (frames) { | |
var result = [], set, channel, sum, avg, | |
offset = 0, | |
len = frames.length, | |
sampleSize = format.sampleRate / 100; | |
while (offset < len) { | |
set = frames.slice(offset, offset + sampleSize); | |
offset += sampleSize; | |
if (set.length) { | |
sum = []; | |
avg = []; | |
for (channel=0;channel < format.channels;channel++) { | |
sum[channel] = set.reduce(function(a, b) { return a + b[channel]; }, 0); | |
avg[channel] = Math.round(sum[channel] / set.length); | |
} | |
result.push(avg); | |
} | |
} | |
return result; | |
} | |
function process () { | |
var reduction = reduce(frames); | |
var height = 600; | |
var y = d3.scale.linear() | |
.domain([-max, max]) | |
.range([-height, height]); | |
var svg = d3.select('body').append('svg') | |
.attr('width', reduction.length) | |
.attr('height', height) | |
.append('g') | |
.attr("transform", "translate(0," + (height / 2) + ")"); | |
var line = d3.svg.line() | |
.x(function(d, i) { return i; }) | |
.y(function(d) { return y(d[0]); }); | |
svg.append('path') | |
.datum(reduction) | |
.style("fill:none;stroke:black;stroke-width: 0.5px;") | |
.attr('d', line); | |
var html = d3.select('body').node().innerHTML; | |
fs.writeFileSync('waveform.html', html); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment