audio-2.0.0
Version:
Class for high-level audio manipulations
582 lines (444 loc) • 10.3 kB
JavaScript
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')
//sources
let remoteMp3 = 'https://raw.githubusercontent.com/Jam3/silent-mp3-datauri/master/silence.mp3'
let localWav = !isBrowser ? './samples/lena.wav' : './test/samples/lena.wav'
let localMp3 = !isBrowser ? './samples/lena.mp3' : './test/samples/lena.mp3'
t('create empty instance', t => {
let a = Audio();
t.equal(a.length, 0);
t.equal(a.duration, 0);
t.equal(a.channels, 1);
t.equal(a.sampleRate, 44100);
t.end();
});
t('create duration', t => {
let a = Audio(2);
t.equal(a.length, 2*44100);
t.equal(a.duration, 2);
t.equal(a.channels, 1);
t.end();
});
t('create duration with channels', t => {
let a = Audio(4, 3)
t.equal(a.length, 4*44100);
t.equal(a.duration, 4);
t.equal(a.channels, 3);
t.end()
})
t('create length from options', t => {
let a = Audio({length: 1024, channels: 3})
t.equal(a.length, 1024)
t.equal(a.channels, 3)
let a2 = Audio({duration: 1, channels: 1})
t.equal(a2.length, 44100)
t.equal(a2.channels, 1)
t.end()
})
t('create duration from options', t => {
let a = Audio({duration: 1, channels: 3, rate: 70000})
t.equal(a.length, 70000)
t.equal(a.sampleRate, 70000)
t.equal(a.duration, 1)
t.equal(a.channels, 3)
t.end()
})
t('create from multiple arguments', t => {
let a = Audio.from([
2,
Audio(1),
new AudioBuffer(null, {length: 44100, numberOfChannels: 2})
])
t.equal(a.duration, 4)
t.equal(a.length, 4*44100)
t.equal(a.channels, 2)
t.end()
});
t.skip('create by concatenating the arguments', t => {
let a = Audio.from([0,1], [0,1])
t.equal(a.length, 4)
})
t('create from audio buffer', t => {
let a = Audio(util.create([0,.1,.2,.3,.4,.5], 3, 48000))
t.equal(a.length, 2)
t.equal(a.channels, 3)
t.equal(a.sampleRate, 48000)
t.end()
})
t('create from audio buffer list', t => {
let src = new AudioBufferList(2).repeat(2)
let a = Audio(src)
t.equal(a.length, 4)
t.equal(a.channels, 1)
t.equal(a.sampleRate, 44100)
t.end()
})
t('create from raw array', t => {
let src = new Audio(new Float32Array([0, .5]))
t.equal(src.channels, 1)
t.equal(src.length, 2)
t.equal(src.duration, 2/src.sampleRate)
t.end();
});
t('create from channels data', t => {
let src = Audio([new Float32Array([0,0,0]), [1,1,1], [2,2,2]])
t.equal(src.length, 3)
t.equal(src.channels, 3)
t.equal(src.sampleRate, 44100)
t.end()
})
t.skip('create from direct string')
isBrowser && t('create from File', t => {
Audio.decode(new File([lena.mp3], 'lena.mp3'), (err, a) => {
t.ok(a.duration > 8)
t.equal(a.channels, 1)
t.end()
});
})
isBrowser && t('create from Blob', t => {
Audio.decode(new Blob([lena.mp3]), (err, a) => {
t.ok(a.duration > 8)
t.equal(a.channels, 1)
t.end()
});
})
t.skip('create from uint8 array')
t.skip('create from arraybuffer with dtype')
t.skip('create from base64 string')
t.skip('create from base64 string with dtype')
t.skip('create from datauri octet-stream')
t.skip('create from ndarray')
t.skip('create from ndsamples')
t.skip('create from buffer', t => {
Promise.all([
// Audio(lena.wav).then(a => {
// t.ok(a)
// t.equal(Math.floor(a.duration), 12)
// }),
// Audio(lena.mp3).then(a => {
// t.ok(a)
// t.equal(Math.floor(a.duration), 12)
// }),
Audio(lena.raw).then(a => {
t.ok(a)
t.equal(Math.floor(a.duration), 12)
})
]).then(a => {
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('load wav', t => {
Audio.load(localWav).then(audio => {
t.equal(audio.length, 541184)
t.equal(audio.channels, 1)
t.end();
}, err => {
t.fail(err)
t.end()
})
})
t('load remote', t => {
Audio.load(remoteMp3, (err, a) => {
if (err) {
t.fail(err)
return t.end()
}
t.ok(a)
t.notEqual(a.duration, 0)
t.end()
})
})
t('load callback', t => {
Audio.load(localMp3, (err, audio) => {
t.equal(audio.channels, 1)
t.end();
}, err => {
t.fail(err)
t.end()
})
})
t('load caching', t => {
let a
//put into cache
Audio.load(localWav).then((audio) => {
a = audio
a.slice(0, 1).save('x.wav', e => {
if (!isBrowser) {
let p = __dirname + path.sep + 'x.wav'
t.ok(fs.existsSync(p))
fs.unlinkSync(p);
}
})
t.ok(audio)
})
//load once first item is loaded
Audio.load(localWav, (err, audio) => {
audio.slice(1, 2).save('y.wav', e => {
if (!isBrowser) {
let p = __dirname + path.sep + 'y.wav'
t.ok(fs.existsSync(p))
fs.unlinkSync(p);
}
})
t.ok(Object.keys(Audio.cache).length)
t.ok(audio instanceof Audio)
t.notEqual(audio, a)
})
//load already loaded
.then(audio => {
return Audio.load(localWav)
})
.then(a => {
t.ok(a instanceof Audio)
t.end()
})
})
t('load error', t => {
t.plan(6)
Audio.load('nonexistent', (err, audio) => {
t.ok(err)
})
Audio.load('./', (err, audio) => {
t.ok(err)
})
Audio.load('../', (err, audio) => {
t.ok(err)
})
Audio.load('/', (err, audio) => {
t.ok(err)
})
Audio.load('https://some-almost-real-url.com/file.mp3', (err, audio) => {
t.ok(err)
})
Audio.load('*').then(ok => {}, err => {
t.ok(err)
})
})
t('load multiple sources', t => {
Audio.load([localMp3, remoteMp3, localWav]).then(list => {
t.equal(list.length, 3)
let a = Audio.from(list)
t.equal(~~a.duration, 24)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('load multiple mixed', t => {
Audio.load(localWav).then(a => {
return Audio.load([a, Audio.load(remoteMp3), localWav, Audio(2), util.create(44100)])
})
.then(list => {
let audio = Audio.from(list)
t.equal(~~audio.duration, 24 + 2 + 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('load multiple error', t => {
Audio.load([localMp3, 'xxx']).then(list => {
t.fail()
t.end()
}, err => {
t.ok(err)
t.end()
})
})
t.skip('fallback to decode')
t('decode base64', t => {
Audio.decode(require('audio-lena/mp3-base64')).then(audio => {
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('decode mp3', t => {
Audio.decode(require('audio-lena/mp3')).then(audio => {
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('decode wav', t => {
Audio.decode(require('audio-lena/wav')).then(audio => {
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t.skip('decode ogg', t => {
require('vorbis.js')
Audio.decode(require('audio-lena/ogg')).then(audio => {
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('decode flac', t => {
require('flac.js')
Audio.decode(require('audio-lena/flac')).then(audio => {
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
isBrowser && t('decode Blob', t => {
let mp3 = require('audio-lena/mp3')
Audio.decode(new Blob([mp3]), (err, audio) => {
if (err) {
t.fail(err)
t.end()
}
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
t.end()
})
})
t('decode Buffer', t => {
let mp3 = Buffer.from(require('audio-lena/mp3'))
t.plan(3)
Audio.decode(mp3, (err, audio) => {
if (err) {
t.fail(err)
t.end()
}
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
})
t.ok('async')
})
t.skip('decode TypedArray', t => {
let arr = new Float32Array(require('audio-lena/raw'))
t.plan(3)
Audio.decode(arr, (err, audio) => {
if (err) {
t.fail(err)
t.end()
}
t.equal(~~audio.duration, 12)
t.equal(audio.channels, 1)
})
t.ok('async')
})
t('decode multiple items', t => {
require('flac.js')
Audio.decode([require('audio-lena/mp3'), require('audio-lena/wav-base64'), require('audio-lena/flac-datauri')]).then(list => {
t.equal(list.length, 3)
t.equal(~~list[0].duration, 12)
t.equal(~~list[1].duration, 12)
t.equal(~~list[2].duration, 12)
t.end()
}, err => {
t.fail(err)
t.end()
})
})
t('error decoding (bad argument)', t => {
Audio.decode('xxxx', (err, audio) => {
if (!err) {
t.fail('No error raised')
}
t.ok(err)
})
Audio.decode([require('audio-lena/mp3'), 'xxxx'], (err, audio) => {
if (!err) {
t.fail('No error raised')
}
t.ok(err)
t.end()
})
})
t('error decoding format')
t('properly detect numeric array vs items array', t => {
let num, mul, ch, err
num = new Audio([0])
t.equal(num.length, 1)
t.equal(num.channels, 1)
num = new Audio([1])
t.equal(num.length, 1)
t.equal(num.channels, 1)
num = new Audio([-1])
t.equal(num.length, 1)
t.equal(num.channels, 1)
num = new Audio([0, 1])
t.equal(num.length, 2)
t.equal(num.channels, 1)
num = new Audio([-1, 1])
t.equal(num.length, 2)
t.equal(num.channels, 1)
num = new Audio([1, 1, 1])
t.equal(num.length, 3)
t.equal(num.channels, 1)
num = new Audio([1, 1])
t.equal(num.length, 2)
t.equal(num.channels, 1)
mul = Audio.from([1, Audio(0), 1])
t.equal(mul.length, 44100*2)
t.equal(mul.channels, 1)
mul = Audio.from([Audio(0)])
t.equal(mul.length, 0)
t.equal(mul.channels, 1)
ch = new Audio([[0, 1], [0, 1]])
t.equal(ch.length, 2)
t.equal(ch.channels, 2)
ch = new Audio([new Float32Array([0, 1]), new Float32Array([0, 1])])
t.equal(ch.length, 2)
t.equal(ch.channels, 2)
mul = Audio.from([1, new Float32Array([0, 1]), new Float32Array([0, 1])], {channels: 2})
t.equal(mul.length, 44102)
t.equal(mul.channels, 2)
err = new Audio([1, 1, Audio(0)])
t.notOk(err.read({channel: 0})[3])
err = new Audio([-1, 1, Audio(0)])
t.notOk(err.read({channel: 0})[3])
t.throws(() => {
err = Audio.from([-1, Audio(0)])
})
t.throws(() => {
err = Audio.from([Audio(0), -1])
})
t.throws(() => {
err = Audio([-1, Audio(0)])
})
t.throws(() => {
err = Audio([Audio(0), -1])
})
mul = Audio.from([Audio(0), 1])
t.equal(mul.length, 44100)
t.equal(mul.channels, 1)
t.end()
})
t('round duration', t => {
let a = Audio(.005)
t.equal(a.length, 221)
t.end()
})