Skip to content

Commit

Permalink
added optional safe clipping
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzo committed Feb 15, 2012
1 parent 638033e commit cf25732
Showing 1 changed file with 40 additions and 22 deletions.
62 changes: 40 additions & 22 deletions wavencoder.js
Expand Up @@ -47,6 +47,7 @@ var WavEncoder = (function(){
var bytesPerSample = options.bytesPerSample || defaults.bytesPerSample;
var numChannels = options.numChannels || defaults.numChannels;
var sampleRateHz = options.sampleRateHz || defaults.sampleRateHz;
this.clip = 'clip' in options ? !!options.clip : defaults.clip;

var PCM_FORMAT = 1;
var bitsPerSample = bytesPerSample * 8;
Expand All @@ -63,14 +64,15 @@ var WavEncoder = (function(){

switch (bytesPerSample) {
case 1:
this.encode = this.encode8;
this.encode = this._encode8;
break;

case 2:
this.encode = this.encode16;
this.encode = this._encode16;
break;

default: throw 'unsupported bytesPerSample: ' + bytesPerSample;
default: throw new WavEncoderException(
'unsupported bytesPerSample: ' + bytesPerSample);
}

// we encode using 16-bit words
Expand Down Expand Up @@ -98,14 +100,14 @@ var WavEncoder = (function(){

var bytesPerWord = 2;
var dataWords = dataBytes / bytesPerWord;
words.length = this.headerWords + dataWords;
words.length = this._headerWords + dataWords;
while (words.length % 3) words.push(0);
};

WavEncoder.prototype = {

headerBytes: 44,
headerWords: 22,
_headerBytes: 44,
_headerWords: 22, // for 16-bit words

/**
* @param {string}
Expand Down Expand Up @@ -144,24 +146,33 @@ var WavEncoder = (function(){
* @param {number[]}
* @returns {string}
*/
encode8: function (samples) {
_encode8: function (samples) {
// this is hard-coded for 8-bit mono

assertEqual(samples.length, this.numSamples, 'Wrong number of samples');

var floor = Math.floor;
var sqrt = Math.sqrt;
var clip = this.clip;

var words = this.words;
var h = this.headerWords;
var h = this._headerWords;
for (var t = 0, T = this.numSamples - 1; t < T; t += 2) {
var x1 = samples[t + 0];
var x2 = samples[t + 1];
if (clip) {
x1 /= sqrt(1 + x1 * x1);
x2 /= sqrt(1 + x2 * x2);
}
// 8-bit samples are unsigned
var sample1 = Math.floor(128 * (x1 + 1));
var sample2 = Math.floor(128 * (x2 + 1));
var sample1 = floor(128 * (x1 + 1));
var sample2 = floor(128 * (x2 + 1));
words[h++] = (sample1 << 8) | sample2;
}
if (this.numSamples % 2) {
var x1 = samples[t + 0];
var sample1 = Math.floor(128 * (x1 + 1));
if (clip) x1 /= sqrt(1 + x1 * x1);
var sample1 = floor(128 * (x1 + 1));
words[h++] = sample1 << 8;
}

Expand All @@ -172,17 +183,22 @@ var WavEncoder = (function(){
* @param {number[]}
* @returns {string}
*/
encode16: function (samples) {
_encode16: function (samples) {
// this is hard-coded for 16-bit mono

assertEqual(samples.length, this.numSamples, 'Wrong number of samples');

var floor = Math.floor;
var sqrt = Math.sqrt;
var clip = this.clip;

var words = this.words;
var h = this.headerWords;
var h = this._headerWords;
for (var t = 0, T = this.numSamples; t < T; ++t) {
var x = samples[t];
if (clip) x /= sqrt(1 + x * x);
// 16-bit samples are signed
var sample = Math.floor(32768 * x);
var sample = floor(32768 * x);
if (sample < 0) sample += 65536; // 2's compliment
words[h++] = ((sample >> 8) | (sample << 8)) & 65535;
}
Expand All @@ -195,7 +211,7 @@ var WavEncoder = (function(){
*/
_encodeWords: function () {
var words = this.words;
var pairTable = WavEncoder.pairTable;
var pairTable = WavEncoder._pairTable;

var result = new Array(1 + words.length * 4/3);
var r = 0;
Expand Down Expand Up @@ -226,7 +242,8 @@ var WavEncoder = (function(){
WavEncoder.defaults = {
numChannels: 1, // mono
sampleRateHz: 22050, // 22050 Hz
bytesPerSample: 2 // 16 bit
bytesPerSample: 2, // 16 bit
clip: true
};

(function(){
Expand All @@ -239,23 +256,24 @@ var WavEncoder = (function(){
pairTable[ij] = charTable[ij >> 6] + charTable[ij & 63];
}

WavEncoder.pairTable = pairTable;
WavEncoder._pairTable = pairTable;
})();

WavEncoder.Exception = WavEncoderException;

/**
* WavEncoder is optimized to encode many data sequences of the same length,
* but we provide a one-off function for convenience.
* but we provide this one-off function for convenience.
*
* @param {number[]}
* @param {object?}
* @returns {string}
*/
WavEncoder.encode = function (data) {
var encoder = new WavEncoder(data.length);
WavEncoder.encode = function (data, options) {
var encoder = new WavEncoder(data.length, options);
return encoder.encode(data);
};

WavEncoder.Exception = WavEncoderException;

return WavEncoder;

}());
Expand Down

0 comments on commit cf25732

Please sign in to comment.