packages/fft-radix2/test/test.js
var almost = require('almost-equal')
const test = require('tst')
const assert = require('assert')
const kdft = require('dsp-dft')
const dspjs = require('dspjs')
const fftRadix2 = require('..').fftRadix2
const { PI, sin, cos } = Math
const arr = require('dsp-array')
test('small signal against dft', function () {
const size = 8
const signal = arr.fill(size, (n, N) => sin(n / N) + cos(0.5 * n / N))
const dftFreqDomain = kdft.dft('forward', signal)
const fftFreqDomain = fftRadix2(size).forward(signal)
assert.almost(fftFreqDomain.real, dftFreqDomain.real)
assert.almost(fftFreqDomain.imag, dftFreqDomain.imag)
// inverse-able
const dftTimeDomain = kdft.dft('inverse', dftFreqDomain)
const fftTimeDomain = fftRadix2(size).inverse(fftFreqDomain)
assert.almost(fftTimeDomain.real, signal)
assert.almost(fftTimeDomain.real, dftTimeDomain.real)
assert.almost(fftTimeDomain.imag, new Float32Array(size))
})
test('big random signal against dspjs', () => {
var SIZE = 1024 * 8
var ref = new dspjs.FFT(SIZE, 44100)
var ft = fftRadix2(SIZE)
var signal = arr.fill(SIZE, () => Math.random() * 2 - 1)
var refFreqDomain, freqDomain
var refTimeDomain, timeDomain
test('forward', () => {
test('dspjs', () => {
ref.forward(signal)
refFreqDomain = { real: ref.real, imag: ref.imag }
})
test('dsp-kit', () => {
freqDomain = ft.forward(signal)
})
test('compare', () => assert.almost(freqDomain.real, refFreqDomain.real, 0.01))
})
test('inverse', () => {
test('dspjs', () => {
ref.inverse(refFreqDomain.real, refFreqDomain.imag)
refTimeDomain = ref.real
})
test('dsp-kit', () => {
timeDomain = ft.inverse(freqDomain).real
})
test('dsp-kit vs. signal', () => assert.almost(timeDomain, signal))
test.skip('dspjs vs signal', () => assert.almost(refTimeDomain, signal))
test.skip('dsp-kit vs. dspjs', () => assert.almost(timeDomain, refTimeDomain))
})
})
test('common signal against ref dspjs', () => {
const size = 1024
// create signal
var signal = arr.fill(size, (n, N) => sin(n / N) - cos(n / N + 0.75 * PI))
assert.equal(Math.max(...signal), 1.8182117098462318)
// forward
var ref = new dspjs.FFT(size, 44100)
ref.forward(signal)
const refFreqDomain = { real: ref.real, imag: ref.imag }
const fftFreqDomain = fftRadix2(size).forward(signal)
const dftFreqDomain = kdft.dft('forward', signal)
assert.almost(fftFreqDomain.real, refFreqDomain.real, 0, 'fft vs ref (real)')
assert.almost(fftFreqDomain.imag, refFreqDomain.imag)
assert.almost(fftFreqDomain.real, dftFreqDomain.real)
assert.almost(fftFreqDomain.imag, dftFreqDomain.imag)
// inverse
const refTimeDomain = ref.inverse(refFreqDomain.real, refFreqDomain.imag)
const fftTimeDomain = fftRadix2(size).inverse(fftFreqDomain)
const dftTimeDomain = kdft.dft('inverse', dftFreqDomain)
assert.almost(fftTimeDomain.real, refTimeDomain)
assert.almost(fftTimeDomain.real, dftTimeDomain.real)
assert.almost(fftTimeDomain.imag, dftTimeDomain.imag)
})
const isNum = (x) => typeof x === 'number' && !isNaN(x)
test('semi real example', function () {
const SIZE = 1024
const WINDOW = 128
const HOP = 40
const signal = arr.fill(SIZE, (n, N) => sin(2 * PI * (n / (N - 1))))
const window = arr.fill(WINDOW, (n, N) => 0.5 * (1 - cos(2 * PI * n / (N - 1))))
const buffer = arr.zeros(WINDOW)
const win = arr.zeros(WINDOW)
const complex = { real: arr.zeros(WINDOW), imag: arr.zeros(WINDOW) }
const fft = fftRadix2(WINDOW)
var ref = new dspjs.FFT(WINDOW, 44100)
const result = []
for (var i = 0; i < SIZE; i += HOP) {
buffer.set(signal.subarray(i, i + WINDOW))
assert(arr.testAll(WINDOW, isNum, buffer), 'subarray is numeric')
arr.mult(buffer, window, win)
assert(arr.testAll(WINDOW, isNum, win), 'window is numeric')
fft.forward(buffer, complex)
ref.forward(buffer)
assert.almost(complex.real, ref.real)
result.push(arr.testAll(WINDOW, isNum, complex.real))
}
const num = Math.floor(SIZE / HOP) + 1
assert.equal(result.length, num)
const expected = arr.fill(num, () => true)
assert.almost(result, expected)
})
var EPSILON = 10e-4
assert.almost = function (x, y, epsilon, message) {
epsilon = epsilon || EPSILON
if (x.length && y.length) {
return x.every(function (x, i) {
return assert.almost(x, y[i], epsilon, (message || '') + ' - index: ' + i)
})
}
if (!almost(x, y, epsilon)) assert.fail(x, y, `${x} ≈ ${y}`, '≈ ' + message + '(' + epsilon + ')')
return true
}