audio-2.0.0
Version:
Class for high-level audio manipulations
479 lines (328 loc) • 11.7 kB
JavaScript
'use strict'
const Audio = require('../')
const t = require('tape')
const AudioBuffer = require('audio-buffer')
const db = require('decibels')
const lena = require('audio-lena')
const isBrowser = require('is-browser')
const path = require('path')
const fs = require('fs')
const AudioBufferList = require('audio-buffer-list')
const util = require('audio-buffer-utils')
const createOscillator = require('audio-oscillator')
const isAudioBuffer = require('is-audio-buffer')
const createBuffer = require('audio-buffer-from')
function f32(v) {
if (v.length) return new Float32Array(v)
return new Float32Array([v])[0]
}
t('read', t => {
let saw = Array.from({length: 10}, (v, i) => i / 10)
let a = Audio.from([saw, saw, saw])
t.equal(a.length, 10)
t.equal(a.channels, 3)
let data
data = a.read({channel: 1})
t.deepEqual(data, new Float32Array([0,.1,.2,.3,.4,.5,.6,.7,.8,.9]))
data = a.read({format: 'audiobuffer'})
t.ok(isAudioBuffer(data))
t.equal(data.length, 10)
t.equal(data.numberOfChannels, 3)
t.deepEqual(data.getChannelData(1), new Float32Array([0,.1,.2,.3,.4,.5,.6,.7,.8,.9]))
data = a.read({channel: 1})
t.equal(data.length, 10)
t.ok(Array.isArray(data))
t.deepEqual(data, new Float32Array([0,.1,.2,.3,.4,.5,.6,.7,.8,.9]))
data = a.read({channel: 0, dtype: 'uint8'})
t.equal(data.length, 10)
t.ok(ArrayBuffer.isView(data))
t.deepEqual(data, [127, 140, 153, 165, 178, 191, 204, 216, 229, 242])
data = a.read(2/44100, 8/44100, {})
t.equal(data[0].length, 8)
t.deepEqual(data[0], new Float32Array([.2,.3,.4,.5,.6,.7,.8,.9]))
t.throws(() => {
a.read(new Uint8Array(10), {channel: 0})
})
data = a.read({dest: new Uint8Array(10), channel: 0})
t.deepEqual(data, [127, 140, 153, 165, 178, 191, 204, 216, 229, 242])
data = a.read(a.time(5), { dst: new Float32Array(15)})
t.deepEqual(data, new Float32Array([.5,.6,.7,.8,.9,.5,.6,.7,.8,.9,.5,.6,.7,.8,.9]))
data = a.read(a.time(5), a.time(1), {dst: new Int8Array(3).buffer, format: 'int8'})
t.deepEqual(new Int8Array(data), [63, 63, 63])
data = a.read(a.time(6), a.time(2), {format: 'float32 interleaved', channels: [1, 2]})
t.deepEqual(data, new Float32Array([.6, .6, .7, .7]))
let silence = Audio({length: 10})
t.throws(() => {
silence.read({channel: 1})
})
// ex-read test
let audio = new Audio(1, 2)
t.deepEqual(audio.read(-100/audio.sampleRate)[0].length, 100)
t.deepEqual(audio.read({channel: 1}).length, audio.sampleRate)
let audio3 = Audio([0, .1, 0, .2, 0, .3], 3)
t.deepEqual(audio3.read(),
[new Float32Array([0, .1]), new Float32Array([0, .2]), new Float32Array([0, .3])])
t.end()
})
t('write', t => {
let a = new Audio(10/44100, 3)
let d
a.write([[0,.5,1], new Float32Array([0, -.5, -1])])
t.deepEqual(a.read({channel: 0}), [0,.5,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,-.5,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0, 0,0,0,0,0,0,0])
a.write(createBuffer(4, 2), 1/44100, 1/44100, {channels: [1,2]})
t.deepEqual(a.read({channel: 0}), [0,.5,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0, 0,0,0,0,0,0,0])
a.write(new Uint8Array([255,255,255]), {start: 8, channel: 2})
t.deepEqual(a.read({channel: 0}), [0,.5,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0, 0,0,0,0,0,1,1])
a.write(new AudioBufferList([-1,1]))
t.deepEqual(a.read({channel: 0}), [-1,1,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0, 0,0,0,0,0,1,1])
a.write(new Audio([-1,-1,-1,-1], 2), 5/44100, {channels: [0,2]})
t.deepEqual(a.read({channel: 0}), [-1,1,1,0,0,-1,-1,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,-1,0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0,0,0,-1,-1,0,1,1])
t.end()
})
t('write value', t => {
let audio = Audio([0, .1, .2, .3, .4, .5], 1)
audio.write(1, 2/audio.sampleRate, 2/audio.sampleRate)
t.deepEqual(audio.read(1/44100,4/44100)[0], f32([.1,1,1,.4]))
t.end()
})
t('write function', t => {
let a = Audio({length: 441}).write(1)
t.equal(a.length, 441)
t.equal(a.duration, .01)
t.deepEqual(a.read({channel: 0}), Array.from({length: 441}, x => 1))
let _i = 0;
a.write((v, i, ch) => {
t.equal(i, _i)
_i++
return v * .5
})
t.end()
})
t('insert', t => {
let a = new Audio(7/44100, 3)
let d
a.insert([[0,.5,1], new Float32Array([0, -.5, -1])], 0)
t.deepEqual(a.read({channel: 0}), [0,.5,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,-.5,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0, 0,0,0,0,0,0,0])
a.insert(createBuffer(2, 2), 1/44100, 1/44100, {channels: [1,2]})
t.deepEqual(a.read({channel: 0}), [0,0,0,.5,1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,0,-.5,-1, 0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0,0,0, 0,0,0,0,0,0,0])
a.insert(new Uint8Array([255,255,255]), {channel: 2})
t.deepEqual(a.read({channel: 0}), [0,0,0,.5,1, 0,0,0,0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 1}), [0,0,0,-.5,-1, 0,0,0,0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0,0,0, 0,0,0,0,0,0,0,1,1,1])
a.insert(new AudioBufferList([-1,1]))
t.deepEqual(a.read({channel: 0}), [0,0,0,.5,1, 0,0,0,0,0,0,0,0,0,0,-1,1])
t.deepEqual(a.read({channel: 1}), [0,0,0,-.5,-1, 0,0,0,0,0,0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0,0,0, 0,0,0,0,0,0,0,1,1,1,0,0])
a.insert(new Audio([-1,-1,-1,-1], 2), 5/44100, {channels: [0,2]})
t.deepEqual(a.read({channel: 0}), [0,0,0,.5,1, -1,-1, 0,0,0,0,0,0,0,0,0,0,-1,1])
t.deepEqual(a.read({channel: 1}), [0,0,0,-.5,-1, 0,0, 0,0,0,0,0,0,0,0,0,0,0,0])
t.deepEqual(a.read({channel: 2}), [0,0,0,0,0, -1,-1, 0,0,0,0,0,0,0,1,1,1,0,0])
t.end()
})
t('remove', t => {
let a = Audio([0, .1, .2, .3, .4], 1)
let frag = a.remove(a.time(1), a.time(1), {keep: true})
t.deepEqual(a.read({channel: 0}), f32([0, .2, .3, .4]))
t.deepEqual(frag.read({channel: 0}), f32([.1]))
let frag2 = a.remove(a.time(2), a.time(1))
t.deepEqual(a.read({channel: 0}), f32([0, .2, .4]))
t.equal(frag2, a)
t.end()
})
t('slice', t => {
let a = Audio([0, .1, .2, .3, .4])
let frag1 = a.slice(a.time(1), a.time(4), {copy: false})
t.deepEqual(a.read({channel: 0}), f32([.1, .2, .3, .4]))
t.equal(frag1, a)
let frag2 = a.slice(a.time(0), a.time(1))
t.deepEqual(a.read({channel: 0}), f32([.1, .2, .3, .4]))
t.notEqual(frag2, a)
t.deepEqual(frag2.read({channel: 0}), f32([.1]))
let dup = a.slice()
t.ok(Audio.equal(a, dup))
t.notEqual(dup, a)
t.end()
})
t('slice & save', t => {
let localWav = !isBrowser ? './samples/lena.wav' : './test/samples/lena.wav'
t.plan(2)
Audio.load(localWav, (err, a) => {
a.slice(1, 2).save('x.wav', e => {
if (!isBrowser) {
let p = __dirname + path.sep + 'x.wav'
t.ok(fs.existsSync(p))
fs.unlinkSync(p);
}
})
a.slice(2, 3).save('y.wav', e => {
if (!isBrowser) {
let p = __dirname + path.sep + 'y.wav'
t.ok(fs.existsSync(p))
fs.unlinkSync(p);
}
})
})
})
t('trim', t => {
let audio = new Audio([0,0,0,.1,.2,-.1,-.2,0,0], 1).trim()
t.deepEqual(audio.read({channel: 0}), new Float32Array([.1,.2,-.1,-.2]))
//trim samples from the beginning below -30 db
audio = Audio([0.0001, 0, .1, .2, 0], 1).trim({threshold: -30, left: true})
t.deepEqual(audio.read({channel: 0}), new Float32Array([.1, .2, 0]))
//remove samples below .02 from the end
audio = Audio([0, .1, .2, -.1, -.2, 0, .0001], 1).trim({threshold: Audio.db(.02), left: false})
t.deepEqual(audio.read()[0], new Float32Array([0, .1, .2, -.1, -.2]))
t.end();
})
t('pad', t => {
let a = Audio(.005, 2)
t.equal(a.length, 221)
a.pad(.01)
t.equal(a.duration, .01)
t.equal(a.length, 441)
a.write(1)
//pad right
a.pad(.015, .5)
t.deepEqual(a.read(.01, a.time(2))[0], [.5,.5])
//pad left
a.pad(.02, {value: .1, left: true})
t.deepEqual(a.read(.0, a.time(2))[0], f32([.1,.1]))
t.end()
})
t('shift', t => {
let a = Audio([0, .25, .5, .75, 1])
a.shift(-a.time(2))
t.deepEqual(a.read({channel: 0}), [.5, .75, 1, 0, 0])
a.shift(a.time(3))
t.deepEqual(a.read({channel: 0}), [0, 0, 0, .5, .75])
a.shift(a.time(10))
t.deepEqual(a.read({channel: 0}), [0, 0, 0, 0, 0])
a.write([0, .25, .5, .75, 1])
a.shift(-a.time(2), {rotate: true})
t.deepEqual(a.read({channel: 0}), [.5, .75, 1, 0, .25])
a.shift(a.time(3), {rotate: true})
t.deepEqual(a.read({channel: 0}), [1, 0, 0.25, .5, .75])
t.end()
})
t.skip('shift channels')
t.skip('stream', t => {
// let a = Audio(MediaInput, a => {
// })
t.end()
})
t.skip('sync sequence', t => {
t.end()
})
t.skip('mixed sequence', t => {
t.end()
})
t('normalize', t => {
//full normalize
let audio = Audio([0, .1, 0, -.1], {channels: 1})
audio.normalize()
t.deepEqual(audio.read({channel: 0}), [0, 1, 0, -1]);
//partial normalize
let audio2 = Audio([0, .1, 0, -.1], {channels: 1})
audio2.normalize(2/audio2.sampleRate)
t.deepEqual(audio2.read()[0], new Float32Array([0, .1, 0, -1]));
//partial channels
let audio3 = Audio([0, .1, 0, .2, 0, .3], 3)
audio3.normalize({channel: [0, 1]})
t.deepEqual(audio3.read({channel: [0, 1]}), [[0, .5], [0, 1]])
t.end();
})
t('fade', t => {
let audio = Audio(Array(100).fill(1), {channels: 1})
let inCurve = Array(10).fill(1).map((v, i) => (i + .5)/10).map(v => db.toGain(v*40 - 40))
let outCurve = inCurve.slice().reverse()
//fade in
audio.fade(10/audio.sampleRate)
t.deepEqual(audio.read(0, 10/audio.sampleRate)[0], new Float32Array(inCurve))
//fade out
// audio.fade(-10/audio.sampleRate)
// t.deepEqual(audio.read(-10/44100)[0], new Float32Array(outCurve))
t.end();
})
t('gain', t => {
let audio = new Audio(f32(Array(441).fill(1))).gain(-20)
// t.equal(audio.read({channel: 0})[10], .1)
t.deepEqual(audio.read({channel: 0}), f32(Array(441).fill(Audio.gain(-20))))
t.end()
})
t('reverse', t => {
let data = Array(10).fill(1).map((v, i) => (.5 + i)/10)
let fixture = new Float32Array(data.slice().reverse())
let audio = new Audio([data, data])
audio.reverse()
t.deepEqual(audio.read({channel: 0}), fixture)
audio.reverse(1/44100, 1/44100, {channel: 1})
t.deepEqual(audio.read(1/44100, 1/44100)[0], new Float32Array(fixture.slice(1, 2)))
t.deepEqual(audio.read(1/44100, 1/44100)[1], new Float32Array(data.slice(8, 9)))
t.end()
})
t('invert', t => {
let data = Array(1000).fill(1).map((v, i) => (.5 + i)/1000)
let fixture = Array(1000).fill(1).map((v, i) => -(.5 + i)/1000)
let audio = new Audio(data, 1)
audio.invert()
t.deepEqual(audio.read()[0], new Float32Array(fixture))
audio.invert(10/44100, 10/44100)
t.deepEqual(audio.read(10/44100, 10/44100)[0], new Float32Array(data.slice(10, 20)))
t.end()
})
t.skip('Write stream', t => {
let source = createSource(lena);
let a = Audio();
a.write(source, (err, a) => {
});
t.end()
});
t.skip('End writing', t => {
let gen = Gen((t) => {
return Math.sin(t*440*Math.PI*2)
});
let through = Through((chunk, done) => {
setTimeout(() => done(chunk), 200)
});
a.write()
a.end();
});
t.skip('save', t => {
let a = Audio(lena, (err, a) => {
a.save('lena.wav', (err, a) => {
if (!isBrowser) {
let p = __dirname + path.sep + 'lena.wav'
t.ok(fs.existsSync(p))
fs.unlinkSync(p);
}
t.end()
})
})
})
t.skip('periodic', t => {
let audio = Audio({length: 8})
audio.periodic('saw', {from: 2, to: 6, frequency: 44100})
t.deepEqual(audio.toArray(), [])
})
t('through', t => {
let a = Audio([1, 1])
a.through(buf => {
util.fill(buf, 1)
}, {from: .5, to: 1.5})
// t.equal(a.read(0, 1), )
t.end()
})