UNPKG

@webaudiomodules/sdk

Version:
334 lines (287 loc) 12.7 kB
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-plusplus */ import expect from './jestUtilities'; import { diffArray } from './testUtilities'; import getWamParameterInfo from '../src/WamParameterInfo.js'; import getWamParameterInterpolator from '../src/WamParameterInterpolator.js'; const samplesPerRenderQuantum = 128; const WamParameterInfo = getWamParameterInfo(); const WamParameterInterpolator = getWamParameterInterpolator(); describe('WamParameterInterpolator Suite', () => { // TODO setup should be in a 'beforeEach' but TS can't resolve scope... let e = 0.0; const samplesPerInterpolation = 32; // Note that some tests will fail if this is made very large const startValue = -10; const endValue = 10; const infoA = new WamParameterInfo('A', { defaultValue: 0.5, minValue: startValue, maxValue: endValue }); const infoB = new WamParameterInfo('B', { defaultValue: 3.0, minValue: startValue, maxValue: endValue }); const testA = new WamParameterInterpolator(infoA, samplesPerInterpolation); const testB = new WamParameterInterpolator(infoB, samplesPerInterpolation); const initialKey = [samplesPerInterpolation, e].join('_'); let startIndex = 0; let endIndex = 0; it('Should manage lifecycles of static lookup tables', () => { /* eslint-disable-next-line */ const tables = WamParameterInterpolator['_tables']; /* eslint-disable-next-line */ const tableReferences = WamParameterInterpolator['_tableReferences']; // Initial key and corresponding references should be present expect(tables[initialKey]).toBeDefined(); // 2 references -- testA and testB share the table expect(tableReferences[initialKey].length).toEqual(2); e = 0.5; testA.setSkew(e); const additionalKey = [samplesPerInterpolation, e].join('_'); // Initial key and corresponding references should still be present expect(tables[initialKey]).toBeDefined(); // 1 reference, only testB uses this table expect(tableReferences[initialKey].length).toEqual(1); // Expected key and corresponding references should be present expect(tables[additionalKey]).toBeDefined(); // 1 reference, only testA uses this table expect(tableReferences[additionalKey].length).toEqual(1); testA.setSkew(0); // Expected key and corresponding reference should no longer be present expect(tables[additionalKey]).toBeUndefined(); expect(tableReferences[additionalKey]).toBeUndefined(); // Initial key and corresponding references should still be present expect(tables[initialKey]).toBeDefined(); // 2 references, testA and testB share the same table again expect(tableReferences[initialKey].length).toEqual(2); // Tables and references should be cleaned up after destroying instances testA.destroy(); // 1 reference, testB still uses this table expect(tableReferences[initialKey].length).toEqual(1); testB.destroy(); // 0 tables, 0 references, all instances destroyed expect(tables[initialKey]).toBeUndefined(); expect(tableReferences[initialKey]).toBeUndefined(); }); it('Should be filled after call to setStartValue', () => { const expectedValue = 0; testA.setStartValue(expectedValue); expect(testA.values).toAllEqual(expectedValue); expect(testA.is(expectedValue)).toEqual(true); }); it('Should only be done when filled with end value', () => { testA.setStartValue(startValue); expect(testA.is(startValue)).toEqual(true); testA.setEndValue(endValue); // No processing yet expect(testA.done).toEqual(false); expect(testA.is(startValue)).toEqual(false); expect(testA.is(endValue)).toEqual(false); startIndex = 0; endIndex = Math.round(samplesPerInterpolation / 3); testA.process(startIndex, endIndex); // Partial slice (less than samplesPerInterpolation) expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); startIndex = endIndex; endIndex += samplesPerInterpolation - endIndex; testA.process(startIndex, endIndex); // Should have finished interpolating, but not yet filled expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); startIndex = samplesPerInterpolation; endIndex = samplesPerRenderQuantum; testA.process(startIndex, endIndex); const rampedSlice = testA.values.slice(0, samplesPerInterpolation); const filledSlice = testA.values.slice(samplesPerInterpolation + 1, samplesPerRenderQuantum); // Verify values expect(rampedSlice).toAllIncrease(); expect(filledSlice).toAllEqual(endValue); expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); startIndex = 0; testA.process(startIndex, endIndex); // Should have finishing filling values expect(testA.done).toEqual(true); expect(testA.values).toAllEqual(endValue); expect(testA.is(endValue)).toEqual(true); }); it('Should continue from current value when setting end value during interpolation', () => { testA.setStartValue(startValue); expect(testA.is(startValue)).toEqual(true); testA.setEndValue(endValue); expect(testA.is(startValue)).toEqual(false); expect(testA.is(endValue)).toEqual(false); const partialInterpolation = Math.round(samplesPerInterpolation / 3); startIndex = 0; endIndex = partialInterpolation; testA.process(startIndex, endIndex); // Partial slice (less than samplesPerInterpolation) testA.setEndValue(startValue); startIndex = endIndex; endIndex += samplesPerInterpolation - endIndex; testA.process(startIndex, endIndex); // Should not have finished interpolating expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); expect(testA.is(startValue)).toEqual(false); startIndex = endIndex; endIndex = samplesPerRenderQuantum; testA.process(startIndex, endIndex); // Should have finished interpolating but not filled expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); expect(testA.is(startValue)).toEqual(false); startIndex = 0; endIndex = partialInterpolation; const increasingSlice = testA.values.slice(startIndex, endIndex); startIndex = endIndex; endIndex = startIndex + samplesPerInterpolation; const decreasingSlice = testA.values.slice(startIndex, endIndex); startIndex = endIndex; endIndex = samplesPerRenderQuantum; const filledSlice = testA.values.slice(startIndex, endIndex); // Verify values expect(increasingSlice).toAllIncrease(); expect(decreasingSlice).toAllDecrease(); expect(filledSlice).toAllEqual(startValue); expect(testA.done).toEqual(false); expect(testA.is(endValue)).toEqual(false); expect(testA.is(startValue)).toEqual(false); startIndex = 0; testA.process(startIndex, endIndex); // Should have finishing filling values expect(testA.done).toEqual(true); expect(testA.values).toAllEqual(startValue); expect(testA.is(endValue)).toEqual(false); expect(testA.is(startValue)).toEqual(true); }); it('Should not interpolate discrete parameters', () => { const infoC = new WamParameterInfo('C', { defaultValue: startValue, minValue: startValue, maxValue: endValue, discreteStep: 1, }); const testC = new WamParameterInterpolator(infoC, samplesPerInterpolation); startIndex = 0; endIndex = samplesPerInterpolation; testC.process(startIndex, endIndex); expect(testC.values.slice(startIndex, endIndex)).toAllEqual(startValue); startIndex = endIndex; endIndex += samplesPerInterpolation; testC.setEndValue(endValue); testC.process(startIndex, endIndex); expect(testC.values.slice(startIndex, endIndex)).toAllEqual(endValue); startIndex = endIndex; endIndex = samplesPerRenderQuantum; testC.process(startIndex, endIndex); expect(testC.values.slice(startIndex, endIndex)).toAllEqual(endValue); startIndex = 0; testC.process(startIndex, endIndex); expect(testC.values.slice(startIndex, endIndex)).toAllEqual(endValue); }); it('Should work properly when interpolation period is greater than that of render quantum', () => { const longerInterpolationPeriod = Math.round(samplesPerRenderQuantum * Math.PI); const infoD = new WamParameterInfo('D', { defaultValue: startValue, minValue: startValue, maxValue: endValue }); const testD = new WamParameterInterpolator(infoD, longerInterpolationPeriod); testD.setEndValue(endValue); startIndex = 0; endIndex = samplesPerRenderQuantum; const remainder = longerInterpolationPeriod % samplesPerRenderQuantum; let fullRenders = Math.floor(longerInterpolationPeriod / samplesPerRenderQuantum); while (fullRenders--) { testD.process(startIndex, endIndex); expect(testD.values).toAllIncrease(); } expect(testD.done).toBe(false); expect(testD.is(endValue)).toEqual(false); testD.process(0, samplesPerRenderQuantum); expect(testD.done).toBe(false); expect(testD.is(endValue)).toEqual(false); expect(testD.values.slice(0, remainder)).toAllIncrease(); expect(testD.values.slice(remainder, samplesPerRenderQuantum)).toAllEqual(endValue); testD.process(0, remainder); expect(testD.done).toBe(true); expect(testD.values).toAllEqual(endValue); expect(testD.is(endValue)).toEqual(true); }); it('Should throw if skew is set out of range', () => { expect(() => new WamParameterInterpolator(infoA, 0, 1.001)).toThrow(); expect(() => new WamParameterInterpolator(infoA, 0, -1.001)).toThrow(); expect(() => new WamParameterInterpolator(infoA, 0, 1.0)).not.toThrow(); expect(() => new WamParameterInterpolator(infoA, 0, -1.0)).not.toThrow(); expect(() => testA.setSkew(1.001)).toThrow(); expect(() => testA.setSkew(-1.001)).toThrow(); expect(() => testA.setSkew(1.0)).not.toThrow(); expect(() => testA.setSkew(-1.0)).not.toThrow(); expect(() => testA.setSkew(0.0)).not.toThrow(); }); it('Should interpolate linearly when skew is zero', () => { startIndex = 0; endIndex = samplesPerInterpolation; testA.setStartValue(startValue); testA.setEndValue(endValue); testA.process(startIndex, endIndex); let diffs = diffArray(testA.values.slice(startIndex, endIndex)); let expectedDelta = (endValue - startValue) / samplesPerInterpolation; expect(diffs).toAllEqual(expectedDelta); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(endValue); testA.setEndValue(startValue); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); diffs = diffArray(testA.values.slice(startIndex, endIndex)); expectedDelta *= -1; expect(diffs).toAllEqual(expectedDelta); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(startValue); }); it('Should interpolate nonlinearly (convex) when skew is greater than zero', () => { startIndex = 0; endIndex = samplesPerInterpolation; testA.setSkew(0.667); testA.setStartValue(startValue); testA.setEndValue(endValue); testA.process(startIndex, endIndex); let diffs = diffArray(testA.values.slice(startIndex, endIndex)); expect(diffs).toAllDecrease(); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(endValue); testA.setEndValue(startValue); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); diffs = diffArray(testA.values.slice(startIndex, endIndex)); expect(diffs).toAllDecrease(); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(startValue); }); it('Should interpolate nonlinearly (concave) when skew is less than zero', () => { startIndex = 0; endIndex = samplesPerInterpolation; testA.setSkew(-0.667); testA.setStartValue(startValue); testA.setEndValue(endValue); testA.process(startIndex, endIndex); let diffs = diffArray(testA.values.slice(startIndex, endIndex)); expect(diffs).toAllIncrease(); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(endValue); testA.setEndValue(startValue); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); diffs = diffArray(testA.values.slice(startIndex, endIndex)); expect(diffs).toAllIncrease(); startIndex = endIndex; endIndex += samplesPerInterpolation; testA.process(startIndex, endIndex); expect(testA.values.slice(startIndex, endIndex)).toAllEqual(startValue); }); });