Skip to content

Commit

Permalink
7mb -> heavy downsample edge case
Browse files Browse the repository at this point in the history
  • Loading branch information
aolsenjazz committed Jan 22, 2024
1 parent b6f67d5 commit b01693d
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 57 deletions.
2 changes: 1 addition & 1 deletion dist/libsamplerate.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/libsamplerate.worklet.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/src.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ModuleType } from "./module-type";
import { ConverterTypeValue } from "./converter-type";
import { ModuleType } from './module-type';
import { ConverterTypeValue } from './converter-type';
/**
* Manages communication between WASM code and JS
*/
Expand Down Expand Up @@ -79,5 +79,5 @@ export declare class SRC {
* @param dataOut if resampleFunc === this.module.full, pass an optional resuable buffer to avoid extra allocations
* @returns The resampled audio, if any
*/
_resample(resampleFunc: ModuleType["simple"] | ModuleType["full"], dataIn: Float32Array, dataOut?: Float32Array | null): Float32Array;
_resample(resampleFunc: ModuleType['simple'] | ModuleType['full'], dataIn: Float32Array, dataOut?: Float32Array | null): Float32Array;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alexanderolsen/libsamplerate-js",
"version": "2.1.0",
"version": "2.1.1",
"description": "Resample audio in node or browser using a webassembly port of libsamplerate.",
"keywords": [
"web assembly",
Expand Down
15 changes: 8 additions & 7 deletions src/src.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toChunks, copyOrWriteArray } from "./util";
import { ModuleType } from "./module-type";
import { ConverterTypeValue } from "./converter-type";
import { toChunks, copyOrWriteArray } from './util';
import { ModuleType } from './module-type';
import { ConverterTypeValue } from './converter-type';

/**
* The length (in `float`s) of the input and output buffers used to transmit data between
Expand Down Expand Up @@ -98,7 +98,7 @@ export class SRC {
*/
destroy(): void {
if (this.isDestroyed === true) {
console.warn("destroy() has already been called on this instance");
console.warn('destroy() has already been called on this instance');
} else {
this.module.destroy();
this.isDestroyed = true;
Expand Down Expand Up @@ -209,19 +209,20 @@ export class SRC {
* @returns The resampled audio, if any
*/
_resample(
resampleFunc: ModuleType["simple"] | ModuleType["full"],
resampleFunc: ModuleType['simple'] | ModuleType['full'],
dataIn: Float32Array,
dataOut: Float32Array | null = null
): Float32Array {
// if we don't actually need to resample, just copy values
if (this.inputSampleRate === this.outputSampleRate) return dataIn;

if (dataOut !== null && dataOut.length < this.ratio * dataIn.length)
throw "dataOut must be at least ceil(srcRatio * dataIn.length) samples long";
throw 'dataOut must be at least ceil(srcRatio * dataIn.length) samples long';

// if client is trying to resample a big piece of audio, process in chunks
const projectedSize = Math.ceil(dataIn.length * this.ratio);
if (projectedSize > BUFFER_LENGTH) return this._chunkAndResample(dataIn);
if (Math.max(projectedSize, dataIn.length) > BUFFER_LENGTH)
return this._chunkAndResample(dataIn);

this.sourceArray.set(dataIn);

Expand Down
61 changes: 40 additions & 21 deletions test/integration.test.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,54 @@
import "babel-polyfill";
import { ConverterType, create } from "../dist/libsamplerate";
import 'babel-polyfill';
import { ConverterType, create } from '../dist/libsamplerate';

let converterType = ConverterType.SRC_SINC_BEST_QUALITY;
let nChannels = 2;
let inputSampleRate = 48000;
let outputSampleRate = 96000;

test("resamples data successfully in node", () => {
return create(nChannels, inputSampleRate, outputSampleRate, {
converterType: converterType, // default SRC_SINC_FASTEST. see API for more
}).then((src) => {
let data = new Float32Array(48000);
let resampledData = src.simple(data);
src.destroy(); // clean up
test('resamples data successfully in node', () => {
return create(nChannels, inputSampleRate, outputSampleRate, {
converterType: converterType, // default SRC_SINC_FASTEST. see API for more
}).then((src) => {
let data = new Float32Array(48000);
let resampledData = src.simple(data);
src.destroy(); // clean up

expect(resampledData.length).toBe(96000);
})
expect(resampledData.length).toBe(96000);
});
});

test('return correct num samples with 96k then 192000k outputs', () => {
return create(nChannels, inputSampleRate, outputSampleRate, {
converterType: converterType
}).then((src) => {
let data = new Float32Array(48000);
let resampledData = src.simple(data);
expect(resampledData.length).toBe(96000);
return create(nChannels, inputSampleRate, outputSampleRate, {
converterType: converterType,
}).then((src) => {
let data = new Float32Array(48000);
let resampledData = src.simple(data);
expect(resampledData.length).toBe(96000);

src.outputSampleRate = 192000;
src.outputSampleRate = 192000;

let secondResampled = src.simple(data);
let secondResampled = src.simple(data);

expect(secondResampled.length).toBe(192000);
});
expect(secondResampled.length).toBe(192000);
});
});

test('correctly resamples edge case of output samples < 4mb, input samples > 4mb', () => {
const inSr = 96000;
const outSr = 48000;
const nChan = 1;

return create(nChan, inSr, outSr, {
converterType: converterType,
}).then((src) => {
// calculate # elements for oa 7mb piece of "audio"
const sizeInBytes = 7 * 1024 * 1024; // 7 MB
const elementSizeInBytes = 4; // Size of a float32 element in bytes
const numberOfElements = sizeInBytes / elementSizeInBytes;

let data = new Float32Array(numberOfElements);
let resampledData = src.simple(data);
expect(resampledData.length).toBe(917361); // arbitrary, but that's the algo
});
});
40 changes: 20 additions & 20 deletions test/src.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SRC } from "SRC";
import { SRC } from 'SRC';

const BUFFER_SIZE = 1008000;

Expand Down Expand Up @@ -37,7 +37,7 @@ function module() {
};
}

test("simple() passes correct args to wasm backend", () => {
test('simple() passes correct args to wasm backend', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -50,7 +50,7 @@ test("simple() passes correct args to wasm backend", () => {
outputSampleRate
);

const spy = jest.spyOn(src.module, "simple");
const spy = jest.spyOn(src.module, 'simple');
const data = new Float32Array(44100);

src.simple(data);
Expand All @@ -64,7 +64,7 @@ test("simple() passes correct args to wasm backend", () => {
);
});

test("full() passes correct args to wasm backend", () => {
test('full() passes correct args to wasm backend', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -77,7 +77,7 @@ test("full() passes correct args to wasm backend", () => {
outputSampleRate
);

const spy = jest.spyOn(src.module, "full");
const spy = jest.spyOn(src.module, 'full');
const data = new Float32Array(44100);

src.full(data);
Expand All @@ -91,7 +91,7 @@ test("full() passes correct args to wasm backend", () => {
);
});

test("simple() w/200k samples calls src.module.full() 23 times", () => {
test('simple() w/200k samples calls src.module.full() 23 times', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -104,15 +104,15 @@ test("simple() w/200k samples calls src.module.full() 23 times", () => {
outputSampleRate
);

const spy = jest.spyOn(src.module, "full");
const spy = jest.spyOn(src.module, 'full');
const data = new Float32Array(BUFFER_SIZE + nChannels);

src.simple(data);

expect(spy).toHaveBeenCalledTimes(115);
});

test("full() w/200k samples calls src.module.full() 23 times", () => {
test('full() w/200k samples calls src.module.full() 23 times', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -125,15 +125,15 @@ test("full() w/200k samples calls src.module.full() 23 times", () => {
outputSampleRate
);

const spy = jest.spyOn(src.module, "full");
const spy = jest.spyOn(src.module, 'full');
const data = new Float32Array(BUFFER_SIZE + nChannels);

src.full(data);

expect(spy).toHaveBeenCalledTimes(115);
});

test("destroy() calls module.destroy() + sets isDestroyed to true", () => {
test('destroy() calls module.destroy() + sets isDestroyed to true', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -146,14 +146,14 @@ test("destroy() calls module.destroy() + sets isDestroyed to true", () => {
outputSampleRate
);

const spy = jest.spyOn(src.module, "destroy");
const spy = jest.spyOn(src.module, 'destroy');
src.destroy();

expect(spy).toHaveBeenCalledTimes(1);
expect(src.isDestroyed).toBe(true);
});

test("call destroy() twice warns the second time", () => {
test('call destroy() twice warns the second time', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -168,7 +168,7 @@ test("call destroy() twice warns the second time", () => {

const consoleWarn = global.console.warn;
global.console = { warn: jest.fn() };
const spy = jest.spyOn(global.console, "warn");
const spy = jest.spyOn(global.console, 'warn');

src.destroy();
src.destroy();
Expand All @@ -178,7 +178,7 @@ test("call destroy() twice warns the second time", () => {
global.console = { warn: consoleWarn };
});

test("calling resample with inputSr===outputSr just returns dataIn", () => {
test('calling resample with inputSr===outputSr just returns dataIn', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -198,7 +198,7 @@ test("calling resample with inputSr===outputSr just returns dataIn", () => {
expect(result).toBe(dataIn);
});

test("calling resample with dataOut.length < dataIn.length && ratio > 1 fails", () => {
test('calling resample with dataOut.length < dataIn.length && ratio > 1 fails', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -217,11 +217,11 @@ test("calling resample with dataOut.length < dataIn.length && ratio > 1 fails",
expect(() => {
src._resample(() => {}, dataIn, dataOut);
}).toThrow(
"dataOut must be at least ceil(srcRatio * dataIn.length) samples long"
'dataOut must be at least ceil(srcRatio * dataIn.length) samples long'
);
});

test("setting outputSampleRate sets src._outputSampleRate", () => {
test('setting outputSampleRate sets src._outputSampleRate', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -239,7 +239,7 @@ test("setting outputSampleRate sets src._outputSampleRate", () => {
expect(src.inputSampleRate).toBe(96000);
});

test("setting inputSampleRate sets src._inputSampleRate", () => {
test('setting inputSampleRate sets src._inputSampleRate', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -257,7 +257,7 @@ test("setting inputSampleRate sets src._inputSampleRate", () => {
expect(src.outputSampleRate).toBe(96000);
});

test("setting nChannels sets src._nChannels", () => {
test('setting nChannels sets src._nChannels', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand All @@ -275,7 +275,7 @@ test("setting nChannels sets src._nChannels", () => {
expect(src.nChannels).toBe(1);
});

test("setting converterType sets src._converterType", () => {
test('setting converterType sets src._converterType', () => {
let nChannels = 2;
let converterType = 0;
let inputSampleRate = 44100;
Expand Down
6 changes: 3 additions & 3 deletions test/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ test('copyOrWriteArray with limited length copies only specified length', () =>
});

test('toFloat32 float32', () => {
let float32 = new Float32Array([1,2,3]);
let float32 = new Float32Array([1, 2, 3]);
let result = toFloat32(float32);
expect(JSON.stringify(result)).toBe(JSON.stringify(float32));
});
Expand Down Expand Up @@ -118,6 +118,6 @@ test('toFloat32 Uint32', () => {

test('toFloat32 Array throws', () => {
expect(() => {
toFloat32([1,2,3]);
toFloat32([1, 2, 3]);
}).toThrow('Unsupport data type function Array() { [native code] }');
})
});

0 comments on commit b01693d

Please sign in to comment.