UNPKG

audio-buffer-utils

Version:
816 lines (609 loc) 20.1 kB
var util = require('./'); var AudioBuffer = require('audio-buffer'); var isBrowser = require('is-browser'); var test = require('tape') var almost = require('almost-equal') var NDArray = require('ndarray') function almostEqual (x, y) { if (x.length && y.length) return x.every(function (x, i) { return almostEqual(x, y[i]); }); var EPSILON = 1e-5; if (!almost(x, y, EPSILON)) t.fail(x, y, `${x}${y}`, '≈'); return true; }; test('zero constructor', function (t) { var buffer = util.create() t.equal(buffer.length, 1) t.equal(buffer.numberOfChannels, 1) t.end() }); test('from Array', function (t) { var buffer = util.create([ 0, 1, 0, 1, 0, 1 ], 2); t.deepEqual(buffer.getChannelData(0), [0, 1, 0]); t.deepEqual(buffer.getChannelData(1), [1, 0, 1]); t.end() }); test('from Number', function (t) { var buffer = util.create(2, 2) t.equal(buffer.length, 2) t.end() }) test('from Float32Array', function (t) { var buffer = util.create(new Float32Array([ 0, 1, 0, 1, 0, 1, 0, 1, 0 ]), 3); t.deepEqual(buffer.getChannelData(0), [0, 1, 0]); t.deepEqual(buffer.getChannelData(1), [1, 0, 1]); t.deepEqual(buffer.getChannelData(2), [0, 1, 0]); t.end() }); test('from Buffer', function (t) { var data = new Buffer(8*3); data.writeFloatLE(1.0, 0); data.writeFloatLE(-1.0, 4); data.writeFloatLE(0.5, 8); data.writeFloatLE(-0.5, 12); data.writeFloatLE(-1, 16); data.writeFloatLE(0.5, 20); var buffer = util.create(data, 'float32 3-channel') t.deepEqual(buffer.getChannelData(0), [1, -1.0]); t.deepEqual(buffer.getChannelData(1), [0.5, -0.5]); t.deepEqual(buffer.getChannelData(2), [-1, 0.5]); t.end() }); test('from AudioBuffer', function (t) { var a1 = util.create([1,-1,0.5,-0.5], 2); var a2 = util.create(a1); var a3 = util.create(a1); t.notEqual(a1, a2); t.notEqual(a1, a3); t.deepEqual(a3.getChannelData(1), [0.5,-0.5]); a1.getChannelData(0)[0] = 0; t.deepEqual(a1.getChannelData(0), [0,-1]); t.deepEqual(a2.getChannelData(0), [1,-1]); t.end() }); test('from ArrayBuffer', function (t) { var a = util.create( (new Float32Array([1,-1,0.5,-0.5])).buffer, 'float32 stereo'); t.deepEqual(a.getChannelData(1), [0.5,-0.5]); t.deepEqual(a.getChannelData(0), [1,-1]); t.end() }); test('from NDArray', function (t) { var a = util.create( new NDArray(new Float32Array([1,-1,0.5,-0.5]), [2,2])); t.deepEqual(a.getChannelData(1), [0.5,-0.5]); t.deepEqual(a.getChannelData(0), [1,-1]); //FIXME: there might need more tests, like detection of ndarray dimensions etc t.end() }); test('from Array of Arrays', function (t) { var a = util.create([ [1, -1], [0.5,-0.5], [-1, 0.5] ]); t.deepEqual(a.getChannelData(1), [0.5,-0.5]); t.deepEqual(a.getChannelData(0), [1,-1]); var a = util.create([ [1, -1], [0.5,-0.5], [-1, 0.5] ] ); t.deepEqual(a.getChannelData(1), [0.5,-0.5]); t.deepEqual(a.getChannelData(0), [1,-1]); t.deepEqual(a.getChannelData(2), [-1,0.5]); t.notEqual(Array.isArray(a.getChannelData(0))) t.end() }); if (isBrowser) test('from WAABuffer', function (t) { var buf = util.context.createBuffer(3, 2, 44100); buf.getChannelData(0).fill(1); buf.getChannelData(1).fill(-1); buf.getChannelData(2).fill(0); var a = util.create( buf, 3 ); t.deepEqual(a.getChannelData(2), [0,0]); t.deepEqual(a.getChannelData(1), [-1,-1]); t.deepEqual(a.getChannelData(0), [1,1]); t.throws(function () { util.create(0, 2) }) //test that data is bound //NOTE: it seems that is shouldn’t - we can gracefully clone the buffer // buf.getChannelData(2).fill(0.5); // t.deepEqual(a.getChannelData(2), buf.getChannelData(2)); t.end() }); test('length', function (t) { var buffer = util.create(Array(12), 1); t.equal(buffer.length, 12); var buffer = util.create(Array(12), 2); t.equal(buffer.length, 6); var buffer = util.create(Array(12), 3); t.equal(buffer.length, 4); var buffer = util.create(Array(12), 4); t.equal(buffer.length, 3); var buffer = util.create(Array(12), 6); t.equal(buffer.length, 2); t.end() }); test('clone', function (t) { var a = util.create(10, 3, 3000); var b = util.create(a); var c = util.create(a, 2, 4000); t.notEqual(a, b); t.deepEqual(a.getChannelData(0), b.getChannelData(0)); t.deepEqual(a.getChannelData(2), b.getChannelData(2)); t.equal(b.numberOfChannels, 3); t.equal(b.sampleRate, 3000); t.equal(c.sampleRate, 4000); t.equal(c.numberOfChannels, 2); t.deepEqual(a.getChannelData(0), c.getChannelData(0)); t.deepEqual(a.getChannelData(1), c.getChannelData(1)); if (isBrowser) { var a = util.context.createBuffer(2,10,44100); var b = util.create(a); t.notEqual(a, b); t.notEqual(a.getChannelData(0), b.getChannelData(0)); t.deepEqual(a.getChannelData(0), b.getChannelData(0)); } t.end() }); test('create', function (t) { var buf1 = util.create(); t.equal(buf1.length, 1); t.equal(buf1.numberOfChannels, 1); var buf2 = util.create([[0,1], [0,1], [1,0]]); t.deepEqual(buf2.getChannelData(2), [1, 0]); t.equal(buf2.numberOfChannels, 3) var buf3 = util.create([new Float32Array([0,1]), new Float32Array([0,1]), new Float32Array([1,0])]); t.deepEqual(buf3.getChannelData(2), [1, 0]); var buf4 = util.create(5, 2, 44100); t.deepEqual(buf4.getChannelData(0), [0,0,0,0,0]); t.equal(buf4.numberOfChannels, 2) var buf5 = util.create(buf4); t.notEqual(buf4, buf5); t.notEqual(buf4.getChannelData(0), buf5.getChannelData(0)); t.deepEqual(buf5.getChannelData(0), [0,0,0,0,0]); t.equal(buf5.numberOfChannels, 2) var buf6 = util.create([1,0,0,1], 2); t.deepEqual(buf6.getChannelData(1), [0,1]); t.equal(buf5.numberOfChannels, 2) var buf7 = util.create([1,0,0,1], 1); t.deepEqual(buf7.getChannelData(0), [1,0,0,1]); t.equal(buf7.numberOfChannels, 1) t.end() }); test('equal', function (t) { var buf1 = util.create([1, 0, -1, 0], 2); var buf2 = util.create([1, 0, -1, 0], 2); var buf3 = util.create([1, 0, 1, 0], 2); var buf4 = util.create([1, 0, 1, 0, 1], 2); //the last sample is lost var buf5 = util.create([1, 0, 1, 0], 2); t.ok(util.equal(buf1, buf2)); t.ok(!util.equal(buf1, buf3)); t.ok(!util.equal(buf1, buf4)); t.ok(util.equal(buf3, buf4)); t.ok(util.equal(buf3, buf4, buf5)); t.ok(!util.equal(buf4, buf5, buf3, buf1)); t.throws(function () { util.equal(buf1, new Float32Array(1)); }); t.end() }); test('shallow', function (t) { var buf1 = util.create([0, 1, 2, 3]); var buf2 = util.shallow(buf1); t.equal(buf1.length, buf2.length); t.equal(buf1.numberOfChannels, buf2.numberOfChannels); t.equal(buf1.sampleRate, buf2.sampleRate); t.throws(function () { util.shallow(new Float32Array(1)); }); t.end() }); test('clone', function (t) { var buf1 = util.create([1, 0, -1, 0], 2); var buf2 = util.clone(buf1); t.ok(util.equal(buf1, buf2)); t.notEqual(buf1, buf2); buf2.getChannelData(0)[1] = 0.5; t.deepEqual(buf1.getChannelData(0), [1, 0]); t.deepEqual(buf2.getChannelData(0), [1, 0.5]); t.throws(function () { util.clone({}); }); t.end() }); test('copy', function (t) { var buf1 = util.create([1, 0, -1, 0], 2); var buf2 = util.shallow(buf1); util.copy(buf1, buf2); t.ok(util.equal(buf1, buf2)); t.notEqual(buf1, buf2); buf2.getChannelData(0)[1] = 0.5; t.deepEqual(buf1.getChannelData(0), [1, 0]); t.deepEqual(buf2.getChannelData(0), [1, 0.5]); var buf3 = util.create(8); util.copy(buf2, buf3, 4); t.deepEqual(buf3.getChannelData(0), [0,0,0,0, 1, 0.5, 0, 0]); // util.copy(buf3, buf2); t.end() }); test('clone - backing arrays are not shared between buffers', function (t) { var buf1 = util.create([0, 1, 2, 3, 4]); var buf2 = util.clone(buf1); buf2.getChannelData(0)[0] = 100; t.equal(0, buf1.getChannelData(0)[0]); t.end() }); test('reverse', function (t) { var buf1 = util.create([1, 0, -1, 0], 2); util.reverse(buf1); t.deepEqual(buf1.getChannelData(0), [0, 1]); t.deepEqual(buf1.getChannelData(1), [0, -1]); t.throws(function () { util.reverse([1,2,3]); }); var buf2 = util.shallow(buf1); util.reverse(buf1, buf2); t.deepEqual(buf2.getChannelData(1), [-1, 0]); var buf3 = util.create([0,.1,.2,.3,.4,.5]) util.reverse(buf3, 1,3) t.deepEqual(buf3.getChannelData(0), new Float32Array([0,.2,.1,.3,.4,.5])) t.end() }); test('invert', function (t) { var buf1 = util.create([1, 0.5, -1, 0], 2); util.invert(buf1); t.deepEqual(buf1.getChannelData(0), [-1, -0.5]); t.deepEqual(buf1.getChannelData(1), [1, 0]); t.throws(function () { util.invert(new Float32Array([1,2,3])); }); var buf2 = util.shallow(buf1); util.invert(buf1, buf2); t.deepEqual(buf2.getChannelData(1), [-1, 0]); var buf3 = util.create([0,.1,.2,.3,.4,.5], 1) util.invert(buf3, 1,3) t.deepEqual(buf3.getChannelData(0), new Float32Array([0,-.1,-.2,.3,.4,.5])) t.end() }); test('zero', function (t) { var buf1 = util.create([1, 0.5, -1, 0], 2); util.zero(buf1); t.deepEqual(buf1.getChannelData(0), [0, 0]); t.deepEqual(buf1.getChannelData(1), [0, 0]); t.throws(function () { util.invert(buf1.getChannelData(0)); }); t.end() }); test('noise', function (t) { var buf1 = util.create(4, 2); util.noise(buf1); t.notDeepEqual(buf1.getChannelData(0), [0, 0]); t.notDeepEqual(buf1.getChannelData(1), [0, 0]); t.throws(function () { util.noise(buf1.getChannelData(0)); }); t.end() }); test('fill with function', function (t) { var a = util.create([1,2,3,4], 2); util.fill(a, function (sample, channel, offset) { return channel + offset }); t.deepEqual(a.getChannelData(0), [0,1]); t.deepEqual(a.getChannelData(1), [1,2]); t.throws(function () { util.fill([1,2,3], function () {}); }); t.end() }); test('fill with value', function (t) { var a = util.create([1,2,3,4], 2); util.fill(a, 1, 1, 3); t.deepEqual(a.getChannelData(0), [1,1]); t.deepEqual(a.getChannelData(1), [3,1]); t.throws(function () { util.fill(a.getChannelData(1), 1); }); t.end() }); test('fill to another buffer', function (t) { var a = util.create([1,2,3,4], 2); var b = util.shallow(a); util.fill(a, b, 1, 1, 3); t.deepEqual(a.getChannelData(0), [1,2]); t.deepEqual(a.getChannelData(1), [3,4]); t.deepEqual(b.getChannelData(0), [0,1]); t.deepEqual(b.getChannelData(1), [0,1]); t.end() }); test('fill callback argument', function (t) { var a = util.create([1,2,3,4], 2); //NOTE: such arguments are possible in case of `Through(util.noise)` etc. util.fill(a, function () {}, function () { return 1; }); t.deepEqual(a.getChannelData(0), [1,1]); t.deepEqual(a.getChannelData(1), [1,1]); t.end() }); test('fill negative offsets', function (t) { var a = util.create(10) util.fill(a, .1, -2) t.deepEqual(a.getChannelData(0), new Float32Array([0,0,0,0,0,0,0,0,.1,.1])) util.fill(a, .2, 0, -7) t.deepEqual(a.getChannelData(0), new Float32Array([.2,.2,.2,0,0,0,0,0,.1,.1])) t.end() }) test('slice', function (t) { var a = util.create([1,2,3,4,5,6,7,8,9], 3); var b = util.slice(a, 1); t.deepEqual(b.getChannelData(0), [2,3]); t.deepEqual(b.getChannelData(1), [5,6]); var c = util.slice(a, 1, 2); t.deepEqual(c.getChannelData(0), [2]); t.deepEqual(c.getChannelData(1), [5]); t.deepEqual(c.numberOfChannels, 3); b.getChannelData(0)[0] = 1; t.deepEqual(b.getChannelData(0), [1,3]); t.deepEqual(a.getChannelData(0), [1, 2, 3]); t.throws(function () { util.slice([1,2,3,4], 1, 2); }); t.end() }); test('repeat', function (t) { var a = util.create([0,.5,1]) var a0 = util.repeat(a, 0) t.equal(a0.length, 0) var a1 = util.repeat(a, 1) t.equal(a1.length, 3) t.deepEqual(a1, a) var a2 = util.repeat(a, 2) t.deepEqual(a2.getChannelData(0), [0,.5,1,0,.5,1]) var a3 = util.repeat(a, 3) t.deepEqual(a3.getChannelData(0), [0,.5,1,0,.5,1,0,.5,1]) t.end() }) test('subbuffer', function (t) { // var a = util.create([0, .1, .2, .3]) // var b = util.create([a.getChannelData(0).subarray(1,2)]) // b.getChannelData(0)[0] = .4 // t.deepEqual(a.getChannelData(0), new Float32Array([0, .4, .2, .3])) var a = util.create([1,2,3,4,5,6,7,8,9], 3); var b = util.subbuffer(a, 1); b.getChannelData(0)[0] += .5 t.deepEqual(a.getChannelData(0), [1,2.5,3]); t.deepEqual(b.getChannelData(0), [2.5,3]); t.deepEqual(b.getChannelData(1), [5,6]); var c = util.subbuffer(a, 1, 2); c.getChannelData(1)[0] += .5 t.deepEqual(a.getChannelData(0), [1,2.5,3]); t.deepEqual(a.getChannelData(1), [4,5.5,6]); t.deepEqual(c.getChannelData(0), [2.5]); t.deepEqual(c.getChannelData(1), [5.5]); t.deepEqual(c.numberOfChannels, 3); b.getChannelData(2)[0] = 1; t.deepEqual(b.getChannelData(2), [1, 9]); t.deepEqual(a.getChannelData(2), [7, 1, 9]); t.deepEqual(c.getChannelData(2), [1]); if (isBrowser) { var s = util.context.createBufferSource() t.throws(function () { s.buffer = c }) s.buffer = util.slice(c) } var d = util.subbuffer(a, [1,2]) t.deepEqual(d.getChannelData(0), [4,5.5,6]) t.deepEqual(d.getChannelData(1), [7,1,9]) t.ok(d.duration) t.end() }); test('map', function (t) { var a = util.create([1, 1, 1, 1, 1, 1], 3); var b = util.shallow(a) var b = util.fill(a, b, function (sample, channel, offset) { return sample + channel + offset }); t.notEqual(a, b); t.ok(!util.equal(a, b)); t.deepEqual(b.getChannelData(0), [1,2]); t.deepEqual(b.getChannelData(1), [2,3]); t.deepEqual(b.numberOfChannels, 3); b.getChannelData(0)[0] = 0; t.deepEqual(a.getChannelData(0), [1,1]); t.deepEqual(b.getChannelData(0), [0,2]); t.throws(function () { util.fill([1,2,3,4], function () {}); }); t.end() }); test('concat', function (t) { var a = util.create([1,1,1,1], 2); var b = util.create(2, 3); var c = util.create([-1, -1], 1, 22050); //handle this! var d = util.concat(a, c); t.deepEqual(d.getChannelData(0), [1,1,-1,-1]); t.deepEqual(d.getChannelData(1), [1,1,0,0]); var d = util.concat(c, a); t.deepEqual(d.getChannelData(0), [-1,-1,1,1]); t.deepEqual(d.getChannelData(1), [0,0,1,1]); var d = util.concat(a, b, c); t.deepEqual(d.getChannelData(0), [1,1,0,0,-1,-1]); t.deepEqual(d.getChannelData(1), [1,1,0,0,0,0]); t.deepEqual(d.getChannelData(2), [0,0,0,0,0,0]); var d = util.concat([a, b, c]); t.deepEqual(d.getChannelData(0), [1,1,0,0,-1,-1]); t.deepEqual(d.getChannelData(1), [1,1,0,0,0,0]); t.deepEqual(d.getChannelData(2), [0,0,0,0,0,0]); t.throws(function () { util.concat([1,2,3,4], [5,6]); }); var e = util.concat(util.create(4), util.create(1),util.create(1),util.create(1),util.create(1),util.create(1),util.create(1)) t.equal(e.length, 10) t.end() }); test('resize', function (t) { var a = util.create([1,1,1,1,1], 1, 44100); //set too big a = util.resize(a, 10); t.deepEqual(a.getChannelData(0), [1,1,1,1,1,0,0,0,0,0]); //set too small a = util.resize(a, 2); t.deepEqual(a.getChannelData(0), [1,1]); t.throws(function () { util.resize('123', 2); }); t.end() }); test('rotate (+ve)', function (t) { var a = util.create([0,0,1,1,0,0,-1,-1]); util.rotate(a, 2); t.deepEqual(a.getChannelData(0), [-1,-1,0,0,1,1,0,0]); t.throws(function () { util.rotate([1,2,3], 2); }); t.end() }); test('rotate (-ve)', function(t) { var a = util.create([0,0,1,1,0,0,-1,-1]); util.rotate(a, -3); t.deepEqual(a.getChannelData(0), [1,0,0,-1,-1,0,0,1]); t.throws(function () { util.rotate([1,2,3], -2); }); t.end() }); test('shift (+ve)', function (t) { var a = util.create([0,0,1,1,0,0,-1,-1]); util.shift(a, 2); t.deepEqual(a.getChannelData(0), [0,0,0,0,1,1,0,0]); t.throws(function () { util.shift([1,2,3], 2); }); t.end() }); test('shift (-ve)', function (t) { var a = util.create([0,0,1,1,0,0,-1,-1]); util.shift(a, -3); t.deepEqual(a.getChannelData(0), [1,0,0,-1,-1,0,0,0]); t.throws(function () { util.shift([1,2,3], -2); }); t.end() }); test('normalize', function (t) { var a = util.create([0, 0.2, 0, -0.4]); util.normalize(a); t.deepEqual(a.getChannelData(0), [0, .5, 0, -1]); var b = util.create([0, 1, 0, -1]); util.normalize(b); t.deepEqual(b.getChannelData(0), [0, 1, 0, -1]); var c = util.create([0, 5, 0, -5]); util.normalize(c); t.deepEqual(c.getChannelData(0), [0, 1, 0, -1]); //channels static var c = util.create([0, .25, 0, -.5], 2); util.normalize(c); t.deepEqual(c.getChannelData(0), [0, .5]); t.deepEqual(c.getChannelData(1), [0, -1]); //too big value //FIXME: too large values are interpreted as 1, but maybe we need deamplifying instead //for example, biquad-filters may return values > 1, then we do not want to clip values var a = util.create([0, 0.1, 0, -0.5, 999, 2], 2); util.normalize(a); t.deepEqual(a.getChannelData(1), [-0.5, 1, 1]); t.throws(function () { util.normalize(new Float32Array([0, 0.1, 0.2])); }); t.end() }); test('removeStatic', function (t) { var a = util.create([.5,.7,.3,.5], 2) util.removeStatic(a) t.ok(almostEqual, a.getChannelData(0), [-.1, .1]) t.ok(almostEqual, a.getChannelData(1), [-.1, .1]) t.end() }); test('trim', function (t) { //trim single var a = util.create([0,0,1,0,0], 1) var b = util.trim(a) t.deepEqual(b.getChannelData(0), [1]) //trim both var a = util.create([0,0,1,0,0,2,3,0], 2); var b = util.trim(a); t.deepEqual(b.getChannelData(0), [0,1]); t.deepEqual(b.getChannelData(1), [2,3]); //no trim var a = util.create([1,0,1,0,0,2,3,1], 2); var b = util.trim(a); t.deepEqual(b.getChannelData(0), [1,0,1,0]); t.deepEqual(b.getChannelData(1), [0,2,3,1]); t.throws(function () { util.trim(new Float32Array([0, 0.1, 0.2])); }); t.end() }); test('pad', function (t) { //pad right var a = util.create([0,1,2,3,4,5], 2); var b = util.padRight(a, 4); t.deepEqual(b.getChannelData(0), [0,1,2,0]); t.deepEqual(b.getChannelData(1), [3,4,5,0]); //pad left var a = util.create([0,1,2,3,4,5], 2); var b = util.padLeft(a, 4); t.deepEqual(b.getChannelData(0), [0,0,1,2]); t.deepEqual(b.getChannelData(1), [0,3,4,5]); //pad value var a = util.create([0,1,2,3,4,5], 2); var b = util.pad(4, a, 0.5); t.deepEqual(b.getChannelData(0), [0.5,0,1,2]); t.deepEqual(b.getChannelData(1), [0.5,3,4,5]); t.throws(function () { util.pad(new Float32Array([0, 0.1, 0.2])); }); //pad conversion var a = util.create(1, 1) var b = util.pad(a, 10); t.equal(b.numberOfChannels, 1); t.end() }); test('size', function (t) { var a = util.create(200, 2); t.equal(util.size(a), 200 * 2 * 4); t.throws(function () { util.size(); }); t.end() }); test('mix', function (t) { var a = util.create([0,1,0,1], 2); var b = util.create([0.5, 0.5, -0.5, -0.5], 2); //simple mix util.mix(a, b); t.deepEqual(a.getChannelData(0), [0.25, 0.75]); t.deepEqual(a.getChannelData(1), [-0.25, 0.25]); //fn mix var a = util.create([0, 1, 0, 1, 0, 1], 2); var b = util.create([1, 1, 1, 1], 2); util.mix(a, b, function (v1, v2) { return v1 + v2; }, 1); t.deepEqual(a.getChannelData(0), [0, 2, 1]); t.deepEqual(a.getChannelData(1), [1, 1, 2]); t.throws(function () { util.mix([1,2,3], [4,5,6], 0.1); }); t.end() }); test('data', function (t) { var b = util.create([1,-1, 0.5, -1, 0, -0.5], 3); var data = util.data(b); t.deepEqual(data[0], [1, -1]); t.deepEqual(data[1], [0.5, -1]); t.deepEqual(data[2], [0, -0.5]); var src = [new Float32Array(2), new Float32Array(2), new Float32Array(2)]; var data = util.data(b, src); t.deepEqual(src[0], [1, -1]); t.deepEqual(src[1], [0.5, -1]); t.deepEqual(src[2], [0, -0.5]); t.end() });